diff options
-rw-r--r-- | androidmk/cmd/androidmk/android.go | 52 | ||||
-rw-r--r-- | androidmk/cmd/androidmk/androidmk_test.go | 204 | ||||
-rw-r--r-- | bpfix/bpfix/bpfix.go | 154 | ||||
-rw-r--r-- | bpfix/bpfix/bpfix_test.go | 59 |
4 files changed, 468 insertions, 1 deletions
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go index e7f2531d..aef8944d 100644 --- a/androidmk/cmd/androidmk/android.go +++ b/androidmk/cmd/androidmk/android.go @@ -56,6 +56,7 @@ var rewriteProperties = map[string](func(variableAssignmentContext) error){ "LOCAL_CFLAGS": cflags, "LOCAL_UNINSTALLABLE_MODULE": invert("installable"), "LOCAL_PROGUARD_ENABLED": proguardEnabled, + "LOCAL_MODULE_PATH": prebuiltModulePath, // composite functions "LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))), @@ -519,6 +520,55 @@ func prebuiltClass(ctx variableAssignmentContext) error { return nil } +func makeBlueprintStringAssignment(file *bpFile, prefix string, suffix string, value string) error { + val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString(value, mkparser.NoPos), bpparser.StringType) + if err == nil { + err = setVariable(file, false, prefix, suffix, val, true) + } + return err +} + +// If variable is a literal variable name, return the name, otherwise return "" +func varLiteralName(variable mkparser.Variable) string { + if len(variable.Name.Variables) == 0 { + return variable.Name.Strings[0] + } + return "" +} + +func prebuiltModulePath(ctx variableAssignmentContext) error { + // Cannot handle appending + if ctx.append { + return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH") + } + // Analyze value in order to set the correct values for the 'device_specific', + // 'product_specific', 'product_services_specific' 'vendor'/'soc_specific', + // 'product_services_specific' attribute. Two cases are allowed: + // $(VAR)/<literal-value> + // $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value> + // The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value> + // Map the variable name if present to `local_module_path_var` + // Map literal-path to local_module_path_fixed + varname := "" + fixed := "" + val := ctx.mkvalue + if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" { + fixed = val.Strings[1] + varname = val.Variables[0].Name.Strings[0] + } else if len(val.Variables) == 2 && varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && varLiteralName(val.Variables[1]) == "TARGET_COPY_OUT_VENDOR" && + len(val.Strings) == 3 && val.Strings[0] == "" && val.Strings[1] == "/" { + fixed = val.Strings[2] + varname = "TARGET_OUT_VENDOR" + } else { + return fmt.Errorf("LOCAL_MODULE_PATH value should start with $(<some-varaible>)/ or $(PRODUCT_OUT)/$(TARGET_COPY_VENDOR)/") + } + err := makeBlueprintStringAssignment(ctx.file, "local_module_path", "var", varname) + if err == nil && fixed != "" { + err = makeBlueprintStringAssignment(ctx.file, "local_module_path", "fixed", fixed) + } + return err +} + func ldflags(ctx variableAssignmentContext) error { val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType) if err != nil { @@ -816,6 +866,7 @@ var prebuiltTypes = map[string]string{ "STATIC_LIBRARIES": "cc_prebuilt_library_static", "EXECUTABLES": "cc_prebuilt_binary", "JAVA_LIBRARIES": "java_import", + "ETC": "prebuilt_etc", } var soongModuleTypes = map[string]bool{} @@ -834,7 +885,6 @@ func androidScope() mkparser.Scope { globalScope.SetFunc("first-makefiles-under", includeIgnored) globalScope.SetFunc("all-named-subdir-makefiles", includeIgnored) globalScope.SetFunc("all-subdir-makefiles", includeIgnored) - for k, v := range moduleTypes { globalScope.Set(k, v) soongModuleTypes[v] = true diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go index 5fbe62a0..52b8476a 100644 --- a/androidmk/cmd/androidmk/androidmk_test.go +++ b/androidmk/cmd/androidmk/androidmk_test.go @@ -824,6 +824,210 @@ java_library { } `, }, + { + desc: "prebuilt_etc_TARGET_OUT_ETC", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_SRC_FILES := mymod +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + src: "mymod", + sub_dir: "foo/bar", + +} +`, + }, + + { + desc: "prebuilt_etc_PRODUCT_OUT/system/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/etc/foo/bar +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + + src: "etc.test1", + sub_dir: "foo/bar", + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_ODM/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + device_specific: true, + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_PRODUCT/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + product_specific: true, + + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_PRODUCT_ETC", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + product_specific: true, + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES)/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + product_services_specific: true, + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_PRODUCT_SERVICES_ETC", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_SERVICES_ETC)/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + product_services_specific: true, + + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_VENDOR/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + proprietary: true, + +} +`, + }, + { + desc: "prebuilt_etc_PRODUCT_OUT/TARGET_COPY_OUT_VENDOR/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + proprietary: true, + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_OUT_VENDOR_ETC", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + proprietary: true, + +} +`, + }, + { + desc: "prebuilt_etc_TARGET_RECOVERY_ROOT_OUT/system/etc", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := etc.test1 +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/foo/bar +include $(BUILD_PREBUILT) +`, + expected: ` +prebuilt_etc { + name: "etc.test1", + sub_dir: "foo/bar", + recovery: true, + +} +`, + }, } func TestEndToEnd(t *testing.T) { 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) + }) + }) + } +} |