aboutsummaryrefslogtreecommitdiffstats
path: root/bpfix
diff options
context:
space:
mode:
authorSasha Smundak <asmundak@google.com>2019-01-22 10:33:49 -0800
committerSasha Smundak <asmundak@google.com>2019-01-24 18:46:31 -0800
commit70547643040729f54a9535cc3fefc350f1af3a2a (patch)
tree5db5c80512f55887af921f67545632aa3e1fe975 /bpfix
parent2f4789d6128d2c773e3a968e8d6fb7ec4e19e838 (diff)
downloadbuild_soong-70547643040729f54a9535cc3fefc350f1af3a2a.tar.gz
build_soong-70547643040729f54a9535cc3fefc350f1af3a2a.tar.bz2
build_soong-70547643040729f54a9535cc3fefc350f1af3a2a.zip
Convert BUILD_PREBUILT with LOCAL_MODULE_CLASS=ETC to prebuilt_etc
The conversion is a two-step process: first, when processing BUILT_PREBUILT, convert LOCAL_SOURCE_PATH to a variable reference+fixed subpath path in the blueprint AST. Then, set various boolean flags depending on variable being referenced. androidmk_test.go has a test for each handled case. Change-Id: Iabd18d5f8645ca7077536863cd6640df5b24d24a Fixes: 122906526 Test: treehugger
Diffstat (limited to 'bpfix')
-rw-r--r--bpfix/bpfix/bpfix.go154
-rw-r--r--bpfix/bpfix/bpfix_test.go59
2 files changed, 213 insertions, 0 deletions
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index 6a755176..11f1877e 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -83,6 +83,10 @@ var fixSteps = []fixStep{
fix: rewriteJavaStaticLibs,
},
{
+ name: "rewritePrebuiltEtc",
+ fix: rewriteAndroidmkPrebuiltEtc,
+ },
+ {
name: "mergeMatchingModuleProperties",
fix: runPatchListMod(mergeMatchingModuleProperties),
},
@@ -407,6 +411,156 @@ func rewriteAndroidmkJavaLibs(f *Fixer) error {
return nil
}
+// Helper function to get the value of a string-valued property in a given compound property.
+func getStringProperty(prop *parser.Property, fieldName string) string {
+ if propsAsMap, ok := prop.Value.(*parser.Map); ok {
+ for _, propField := range propsAsMap.Properties {
+ if fieldName == propField.Name {
+ if propFieldAsString, ok := propField.Value.(*parser.String); ok {
+ return propFieldAsString.Value
+ } else {
+ return ""
+ }
+ }
+ }
+ }
+ return ""
+}
+
+// Create sub_dir: attribute for the given path
+func makePrebuiltEtcDestination(mod *parser.Module, path string) {
+ mod.Properties = append(mod.Properties, &parser.Property{
+ Name: "sub_dir",
+ Value: &parser.String{Value: path},
+ })
+}
+
+// Set the value of the given attribute to the error message
+func indicateAttributeError(mod *parser.Module, attributeName string, format string, a ...interface{}) error {
+ msg := fmt.Sprintf(format, a...)
+ mod.Properties = append(mod.Properties, &parser.Property{
+ Name: attributeName,
+ Value: &parser.String{Value: "ERROR: " + msg},
+ })
+ return errors.New(msg)
+}
+
+// If a variable is LOCAL_MODULE, get its value from the 'name' attribute.
+// This handles the statement
+// LOCAL_SRC_FILES := $(LOCAL_MODULE)
+// which occurs often.
+func resolveLocalModule(mod *parser.Module, val parser.Expression) parser.Expression {
+ if varLocalName, ok := val.(*parser.Variable); ok {
+ if varLocalName.Name == "LOCAL_MODULE" {
+ if v, ok := getLiteralStringProperty(mod, "name"); ok {
+ return v
+ }
+ }
+ }
+ return val
+}
+
+// A prefix to strip before setting 'filename' attribute and an array of boolean attributes to set.
+type filenamePrefixToFlags struct {
+ prefix string
+ flags []string
+}
+
+var localModulePathRewrite = map[string][]filenamePrefixToFlags{
+ "HOST_OUT": {{prefix: "/etc"}},
+ "PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}},
+ "TARGET_OUT": {{prefix: "/etc"}},
+ "TARGET_OUT_ETC": {{prefix: ""}},
+ "TARGET_OUT_PRODUCT": {{prefix: "/etc", flags: []string{"product_specific"}}},
+ "TARGET_OUT_PRODUCT_ETC": {{prefix: "", flags: []string{"product_specific"}}},
+ "TARGET_OUT_ODM": {{prefix: "/etc", flags: []string{"device_specific"}}},
+ "TARGET_OUT_PRODUCT_SERVICES": {{prefix: "/etc", flags: []string{"product_services_specific"}}},
+ "TARGET_OUT_PRODUCT_SERVICES_ETC": {{prefix: "", flags: []string{"product_services_specific"}}},
+ "TARGET_OUT_VENDOR": {{prefix: "/etc", flags: []string{"proprietary"}}},
+ "TARGET_OUT_VENDOR_ETC": {{prefix: "", flags: []string{"proprietary"}}},
+ "TARGET_RECOVERY_ROOT_OUT": {{prefix: "/system/etc", flags: []string{"recovery"}}},
+}
+
+// rewriteAndroidPrebuiltEtc fixes prebuilt_etc rule
+func rewriteAndroidmkPrebuiltEtc(f *Fixer) error {
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ if mod.Type != "prebuilt_etc" && mod.Type != "prebuilt_etc_host" {
+ continue
+ }
+
+ // The rewriter converts LOCAL_SRC_FILES to `srcs` attribute. Convert
+ // it to 'src' attribute (which is where the file is installed). If the
+ // value 'srcs' is a list, we can convert it only if it contains a single
+ // element.
+ if srcs, ok := mod.GetProperty("srcs"); ok {
+ if srcList, ok := srcs.Value.(*parser.List); ok {
+ removeProperty(mod, "srcs")
+ if len(srcList.Values) == 1 {
+ mod.Properties = append(mod.Properties,
+ &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcList.Values[0])})
+ } else if len(srcList.Values) > 1 {
+ indicateAttributeError(mod, "src", "LOCAL_SRC_FILES should contain at most one item")
+ }
+ } else if _, ok = srcs.Value.(*parser.Variable); ok {
+ removeProperty(mod, "srcs")
+ mod.Properties = append(mod.Properties,
+ &parser.Property{Name: "src", NamePos: srcs.NamePos, ColonPos: srcs.ColonPos, Value: resolveLocalModule(mod, srcs.Value)})
+ } else {
+ renameProperty(mod, "srcs", "src")
+ }
+ }
+
+ // The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
+ // 'local_module_path'. Analyze its contents and create the correct sub_dir:,
+ // filename: and boolean attributes combination
+ const local_module_path = "local_module_path"
+ if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
+ removeProperty(mod, local_module_path)
+ prefixVariableName := getStringProperty(prop_local_module_path, "var")
+ path := getStringProperty(prop_local_module_path, "fixed")
+ if prefixRewrites, ok := localModulePathRewrite[prefixVariableName]; ok {
+ rewritten := false
+ for _, prefixRewrite := range prefixRewrites {
+ if path == prefixRewrite.prefix {
+ rewritten = true
+ } else if trimmedPath := strings.TrimPrefix(path, prefixRewrite.prefix+"/"); trimmedPath != path {
+ makePrebuiltEtcDestination(mod, trimmedPath)
+ rewritten = true
+ }
+ if rewritten {
+ for _, flag := range prefixRewrite.flags {
+ mod.Properties = append(mod.Properties, &parser.Property{Name: flag, Value: &parser.Bool{Value: true, Token: "true"}})
+ }
+ break
+ }
+ }
+ if !rewritten {
+ expectedPrefices := ""
+ sep := ""
+ for _, prefixRewrite := range prefixRewrites {
+ expectedPrefices += sep
+ sep = ", "
+ expectedPrefices += prefixRewrite.prefix
+ }
+ return indicateAttributeError(mod, "filename",
+ "LOCAL_MODULE_PATH value under $(%s) should start with %s", prefixVariableName, expectedPrefices)
+ }
+ if prefixVariableName == "HOST_OUT" {
+ mod.Type = "prebuilt_etc_host"
+ }
+ } else {
+ return indicateAttributeError(mod, "filename", "Cannot handle $(%s) for the prebuilt_etc", prefixVariableName)
+ }
+ }
+ }
+ return nil
+}
+
func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
return func(f *Fixer) error {
// Make sure all the offsets are accurate
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 5224ee3a..13942235 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -692,3 +692,62 @@ func TestRewriteCtsModuleTypes(t *testing.T) {
})
}
}
+
+func TestRewritePrebuiltEtc(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: ["bar"],
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: "bar",
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: FOO,
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: FOO,
+ }
+ `,
+ },
+ {
+ name: "prebuilt_etc src",
+ in: `
+ prebuilt_etc {
+ name: "foo",
+ srcs: ["bar", "baz"],
+ }
+ `,
+ out: `prebuilt_etc {
+ name: "foo",
+ src: "ERROR: LOCAL_SRC_FILES should contain at most one item",
+
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPass(t, test.in, test.out, func(fixer *Fixer) error {
+ return rewriteAndroidmkPrebuiltEtc(fixer)
+ })
+ })
+ }
+}