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
This commit is contained in:
parent
2f4789d612
commit
7054764304
|
@ -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
|
||||
|
|
|
@ -821,6 +821,210 @@ java_library {
|
|||
name: "foolib",
|
||||
plugins: ["bar"],
|
||||
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
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,
|
||||
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
|
|
@ -82,6 +82,10 @@ var fixSteps = []fixStep{
|
|||
name: "rewriteJavaStaticLibs",
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue