| package main |
| |
| import ( |
| mkparser "android/soong/androidmk/parser" |
| "fmt" |
| "strings" |
| |
| bpparser "github.com/google/blueprint/parser" |
| ) |
| |
| const ( |
| clear_vars = "__android_mk_clear_vars" |
| ) |
| |
| var standardProperties = map[string]struct { |
| string |
| bpparser.ValueType |
| }{ |
| // String properties |
| "LOCAL_MODULE": {"name", bpparser.String}, |
| "LOCAL_MODULE_CLASS": {"class", bpparser.String}, |
| "LOCAL_CXX_STL": {"stl", bpparser.String}, |
| "LOCAL_STRIP_MODULE": {"strip", bpparser.String}, |
| "LOCAL_MULTILIB": {"compile_multilib", bpparser.String}, |
| "LOCAL_ARM_MODE_HACK": {"instruction_set", bpparser.String}, |
| "LOCAL_SDK_VERSION": {"sdk_version", bpparser.String}, |
| "LOCAL_NDK_STL_VARIANT": {"stl", bpparser.String}, |
| "LOCAL_JAR_MANIFEST": {"manifest", bpparser.String}, |
| "LOCAL_JARJAR_RULES": {"jarjar_rules", bpparser.String}, |
| "LOCAL_CERTIFICATE": {"certificate", bpparser.String}, |
| "LOCAL_PACKAGE_NAME": {"name", bpparser.String}, |
| "LOCAL_MODULE_RELATIVE_PATH": {"relative_install_path", bpparser.String}, |
| |
| // List properties |
| "LOCAL_SRC_FILES_EXCLUDE": {"exclude_srcs", bpparser.List}, |
| "LOCAL_SHARED_LIBRARIES": {"shared_libs", bpparser.List}, |
| "LOCAL_STATIC_LIBRARIES": {"static_libs", bpparser.List}, |
| "LOCAL_WHOLE_STATIC_LIBRARIES": {"whole_static_libs", bpparser.List}, |
| "LOCAL_SYSTEM_SHARED_LIBRARIES": {"system_shared_libs", bpparser.List}, |
| "LOCAL_ASFLAGS": {"asflags", bpparser.List}, |
| "LOCAL_CLANG_ASFLAGS": {"clang_asflags", bpparser.List}, |
| "LOCAL_CFLAGS": {"cflags", bpparser.List}, |
| "LOCAL_CONLYFLAGS": {"conlyflags", bpparser.List}, |
| "LOCAL_CPPFLAGS": {"cppflags", bpparser.List}, |
| "LOCAL_REQUIRED_MODULES": {"required", bpparser.List}, |
| "LOCAL_MODULE_TAGS": {"tags", bpparser.List}, |
| "LOCAL_LDLIBS": {"host_ldlibs", bpparser.List}, |
| "LOCAL_CLANG_CFLAGS": {"clang_cflags", bpparser.List}, |
| "LOCAL_YACCFLAGS": {"yaccflags", bpparser.List}, |
| "LOCAL_SANITIZE_RECOVER": {"sanitize.recover", bpparser.List}, |
| "LOCAL_LOGTAGS_FILES": {"logtags", bpparser.List}, |
| "LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": {"export_shared_lib_headers", bpparser.List}, |
| "LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": {"export_static_lib_headers", bpparser.List}, |
| |
| "LOCAL_JAVA_RESOURCE_DIRS": {"java_resource_dirs", bpparser.List}, |
| "LOCAL_JAVACFLAGS": {"javacflags", bpparser.List}, |
| "LOCAL_DX_FLAGS": {"dxflags", bpparser.List}, |
| "LOCAL_JAVA_LIBRARIES": {"java_libs", bpparser.List}, |
| "LOCAL_STATIC_JAVA_LIBRARIES": {"java_static_libs", bpparser.List}, |
| "LOCAL_AIDL_INCLUDES": {"aidl_includes", bpparser.List}, |
| "LOCAL_AAPT_FLAGS": {"aaptflags", bpparser.List}, |
| "LOCAL_PACKAGE_SPLITS": {"package_splits", bpparser.List}, |
| |
| // Bool properties |
| "LOCAL_IS_HOST_MODULE": {"host", bpparser.Bool}, |
| "LOCAL_CLANG": {"clang", bpparser.Bool}, |
| "LOCAL_FORCE_STATIC_EXECUTABLE": {"static_executable", bpparser.Bool}, |
| "LOCAL_NATIVE_COVERAGE": {"native_coverage", bpparser.Bool}, |
| "LOCAL_NO_CRT": {"nocrt", bpparser.Bool}, |
| "LOCAL_ALLOW_UNDEFINED_SYMBOLS": {"allow_undefined_symbols", bpparser.Bool}, |
| "LOCAL_RTTI_FLAG": {"rtti", bpparser.Bool}, |
| |
| "LOCAL_NO_STANDARD_LIBRARIES": {"no_standard_libraries", bpparser.Bool}, |
| |
| "LOCAL_EXPORT_PACKAGE_RESOURCES": {"export_package_resources", bpparser.Bool}, |
| } |
| |
| var rewriteProperties = map[string]struct { |
| f func(file *bpFile, prefix string, value *mkparser.MakeString, append bool) error |
| }{ |
| "LOCAL_C_INCLUDES": {localIncludeDirs}, |
| "LOCAL_EXPORT_C_INCLUDE_DIRS": {exportIncludeDirs}, |
| "LOCAL_MODULE_STEM": {stem}, |
| "LOCAL_MODULE_HOST_OS": {hostOs}, |
| "LOCAL_SRC_FILES": {srcFiles}, |
| "LOCAL_SANITIZE": {sanitize}, |
| "LOCAL_LDFLAGS": {ldflags}, |
| } |
| |
| type listSplitFunc func(bpparser.Value) (string, *bpparser.Value, error) |
| |
| func emptyList(value *bpparser.Value) bool { |
| return value.Type == bpparser.List && value.Expression == nil && value.Variable == "" && |
| len(value.ListValue) == 0 |
| } |
| |
| func splitBpList(val *bpparser.Value, keyFunc listSplitFunc) (lists map[string]*bpparser.Value, err error) { |
| lists = make(map[string]*bpparser.Value) |
| |
| if val.Expression != nil { |
| listsA, err := splitBpList(&val.Expression.Args[0], keyFunc) |
| if err != nil { |
| return nil, err |
| } |
| |
| listsB, err := splitBpList(&val.Expression.Args[1], keyFunc) |
| if err != nil { |
| return nil, err |
| } |
| |
| for k, v := range listsA { |
| if !emptyList(v) { |
| lists[k] = v |
| } |
| } |
| |
| for k, vB := range listsB { |
| if emptyList(vB) { |
| continue |
| } |
| |
| if vA, ok := lists[k]; ok { |
| expression := *val.Expression |
| lists[k] = &bpparser.Value{ |
| Type: bpparser.List, |
| Expression: &expression, |
| } |
| lists[k].Expression.Args = [2]bpparser.Value{*vA, *vB} |
| } else { |
| lists[k] = vB |
| } |
| } |
| } else if val.Variable != "" { |
| key, value, err := keyFunc(*val) |
| if err != nil { |
| return nil, err |
| } |
| if value.Type == bpparser.List { |
| lists[key] = value |
| } else { |
| lists[key] = &bpparser.Value{ |
| Type: bpparser.List, |
| ListValue: []bpparser.Value{*value}, |
| } |
| } |
| } else { |
| for _, v := range val.ListValue { |
| key, value, err := keyFunc(v) |
| if err != nil { |
| return nil, err |
| } |
| if _, ok := lists[key]; !ok { |
| lists[key] = &bpparser.Value{ |
| Type: bpparser.List, |
| } |
| } |
| lists[key].ListValue = append(lists[key].ListValue, *value) |
| } |
| } |
| |
| return lists, nil |
| } |
| |
| func splitLocalGlobalPath(value bpparser.Value) (string, *bpparser.Value, error) { |
| if value.Variable == "LOCAL_PATH" { |
| return "local", &bpparser.Value{ |
| Type: bpparser.String, |
| StringValue: ".", |
| }, nil |
| } else if value.Variable != "" { |
| // TODO: Should we split variables? |
| return "global", &value, nil |
| } |
| |
| if value.Type != bpparser.String { |
| return "", nil, fmt.Errorf("splitLocalGlobalPath expected a string, got %s", value.Type) |
| } |
| |
| if value.Expression == nil { |
| return "global", &value, nil |
| } |
| |
| if value.Expression.Operator != '+' { |
| return "global", &value, nil |
| } |
| |
| firstOperand := value.Expression.Args[0] |
| secondOperand := value.Expression.Args[1] |
| if firstOperand.Type != bpparser.String { |
| return "global", &value, nil |
| } |
| |
| if firstOperand.Expression != nil { |
| return "global", &value, nil |
| } |
| |
| if firstOperand.Variable != "LOCAL_PATH" { |
| return "global", &value, nil |
| } |
| |
| if secondOperand.Expression == nil && secondOperand.Variable == "" { |
| if strings.HasPrefix(secondOperand.StringValue, "/") { |
| secondOperand.StringValue = secondOperand.StringValue[1:] |
| } |
| } |
| return "local", &secondOperand, nil |
| } |
| |
| func localIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, value, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| lists, err := splitBpList(val, splitLocalGlobalPath) |
| if err != nil { |
| return err |
| } |
| |
| if global, ok := lists["global"]; ok && !emptyList(global) { |
| err = setVariable(file, appendVariable, prefix, "include_dirs", global, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| if local, ok := lists["local"]; ok && !emptyList(local) { |
| err = setVariable(file, appendVariable, prefix, "local_include_dirs", local, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func exportIncludeDirs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, value, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| lists, err := splitBpList(val, splitLocalGlobalPath) |
| if err != nil { |
| return err |
| } |
| |
| if local, ok := lists["local"]; ok && !emptyList(local) { |
| err = setVariable(file, appendVariable, prefix, "export_include_dirs", local, true) |
| if err != nil { |
| return err |
| } |
| appendVariable = true |
| } |
| |
| // Add any paths that could not be converted to local relative paths to export_include_dirs |
| // anyways, they will cause an error if they don't exist and can be fixed manually. |
| if global, ok := lists["global"]; ok && !emptyList(global) { |
| err = setVariable(file, appendVariable, prefix, "export_include_dirs", global, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func stem(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, value, bpparser.String) |
| if err != nil { |
| return err |
| } |
| varName := "stem" |
| |
| if val.Expression != nil && val.Expression.Operator == '+' && |
| val.Expression.Args[0].Variable == "LOCAL_MODULE" { |
| varName = "suffix" |
| val = &val.Expression.Args[1] |
| } |
| |
| return setVariable(file, appendVariable, prefix, varName, val, true) |
| } |
| |
| func hostOs(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, value, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| inList := func(s string) bool { |
| for _, v := range val.ListValue { |
| if v.StringValue == s { |
| return true |
| } |
| } |
| return false |
| } |
| |
| falseValue := &bpparser.Value{ |
| Type: bpparser.Bool, |
| BoolValue: false, |
| } |
| |
| trueValue := &bpparser.Value{ |
| Type: bpparser.Bool, |
| BoolValue: true, |
| } |
| |
| if inList("windows") { |
| err = setVariable(file, appendVariable, "target.windows", "enabled", trueValue, true) |
| } |
| |
| if !inList("linux") && err == nil { |
| err = setVariable(file, appendVariable, "target.linux", "enabled", falseValue, true) |
| } |
| |
| if !inList("darwin") && err == nil { |
| err = setVariable(file, appendVariable, "target.darwin", "enabled", falseValue, true) |
| } |
| |
| return err |
| } |
| |
| func splitSrcsLogtags(value bpparser.Value) (string, *bpparser.Value, error) { |
| if value.Variable != "" { |
| // TODO: attempt to split variables? |
| return "srcs", &value, nil |
| } |
| |
| if value.Type != bpparser.String { |
| return "", nil, fmt.Errorf("splitSrcsLogtags expected a string, got %s", value.Type) |
| } |
| |
| if value.Expression != nil { |
| // TODO: attempt to handle expressions? |
| return "srcs", &value, nil |
| } |
| |
| if strings.HasSuffix(value.StringValue, ".logtags") { |
| return "logtags", &value, nil |
| } |
| |
| return "srcs", &value, nil |
| } |
| |
| func srcFiles(file *bpFile, prefix string, value *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, value, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| lists, err := splitBpList(val, splitSrcsLogtags) |
| |
| if srcs, ok := lists["srcs"]; ok && !emptyList(srcs) { |
| err = setVariable(file, appendVariable, prefix, "srcs", srcs, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| if logtags, ok := lists["logtags"]; ok && !emptyList(logtags) { |
| err = setVariable(file, true, prefix, "logtags", logtags, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func sanitize(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, mkvalue, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| lists, err := splitBpList(val, func(value bpparser.Value) (string, *bpparser.Value, error) { |
| if value.Variable != "" { |
| return "vars", &value, nil |
| } |
| |
| if value.Expression != nil { |
| file.errorf(mkvalue, "unknown sanitize expression") |
| return "unknown", &value, nil |
| } |
| |
| switch value.StringValue { |
| case "never", "address", "coverage", "integer", "thread", "undefined": |
| bpTrue := bpparser.Value{ |
| Type: bpparser.Bool, |
| BoolValue: true, |
| } |
| return value.StringValue, &bpTrue, nil |
| default: |
| file.errorf(mkvalue, "unknown sanitize argument: %s", value.StringValue) |
| return "unknown", &value, nil |
| } |
| }) |
| if err != nil { |
| return err |
| } |
| |
| for k, v := range lists { |
| if emptyList(v) { |
| continue |
| } |
| |
| switch k { |
| case "never", "address", "coverage", "integer", "thread", "undefined": |
| err = setVariable(file, false, prefix, "sanitize."+k, &lists[k].ListValue[0], true) |
| case "unknown": |
| // Nothing, we already added the error above |
| case "vars": |
| fallthrough |
| default: |
| err = setVariable(file, true, prefix, "sanitize", v, true) |
| } |
| |
| if err != nil { |
| return err |
| } |
| } |
| |
| return err |
| } |
| |
| func ldflags(file *bpFile, prefix string, mkvalue *mkparser.MakeString, appendVariable bool) error { |
| val, err := makeVariableToBlueprint(file, mkvalue, bpparser.List) |
| if err != nil { |
| return err |
| } |
| |
| lists, err := splitBpList(val, func(value bpparser.Value) (string, *bpparser.Value, error) { |
| // Anything other than "-Wl,--version_script," + LOCAL_PATH + "<path>" matches ldflags |
| if value.Variable != "" || value.Expression == nil { |
| return "ldflags", &value, nil |
| } |
| |
| exp := value.Expression.Args[0] |
| if exp.Variable != "" || exp.Expression == nil || exp.Expression.Args[0].StringValue != "-Wl,--version-script," { |
| return "ldflags", &value, nil |
| } |
| |
| if exp.Expression.Args[1].Variable != "LOCAL_PATH" { |
| file.errorf(mkvalue, "Unrecognized version-script") |
| return "ldflags", &value, nil |
| } |
| |
| value.Expression.Args[1].StringValue = strings.TrimPrefix(value.Expression.Args[1].StringValue, "/") |
| |
| return "version", &value.Expression.Args[1], nil |
| }) |
| if err != nil { |
| return err |
| } |
| |
| if ldflags, ok := lists["ldflags"]; ok && !emptyList(ldflags) { |
| err = setVariable(file, appendVariable, prefix, "ldflags", ldflags, true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| if version_script, ok := lists["version"]; ok && !emptyList(version_script) { |
| if len(version_script.ListValue) > 1 { |
| file.errorf(mkvalue, "multiple version scripts found?") |
| } |
| err = setVariable(file, false, prefix, "version_script", &version_script.ListValue[0], true) |
| if err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| var deleteProperties = map[string]struct{}{ |
| "LOCAL_CPP_EXTENSION": struct{}{}, |
| } |
| |
| // Shorter suffixes of other suffixes must be at the end of the list |
| var propertyPrefixes = []struct{ mk, bp string }{ |
| {"arm", "arch.arm"}, |
| {"arm64", "arch.arm64"}, |
| {"mips", "arch.mips"}, |
| {"mips64", "arch.mips64"}, |
| {"x86", "arch.x86"}, |
| {"x86_64", "arch.x86_64"}, |
| {"32", "multilib.lib32"}, |
| // 64 must be after x86_64 |
| {"64", "multilib.lib64"}, |
| {"darwin", "target.darwin"}, |
| {"linux", "target.linux"}, |
| {"windows", "target.windows"}, |
| } |
| |
| var conditionalTranslations = map[string]map[bool]string{ |
| "($(HOST_OS),darwin)": { |
| true: "target.darwin", |
| false: "target.not_darwin"}, |
| "($(HOST_OS), darwin)": { |
| true: "target.darwin", |
| false: "target.not_darwin"}, |
| "($(HOST_OS),windows)": { |
| true: "target.windows", |
| false: "target.not_windows"}, |
| "($(HOST_OS), windows)": { |
| true: "target.windows", |
| false: "target.not_windows"}, |
| "($(HOST_OS),linux)": { |
| true: "target.linux", |
| false: "target.not_linux"}, |
| "($(HOST_OS), linux)": { |
| true: "target.linux", |
| false: "target.not_linux"}, |
| "($(BUILD_OS),darwin)": { |
| true: "target.darwin", |
| false: "target.not_darwin"}, |
| "($(BUILD_OS), darwin)": { |
| true: "target.darwin", |
| false: "target.not_darwin"}, |
| "($(BUILD_OS),linux)": { |
| true: "target.linux", |
| false: "target.not_linux"}, |
| "($(BUILD_OS), linux)": { |
| true: "target.linux", |
| false: "target.not_linux"}, |
| "(,$(TARGET_BUILD_APPS))": { |
| false: "product_variables.unbundled_build", |
| }, |
| } |
| |
| func mydir(args []string) string { |
| return "." |
| } |
| |
| func allJavaFilesUnder(args []string) string { |
| dir := "" |
| if len(args) > 0 { |
| dir = strings.TrimSpace(args[0]) |
| } |
| |
| return fmt.Sprintf("%s/**/*.java", dir) |
| } |
| |
| func allSubdirJavaFiles(args []string) string { |
| return "**/*.java" |
| } |
| |
| var moduleTypes = map[string]string{ |
| "BUILD_SHARED_LIBRARY": "cc_library_shared", |
| "BUILD_STATIC_LIBRARY": "cc_library_static", |
| "BUILD_HOST_SHARED_LIBRARY": "cc_library_host_shared", |
| "BUILD_HOST_STATIC_LIBRARY": "cc_library_host_static", |
| "BUILD_EXECUTABLE": "cc_binary", |
| "BUILD_HOST_EXECUTABLE": "cc_binary_host", |
| "BUILD_NATIVE_TEST": "cc_test", |
| "BUILD_HOST_NATIVE_TEST": "cc_test_host", |
| "BUILD_NATIVE_BENCHMARK": "cc_benchmark", |
| "BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host", |
| |
| "BUILD_JAVA_LIBRARY": "java_library", |
| "BUILD_STATIC_JAVA_LIBRARY": "java_library_static", |
| "BUILD_HOST_JAVA_LIBRARY": "java_library_host", |
| "BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik", |
| "BUILD_PACKAGE": "android_app", |
| |
| "BUILD_PREBUILT": "prebuilt", |
| } |
| |
| var soongModuleTypes = map[string]bool{} |
| |
| func androidScope() mkparser.Scope { |
| globalScope := mkparser.NewScope(nil) |
| globalScope.Set("CLEAR_VARS", clear_vars) |
| globalScope.SetFunc("my-dir", mydir) |
| globalScope.SetFunc("all-java-files-under", allJavaFilesUnder) |
| globalScope.SetFunc("all-subdir-java-files", allSubdirJavaFiles) |
| |
| for k, v := range moduleTypes { |
| globalScope.Set(k, v) |
| soongModuleTypes[v] = true |
| } |
| |
| return globalScope |
| } |