Home | History | Annotate | Download | only in androidmk
      1 // Copyright 2017 Google Inc. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package main
     16 
     17 import (
     18 	mkparser "android/soong/androidmk/parser"
     19 	"fmt"
     20 	"sort"
     21 	"strings"
     22 
     23 	bpparser "github.com/google/blueprint/parser"
     24 )
     25 
     26 const (
     27 	clear_vars      = "__android_mk_clear_vars"
     28 	include_ignored = "__android_mk_include_ignored"
     29 )
     30 
     31 type bpVariable struct {
     32 	name         string
     33 	variableType bpparser.Type
     34 }
     35 
     36 type variableAssignmentContext struct {
     37 	file    *bpFile
     38 	prefix  string
     39 	mkvalue *mkparser.MakeString
     40 	append  bool
     41 }
     42 
     43 var rewriteProperties = map[string](func(variableAssignmentContext) error){
     44 	// custom functions
     45 	"LOCAL_32_BIT_ONLY":           local32BitOnly,
     46 	"LOCAL_AIDL_INCLUDES":         localAidlIncludes,
     47 	"LOCAL_ASSET_DIR":             localizePathList("asset_dirs"),
     48 	"LOCAL_C_INCLUDES":            localIncludeDirs,
     49 	"LOCAL_EXPORT_C_INCLUDE_DIRS": exportIncludeDirs,
     50 	"LOCAL_JARJAR_RULES":          localizePath("jarjar_rules"),
     51 	"LOCAL_LDFLAGS":               ldflags,
     52 	"LOCAL_MODULE_CLASS":          prebuiltClass,
     53 	"LOCAL_MODULE_STEM":           stem,
     54 	"LOCAL_MODULE_HOST_OS":        hostOs,
     55 	"LOCAL_RESOURCE_DIR":          localizePathList("resource_dirs"),
     56 	"LOCAL_SANITIZE":              sanitize(""),
     57 	"LOCAL_SANITIZE_DIAG":         sanitize("diag."),
     58 	"LOCAL_STRIP_MODULE":          strip(),
     59 	"LOCAL_CFLAGS":                cflags,
     60 	"LOCAL_UNINSTALLABLE_MODULE":  invert("installable"),
     61 	"LOCAL_PROGUARD_ENABLED":      proguardEnabled,
     62 	"LOCAL_MODULE_PATH":           prebuiltModulePath,
     63 
     64 	// composite functions
     65 	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
     66 
     67 	// skip functions
     68 	"LOCAL_ADDITIONAL_DEPENDENCIES": skip, // TODO: check for only .mk files?
     69 	"LOCAL_CPP_EXTENSION":           skip,
     70 	"LOCAL_MODULE_SUFFIX":           skip, // TODO
     71 	"LOCAL_PATH":                    skip, // Nothing to do, except maybe avoid the "./" in paths?
     72 	"LOCAL_PRELINK_MODULE":          skip, // Already phased out
     73 	"LOCAL_BUILT_MODULE_STEM":       skip,
     74 	"LOCAL_USE_AAPT2":               skip, // Always enabled in Soong
     75 	"LOCAL_JAR_EXCLUDE_FILES":       skip, // Soong never excludes files from jars
     76 
     77 	"LOCAL_ANNOTATION_PROCESSOR_CLASSES": skip, // Soong gets the processor classes from the plugin
     78 	"LOCAL_CTS_TEST_PACKAGE":             skip, // Obsolete
     79 	"LOCAL_JACK_ENABLED":                 skip, // Obselete
     80 	"LOCAL_JACK_FLAGS":                   skip, // Obselete
     81 }
     82 
     83 // adds a group of properties all having the same type
     84 func addStandardProperties(propertyType bpparser.Type, properties map[string]string) {
     85 	for key, val := range properties {
     86 		rewriteProperties[key] = includeVariable(bpVariable{val, propertyType})
     87 	}
     88 }
     89 
     90 func init() {
     91 	addStandardProperties(bpparser.StringType,
     92 		map[string]string{
     93 			"LOCAL_MODULE":                  "name",
     94 			"LOCAL_CXX_STL":                 "stl",
     95 			"LOCAL_MULTILIB":                "compile_multilib",
     96 			"LOCAL_ARM_MODE_HACK":           "instruction_set",
     97 			"LOCAL_SDK_VERSION":             "sdk_version",
     98 			"LOCAL_MIN_SDK_VERSION":         "min_sdk_version",
     99 			"LOCAL_NDK_STL_VARIANT":         "stl",
    100 			"LOCAL_JAR_MANIFEST":            "manifest",
    101 			"LOCAL_CERTIFICATE":             "certificate",
    102 			"LOCAL_PACKAGE_NAME":            "name",
    103 			"LOCAL_MODULE_RELATIVE_PATH":    "relative_install_path",
    104 			"LOCAL_PROTOC_OPTIMIZE_TYPE":    "proto.type",
    105 			"LOCAL_MODULE_OWNER":            "owner",
    106 			"LOCAL_RENDERSCRIPT_TARGET_API": "renderscript.target_api",
    107 			"LOCAL_NOTICE_FILE":             "notice",
    108 			"LOCAL_JAVA_LANGUAGE_VERSION":   "java_version",
    109 			"LOCAL_INSTRUMENTATION_FOR":     "instrumentation_for",
    110 			"LOCAL_MANIFEST_FILE":           "manifest",
    111 
    112 			"LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile",
    113 			"LOCAL_TEST_CONFIG":                      "test_config",
    114 		})
    115 	addStandardProperties(bpparser.ListType,
    116 		map[string]string{
    117 			"LOCAL_SRC_FILES":                     "srcs",
    118 			"LOCAL_SRC_FILES_EXCLUDE":             "exclude_srcs",
    119 			"LOCAL_HEADER_LIBRARIES":              "header_libs",
    120 			"LOCAL_SHARED_LIBRARIES":              "shared_libs",
    121 			"LOCAL_STATIC_LIBRARIES":              "static_libs",
    122 			"LOCAL_WHOLE_STATIC_LIBRARIES":        "whole_static_libs",
    123 			"LOCAL_SYSTEM_SHARED_LIBRARIES":       "system_shared_libs",
    124 			"LOCAL_ASFLAGS":                       "asflags",
    125 			"LOCAL_CLANG_ASFLAGS":                 "clang_asflags",
    126 			"LOCAL_CONLYFLAGS":                    "conlyflags",
    127 			"LOCAL_CPPFLAGS":                      "cppflags",
    128 			"LOCAL_REQUIRED_MODULES":              "required",
    129 			"LOCAL_OVERRIDES_MODULES":             "overrides",
    130 			"LOCAL_LDLIBS":                        "host_ldlibs",
    131 			"LOCAL_CLANG_CFLAGS":                  "clang_cflags",
    132 			"LOCAL_YACCFLAGS":                     "yaccflags",
    133 			"LOCAL_SANITIZE_RECOVER":              "sanitize.recover",
    134 			"LOCAL_LOGTAGS_FILES":                 "logtags",
    135 			"LOCAL_EXPORT_HEADER_LIBRARY_HEADERS": "export_header_lib_headers",
    136 			"LOCAL_EXPORT_SHARED_LIBRARY_HEADERS": "export_shared_lib_headers",
    137 			"LOCAL_EXPORT_STATIC_LIBRARY_HEADERS": "export_static_lib_headers",
    138 			"LOCAL_INIT_RC":                       "init_rc",
    139 			"LOCAL_VINTF_FRAGMENTS":               "vintf_fragments",
    140 			"LOCAL_TIDY_FLAGS":                    "tidy_flags",
    141 			// TODO: This is comma-separated, not space-separated
    142 			"LOCAL_TIDY_CHECKS":           "tidy_checks",
    143 			"LOCAL_RENDERSCRIPT_INCLUDES": "renderscript.include_dirs",
    144 			"LOCAL_RENDERSCRIPT_FLAGS":    "renderscript.flags",
    145 
    146 			"LOCAL_JAVA_RESOURCE_DIRS":    "java_resource_dirs",
    147 			"LOCAL_JAVACFLAGS":            "javacflags",
    148 			"LOCAL_ERROR_PRONE_FLAGS":     "errorprone.javacflags",
    149 			"LOCAL_DX_FLAGS":              "dxflags",
    150 			"LOCAL_JAVA_LIBRARIES":        "libs",
    151 			"LOCAL_STATIC_JAVA_LIBRARIES": "static_libs",
    152 			"LOCAL_JNI_SHARED_LIBRARIES":  "jni_libs",
    153 			"LOCAL_AAPT_FLAGS":            "aaptflags",
    154 			"LOCAL_PACKAGE_SPLITS":        "package_splits",
    155 			"LOCAL_COMPATIBILITY_SUITE":   "test_suites",
    156 			"LOCAL_OVERRIDES_PACKAGES":    "overrides",
    157 
    158 			"LOCAL_ANNOTATION_PROCESSORS": "plugins",
    159 
    160 			"LOCAL_PROGUARD_FLAGS":      "optimize.proguard_flags",
    161 			"LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flags_files",
    162 
    163 			// These will be rewritten to libs/static_libs by bpfix, after their presence is used to convert
    164 			// java_library_static to android_library.
    165 			"LOCAL_SHARED_ANDROID_LIBRARIES": "android_libs",
    166 			"LOCAL_STATIC_ANDROID_LIBRARIES": "android_static_libs",
    167 			"LOCAL_ADDITIONAL_CERTIFICATES":  "additional_certificates",
    168 
    169 			// Jacoco filters:
    170 			"LOCAL_JACK_COVERAGE_INCLUDE_FILTER": "jacoco.include_filter",
    171 			"LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
    172 		})
    173 
    174 	addStandardProperties(bpparser.BoolType,
    175 		map[string]string{
    176 			// Bool properties
    177 			"LOCAL_IS_HOST_MODULE":             "host",
    178 			"LOCAL_CLANG":                      "clang",
    179 			"LOCAL_FORCE_STATIC_EXECUTABLE":    "static_executable",
    180 			"LOCAL_NATIVE_COVERAGE":            "native_coverage",
    181 			"LOCAL_NO_CRT":                     "nocrt",
    182 			"LOCAL_ALLOW_UNDEFINED_SYMBOLS":    "allow_undefined_symbols",
    183 			"LOCAL_RTTI_FLAG":                  "rtti",
    184 			"LOCAL_NO_STANDARD_LIBRARIES":      "no_standard_libs",
    185 			"LOCAL_PACK_MODULE_RELOCATIONS":    "pack_relocations",
    186 			"LOCAL_TIDY":                       "tidy",
    187 			"LOCAL_USE_CLANG_LLD":              "use_clang_lld",
    188 			"LOCAL_PROPRIETARY_MODULE":         "proprietary",
    189 			"LOCAL_VENDOR_MODULE":              "vendor",
    190 			"LOCAL_ODM_MODULE":                 "device_specific",
    191 			"LOCAL_PRODUCT_MODULE":             "product_specific",
    192 			"LOCAL_PRODUCT_SERVICES_MODULE":    "product_services_specific",
    193 			"LOCAL_EXPORT_PACKAGE_RESOURCES":   "export_package_resources",
    194 			"LOCAL_PRIVILEGED_MODULE":          "privileged",
    195 			"LOCAL_AAPT_INCLUDE_ALL_RESOURCES": "aapt_include_all_resources",
    196 			"LOCAL_USE_EMBEDDED_NATIVE_LIBS":   "use_embedded_native_libs",
    197 			"LOCAL_USE_EMBEDDED_DEX":           "use_embedded_dex",
    198 
    199 			"LOCAL_DEX_PREOPT":                  "dex_preopt.enabled",
    200 			"LOCAL_DEX_PREOPT_APP_IMAGE":        "dex_preopt.app_image",
    201 			"LOCAL_DEX_PREOPT_GENERATE_PROFILE": "dex_preopt.profile_guided",
    202 
    203 			"LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
    204 			"LOCAL_JETIFIER_ENABLED":      "jetifier",
    205 		})
    206 }
    207 
    208 type listSplitFunc func(bpparser.Expression) (string, bpparser.Expression, error)
    209 
    210 func emptyList(value bpparser.Expression) bool {
    211 	if list, ok := value.(*bpparser.List); ok {
    212 		return len(list.Values) == 0
    213 	}
    214 	return false
    215 }
    216 
    217 func splitBpList(val bpparser.Expression, keyFunc listSplitFunc) (lists map[string]bpparser.Expression, err error) {
    218 	lists = make(map[string]bpparser.Expression)
    219 
    220 	switch val := val.(type) {
    221 	case *bpparser.Operator:
    222 		listsA, err := splitBpList(val.Args[0], keyFunc)
    223 		if err != nil {
    224 			return nil, err
    225 		}
    226 
    227 		listsB, err := splitBpList(val.Args[1], keyFunc)
    228 		if err != nil {
    229 			return nil, err
    230 		}
    231 
    232 		for k, v := range listsA {
    233 			if !emptyList(v) {
    234 				lists[k] = v
    235 			}
    236 		}
    237 
    238 		for k, vB := range listsB {
    239 			if emptyList(vB) {
    240 				continue
    241 			}
    242 
    243 			if vA, ok := lists[k]; ok {
    244 				expression := val.Copy().(*bpparser.Operator)
    245 				expression.Args = [2]bpparser.Expression{vA, vB}
    246 				lists[k] = expression
    247 			} else {
    248 				lists[k] = vB
    249 			}
    250 		}
    251 	case *bpparser.Variable:
    252 		key, value, err := keyFunc(val)
    253 		if err != nil {
    254 			return nil, err
    255 		}
    256 		if value.Type() == bpparser.ListType {
    257 			lists[key] = value
    258 		} else {
    259 			lists[key] = &bpparser.List{
    260 				Values: []bpparser.Expression{value},
    261 			}
    262 		}
    263 	case *bpparser.List:
    264 		for _, v := range val.Values {
    265 			key, value, err := keyFunc(v)
    266 			if err != nil {
    267 				return nil, err
    268 			}
    269 			l := lists[key]
    270 			if l == nil {
    271 				l = &bpparser.List{}
    272 			}
    273 			l.(*bpparser.List).Values = append(l.(*bpparser.List).Values, value)
    274 			lists[key] = l
    275 		}
    276 	default:
    277 		panic(fmt.Errorf("unexpected type %t", val))
    278 	}
    279 
    280 	return lists, nil
    281 }
    282 
    283 // classifyLocalOrGlobalPath tells whether a file path should be interpreted relative to the current module (local)
    284 // or relative to the root of the source checkout (global)
    285 func classifyLocalOrGlobalPath(value bpparser.Expression) (string, bpparser.Expression, error) {
    286 	switch v := value.(type) {
    287 	case *bpparser.Variable:
    288 		if v.Name == "LOCAL_PATH" {
    289 			return "local", &bpparser.String{
    290 				Value: ".",
    291 			}, nil
    292 		} else {
    293 			// TODO: Should we split variables?
    294 			return "global", value, nil
    295 		}
    296 	case *bpparser.Operator:
    297 		if v.Type() != bpparser.StringType {
    298 			return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", v.Type())
    299 		}
    300 
    301 		if v.Operator != '+' {
    302 			return "global", value, nil
    303 		}
    304 
    305 		firstOperand := v.Args[0]
    306 		secondOperand := v.Args[1]
    307 		if firstOperand.Type() != bpparser.StringType {
    308 			return "global", value, nil
    309 		}
    310 
    311 		if _, ok := firstOperand.(*bpparser.Operator); ok {
    312 			return "global", value, nil
    313 		}
    314 
    315 		if variable, ok := firstOperand.(*bpparser.Variable); !ok || variable.Name != "LOCAL_PATH" {
    316 			return "global", value, nil
    317 		}
    318 
    319 		local := secondOperand
    320 		if s, ok := secondOperand.(*bpparser.String); ok {
    321 			if strings.HasPrefix(s.Value, "/") {
    322 				s.Value = s.Value[1:]
    323 			}
    324 		}
    325 		return "local", local, nil
    326 	case *bpparser.String:
    327 		return "global", value, nil
    328 	default:
    329 		return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", v.Type())
    330 
    331 	}
    332 }
    333 
    334 func sortedMapKeys(inputMap map[string]string) (sortedKeys []string) {
    335 	keys := make([]string, 0, len(inputMap))
    336 	for key := range inputMap {
    337 		keys = append(keys, key)
    338 	}
    339 	sort.Strings(keys)
    340 	return keys
    341 }
    342 
    343 // splitAndAssign splits a Make list into components and then
    344 // creates the corresponding variable assignments.
    345 func splitAndAssign(ctx variableAssignmentContext, splitFunc listSplitFunc, namesByClassification map[string]string) error {
    346 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    347 	if err != nil {
    348 		return err
    349 	}
    350 
    351 	lists, err := splitBpList(val, splitFunc)
    352 	if err != nil {
    353 		return err
    354 	}
    355 
    356 	for _, nameClassification := range sortedMapKeys(namesByClassification) {
    357 		name := namesByClassification[nameClassification]
    358 		if component, ok := lists[nameClassification]; ok && !emptyList(component) {
    359 			err = setVariable(ctx.file, ctx.append, ctx.prefix, name, component, true)
    360 			if err != nil {
    361 				return err
    362 			}
    363 		}
    364 	}
    365 	return nil
    366 }
    367 
    368 func localIncludeDirs(ctx variableAssignmentContext) error {
    369 	return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "include_dirs", "local": "local_include_dirs"})
    370 }
    371 
    372 func exportIncludeDirs(ctx variableAssignmentContext) error {
    373 	// Add any paths that could not be converted to local relative paths to export_include_dirs
    374 	// anyways, they will cause an error if they don't exist and can be fixed manually.
    375 	return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "export_include_dirs", "local": "export_include_dirs"})
    376 }
    377 
    378 func local32BitOnly(ctx variableAssignmentContext) error {
    379 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.BoolType)
    380 	if err != nil {
    381 		return err
    382 	}
    383 	if val.(*bpparser.Bool).Value {
    384 		thirtyTwo := &bpparser.String{
    385 			Value: "32",
    386 		}
    387 		setVariable(ctx.file, false, ctx.prefix, "compile_multilib", thirtyTwo, true)
    388 	}
    389 	return nil
    390 }
    391 
    392 func localAidlIncludes(ctx variableAssignmentContext) error {
    393 	return splitAndAssign(ctx, classifyLocalOrGlobalPath, map[string]string{"global": "aidl.include_dirs", "local": "aidl.local_include_dirs"})
    394 }
    395 
    396 func localizePathList(attribute string) func(ctx variableAssignmentContext) error {
    397 	return func(ctx variableAssignmentContext) error {
    398 		paths, err := localizePaths(ctx)
    399 		if err == nil {
    400 			err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, paths, true)
    401 		}
    402 		return err
    403 	}
    404 }
    405 
    406 func localizePath(attribute string) func(ctx variableAssignmentContext) error {
    407 	return func(ctx variableAssignmentContext) error {
    408 		paths, err := localizePaths(ctx)
    409 		if err == nil {
    410 			pathList, ok := paths.(*bpparser.List)
    411 			if !ok {
    412 				panic("Expected list")
    413 			}
    414 			switch len(pathList.Values) {
    415 			case 0:
    416 				err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, &bpparser.List{}, true)
    417 			case 1:
    418 				err = setVariable(ctx.file, ctx.append, ctx.prefix, attribute, pathList.Values[0], true)
    419 			default:
    420 				err = fmt.Errorf("Expected single value for %s", attribute)
    421 			}
    422 		}
    423 		return err
    424 	}
    425 }
    426 
    427 // Convert the "full" paths (that is, from the top of the source tree) to the relative one
    428 // (from the directory containing the blueprint file) and set given attribute to it.
    429 // This is needed for some of makefile variables (e.g., LOCAL_RESOURCE_DIR).
    430 // At the moment only the paths of the `$(LOCAL_PATH)/foo/bar` format can be converted
    431 // (to `foo/bar` in this case) as we cannot convert a literal path without
    432 // knowing makefiles's location in the source tree. We just issue a warning in the latter case.
    433 func localizePaths(ctx variableAssignmentContext) (bpparser.Expression, error) {
    434 	bpvalue, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    435 	var result bpparser.Expression
    436 	if err != nil {
    437 		return result, err
    438 	}
    439 	classifiedPaths, err := splitBpList(bpvalue, classifyLocalOrGlobalPath)
    440 	if err != nil {
    441 		return result, err
    442 	}
    443 	for pathClass, path := range classifiedPaths {
    444 		switch pathClass {
    445 		case "local":
    446 			result = path
    447 		default:
    448 			err = fmt.Errorf("Only $(LOCAL_PATH)/.. values are allowed")
    449 		}
    450 	}
    451 	return result, err
    452 }
    453 
    454 func stem(ctx variableAssignmentContext) error {
    455 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.StringType)
    456 	if err != nil {
    457 		return err
    458 	}
    459 	varName := "stem"
    460 
    461 	if exp, ok := val.(*bpparser.Operator); ok && exp.Operator == '+' {
    462 		if variable, ok := exp.Args[0].(*bpparser.Variable); ok && variable.Name == "LOCAL_MODULE" {
    463 			varName = "suffix"
    464 			val = exp.Args[1]
    465 		}
    466 	}
    467 
    468 	return setVariable(ctx.file, ctx.append, ctx.prefix, varName, val, true)
    469 }
    470 
    471 func hostOs(ctx variableAssignmentContext) error {
    472 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    473 	if err != nil {
    474 		return err
    475 	}
    476 
    477 	inList := func(s string) bool {
    478 		for _, v := range val.(*bpparser.List).Values {
    479 			if v.(*bpparser.String).Value == s {
    480 				return true
    481 			}
    482 		}
    483 		return false
    484 	}
    485 
    486 	falseValue := &bpparser.Bool{
    487 		Value: false,
    488 	}
    489 
    490 	trueValue := &bpparser.Bool{
    491 		Value: true,
    492 	}
    493 
    494 	if inList("windows") {
    495 		err = setVariable(ctx.file, ctx.append, "target.windows", "enabled", trueValue, true)
    496 	}
    497 
    498 	if !inList("linux") && err == nil {
    499 		err = setVariable(ctx.file, ctx.append, "target.linux_glibc", "enabled", falseValue, true)
    500 	}
    501 
    502 	if !inList("darwin") && err == nil {
    503 		err = setVariable(ctx.file, ctx.append, "target.darwin", "enabled", falseValue, true)
    504 	}
    505 
    506 	return err
    507 }
    508 
    509 func sanitize(sub string) func(ctx variableAssignmentContext) error {
    510 	return func(ctx variableAssignmentContext) error {
    511 		val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    512 		if err != nil {
    513 			return err
    514 		}
    515 
    516 		if _, ok := val.(*bpparser.List); !ok {
    517 			return fmt.Errorf("unsupported sanitize expression")
    518 		}
    519 
    520 		misc := &bpparser.List{}
    521 
    522 		for _, v := range val.(*bpparser.List).Values {
    523 			switch v := v.(type) {
    524 			case *bpparser.Variable, *bpparser.Operator:
    525 				ctx.file.errorf(ctx.mkvalue, "unsupported sanitize expression")
    526 			case *bpparser.String:
    527 				switch v.Value {
    528 				case "never", "address", "coverage", "thread", "undefined", "cfi":
    529 					bpTrue := &bpparser.Bool{
    530 						Value: true,
    531 					}
    532 					err = setVariable(ctx.file, false, ctx.prefix, "sanitize."+sub+v.Value, bpTrue, true)
    533 					if err != nil {
    534 						return err
    535 					}
    536 				default:
    537 					misc.Values = append(misc.Values, v)
    538 				}
    539 			default:
    540 				return fmt.Errorf("sanitize expected a string, got %s", v.Type())
    541 			}
    542 		}
    543 
    544 		if len(misc.Values) > 0 {
    545 			err = setVariable(ctx.file, false, ctx.prefix, "sanitize."+sub+"misc_undefined", misc, true)
    546 			if err != nil {
    547 				return err
    548 			}
    549 		}
    550 
    551 		return err
    552 	}
    553 }
    554 
    555 func strip() func(ctx variableAssignmentContext) error {
    556 	return func(ctx variableAssignmentContext) error {
    557 		val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.StringType)
    558 		if err != nil {
    559 			return err
    560 		}
    561 
    562 		if _, ok := val.(*bpparser.String); !ok {
    563 			return fmt.Errorf("unsupported strip expression")
    564 		}
    565 
    566 		bpTrue := &bpparser.Bool{
    567 			Value: true,
    568 		}
    569 		v := val.(*bpparser.String).Value
    570 		sub := (map[string]string{"false": "none", "true": "all", "keep_symbols": "keep_symbols"})[v]
    571 		if sub == "" {
    572 			return fmt.Errorf("unexpected strip option: %s", v)
    573 		}
    574 		return setVariable(ctx.file, false, ctx.prefix, "strip."+sub, bpTrue, true)
    575 	}
    576 }
    577 
    578 func prebuiltClass(ctx variableAssignmentContext) error {
    579 	class := ctx.mkvalue.Value(ctx.file.scope)
    580 	if _, ok := prebuiltTypes[class]; ok {
    581 		ctx.file.scope.Set("BUILD_PREBUILT", class)
    582 	} else {
    583 		// reset to default
    584 		ctx.file.scope.Set("BUILD_PREBUILT", "prebuilt")
    585 	}
    586 	return nil
    587 }
    588 
    589 func makeBlueprintStringAssignment(file *bpFile, prefix string, suffix string, value string) error {
    590 	val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString(value, mkparser.NoPos), bpparser.StringType)
    591 	if err == nil {
    592 		err = setVariable(file, false, prefix, suffix, val, true)
    593 	}
    594 	return err
    595 }
    596 
    597 // If variable is a literal variable name, return the name, otherwise return ""
    598 func varLiteralName(variable mkparser.Variable) string {
    599 	if len(variable.Name.Variables) == 0 {
    600 		return variable.Name.Strings[0]
    601 	}
    602 	return ""
    603 }
    604 
    605 func prebuiltModulePath(ctx variableAssignmentContext) error {
    606 	// Cannot handle appending
    607 	if ctx.append {
    608 		return fmt.Errorf("Cannot handle appending to LOCAL_MODULE_PATH")
    609 	}
    610 	// Analyze value in order to set the correct values for the 'device_specific',
    611 	// 'product_specific', 'product_services_specific' 'vendor'/'soc_specific',
    612 	// 'product_services_specific' attribute. Two cases are allowed:
    613 	//   $(VAR)/<literal-value>
    614 	//   $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/<literal-value>
    615 	// The last case is equivalent to $(TARGET_OUT_VENDOR)/<literal-value>
    616 	// Map the variable name if present to `local_module_path_var`
    617 	// Map literal-path to local_module_path_fixed
    618 	varname := ""
    619 	fixed := ""
    620 	val := ctx.mkvalue
    621 	if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
    622 		fixed = val.Strings[1]
    623 		varname = val.Variables[0].Name.Strings[0]
    624 	} else if len(val.Variables) == 2 && varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && varLiteralName(val.Variables[1]) == "TARGET_COPY_OUT_VENDOR" &&
    625 		len(val.Strings) == 3 && val.Strings[0] == "" && val.Strings[1] == "/" {
    626 		fixed = val.Strings[2]
    627 		varname = "TARGET_OUT_VENDOR"
    628 	} else {
    629 		return fmt.Errorf("LOCAL_MODULE_PATH value should start with $(<some-varaible>)/ or $(PRODUCT_OUT)/$(TARGET_COPY_VENDOR)/")
    630 	}
    631 	err := makeBlueprintStringAssignment(ctx.file, "local_module_path", "var", varname)
    632 	if err == nil && fixed != "" {
    633 		err = makeBlueprintStringAssignment(ctx.file, "local_module_path", "fixed", fixed)
    634 	}
    635 	return err
    636 }
    637 
    638 func ldflags(ctx variableAssignmentContext) error {
    639 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    640 	if err != nil {
    641 		return err
    642 	}
    643 
    644 	lists, err := splitBpList(val, func(value bpparser.Expression) (string, bpparser.Expression, error) {
    645 		// Anything other than "-Wl,--version_script," + LOCAL_PATH + "<path>" matches ldflags
    646 		exp1, ok := value.(*bpparser.Operator)
    647 		if !ok {
    648 			return "ldflags", value, nil
    649 		}
    650 
    651 		exp2, ok := exp1.Args[0].(*bpparser.Operator)
    652 		if !ok {
    653 			return "ldflags", value, nil
    654 		}
    655 
    656 		if s, ok := exp2.Args[0].(*bpparser.String); !ok || s.Value != "-Wl,--version-script," {
    657 			return "ldflags", value, nil
    658 		}
    659 
    660 		if v, ok := exp2.Args[1].(*bpparser.Variable); !ok || v.Name != "LOCAL_PATH" {
    661 			ctx.file.errorf(ctx.mkvalue, "Unrecognized version-script")
    662 			return "ldflags", value, nil
    663 		}
    664 
    665 		s, ok := exp1.Args[1].(*bpparser.String)
    666 		if !ok {
    667 			ctx.file.errorf(ctx.mkvalue, "Unrecognized version-script")
    668 			return "ldflags", value, nil
    669 		}
    670 
    671 		s.Value = strings.TrimPrefix(s.Value, "/")
    672 
    673 		return "version", s, nil
    674 	})
    675 	if err != nil {
    676 		return err
    677 	}
    678 
    679 	if ldflags, ok := lists["ldflags"]; ok && !emptyList(ldflags) {
    680 		err = setVariable(ctx.file, ctx.append, ctx.prefix, "ldflags", ldflags, true)
    681 		if err != nil {
    682 			return err
    683 		}
    684 	}
    685 
    686 	if version_script, ok := lists["version"]; ok && !emptyList(version_script) {
    687 		if len(version_script.(*bpparser.List).Values) > 1 {
    688 			ctx.file.errorf(ctx.mkvalue, "multiple version scripts found?")
    689 		}
    690 		err = setVariable(ctx.file, false, ctx.prefix, "version_script", version_script.(*bpparser.List).Values[0], true)
    691 		if err != nil {
    692 			return err
    693 		}
    694 	}
    695 
    696 	return nil
    697 }
    698 
    699 func cflags(ctx variableAssignmentContext) error {
    700 	// The Soong replacement for CFLAGS doesn't need the same extra escaped quotes that were present in Make
    701 	ctx.mkvalue = ctx.mkvalue.Clone()
    702 	ctx.mkvalue.ReplaceLiteral(`\"`, `"`)
    703 	return includeVariableNow(bpVariable{"cflags", bpparser.ListType}, ctx)
    704 }
    705 
    706 func proguardEnabled(ctx variableAssignmentContext) error {
    707 	val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.ListType)
    708 	if err != nil {
    709 		return err
    710 	}
    711 
    712 	list, ok := val.(*bpparser.List)
    713 	if !ok {
    714 		return fmt.Errorf("unsupported proguard expression")
    715 	}
    716 
    717 	set := func(prop string, value bool) {
    718 		bpValue := &bpparser.Bool{
    719 			Value: value,
    720 		}
    721 		setVariable(ctx.file, false, ctx.prefix, prop, bpValue, true)
    722 	}
    723 
    724 	enable := false
    725 
    726 	for _, v := range list.Values {
    727 		s, ok := v.(*bpparser.String)
    728 		if !ok {
    729 			return fmt.Errorf("unsupported proguard expression")
    730 		}
    731 
    732 		switch s.Value {
    733 		case "disabled":
    734 			set("optimize.enabled", false)
    735 		case "obfuscation":
    736 			enable = true
    737 			set("optimize.obfuscate", true)
    738 		case "optimization":
    739 			enable = true
    740 			set("optimize.optimize", true)
    741 		case "full":
    742 			enable = true
    743 		case "custom":
    744 			set("optimize.no_aapt_flags", true)
    745 			enable = true
    746 		default:
    747 			return fmt.Errorf("unsupported proguard value %q", s)
    748 		}
    749 	}
    750 
    751 	if enable {
    752 		// This is only necessary for libraries which default to false, but we can't
    753 		// tell the difference between a library and an app here.
    754 		set("optimize.enabled", true)
    755 	}
    756 
    757 	return nil
    758 }
    759 
    760 func invert(name string) func(ctx variableAssignmentContext) error {
    761 	return func(ctx variableAssignmentContext) error {
    762 		val, err := makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpparser.BoolType)
    763 		if err != nil {
    764 			return err
    765 		}
    766 
    767 		val.(*bpparser.Bool).Value = !val.(*bpparser.Bool).Value
    768 
    769 		return setVariable(ctx.file, ctx.append, ctx.prefix, name, val, true)
    770 	}
    771 }
    772 
    773 // given a conditional, returns a function that will insert a variable assignment or not, based on the conditional
    774 func includeVariableIf(bpVar bpVariable, conditional func(ctx variableAssignmentContext) bool) func(ctx variableAssignmentContext) error {
    775 	return func(ctx variableAssignmentContext) error {
    776 		var err error
    777 		if conditional(ctx) {
    778 			err = includeVariableNow(bpVar, ctx)
    779 		}
    780 		return err
    781 	}
    782 }
    783 
    784 // given a variable, returns a function that will always insert a variable assignment
    785 func includeVariable(bpVar bpVariable) func(ctx variableAssignmentContext) error {
    786 	return includeVariableIf(bpVar, always)
    787 }
    788 
    789 func includeVariableNow(bpVar bpVariable, ctx variableAssignmentContext) error {
    790 	var val bpparser.Expression
    791 	var err error
    792 	val, err = makeVariableToBlueprint(ctx.file, ctx.mkvalue, bpVar.variableType)
    793 	if err == nil {
    794 		err = setVariable(ctx.file, ctx.append, ctx.prefix, bpVar.name, val, true)
    795 	}
    796 	return err
    797 }
    798 
    799 // given a function that returns a bool, returns a function that returns the opposite
    800 func not(conditional func(ctx variableAssignmentContext) bool) func(ctx variableAssignmentContext) bool {
    801 	return func(ctx variableAssignmentContext) bool {
    802 		return !conditional(ctx)
    803 	}
    804 }
    805 
    806 // returns a function that tells whether mkvalue.Dump equals the given query string
    807 func valueDumpEquals(textToMatch string) func(ctx variableAssignmentContext) bool {
    808 	return func(ctx variableAssignmentContext) bool {
    809 		return (ctx.mkvalue.Dump() == textToMatch)
    810 	}
    811 }
    812 
    813 func always(ctx variableAssignmentContext) bool {
    814 	return true
    815 }
    816 
    817 func skip(ctx variableAssignmentContext) error {
    818 	return nil
    819 }
    820 
    821 // Shorter suffixes of other suffixes must be at the end of the list
    822 var propertyPrefixes = []struct{ mk, bp string }{
    823 	{"arm", "arch.arm"},
    824 	{"arm64", "arch.arm64"},
    825 	{"mips", "arch.mips"},
    826 	{"mips64", "arch.mips64"},
    827 	{"x86", "arch.x86"},
    828 	{"x86_64", "arch.x86_64"},
    829 	{"32", "multilib.lib32"},
    830 	// 64 must be after x86_64
    831 	{"64", "multilib.lib64"},
    832 	{"darwin", "target.darwin"},
    833 	{"linux", "target.linux_glibc"},
    834 	{"windows", "target.windows"},
    835 }
    836 
    837 var conditionalTranslations = map[string]map[bool]string{
    838 	"($(HOST_OS),darwin)": {
    839 		true:  "target.darwin",
    840 		false: "target.not_darwin"},
    841 	"($(HOST_OS), darwin)": {
    842 		true:  "target.darwin",
    843 		false: "target.not_darwin"},
    844 	"($(HOST_OS),windows)": {
    845 		true:  "target.windows",
    846 		false: "target.not_windows"},
    847 	"($(HOST_OS), windows)": {
    848 		true:  "target.windows",
    849 		false: "target.not_windows"},
    850 	"($(HOST_OS),linux)": {
    851 		true:  "target.linux_glibc",
    852 		false: "target.not_linux_glibc"},
    853 	"($(HOST_OS), linux)": {
    854 		true:  "target.linux_glibc",
    855 		false: "target.not_linux_glibc"},
    856 	"($(BUILD_OS),darwin)": {
    857 		true:  "target.darwin",
    858 		false: "target.not_darwin"},
    859 	"($(BUILD_OS), darwin)": {
    860 		true:  "target.darwin",
    861 		false: "target.not_darwin"},
    862 	"($(BUILD_OS),linux)": {
    863 		true:  "target.linux_glibc",
    864 		false: "target.not_linux_glibc"},
    865 	"($(BUILD_OS), linux)": {
    866 		true:  "target.linux_glibc",
    867 		false: "target.not_linux_glibc"},
    868 	"(,$(TARGET_BUILD_APPS))": {
    869 		false: "product_variables.unbundled_build"},
    870 	"($(TARGET_BUILD_APPS),)": {
    871 		false: "product_variables.unbundled_build"},
    872 	"($(TARGET_BUILD_PDK),true)": {
    873 		true: "product_variables.pdk"},
    874 	"($(TARGET_BUILD_PDK), true)": {
    875 		true: "product_variables.pdk"},
    876 }
    877 
    878 func mydir(args []string) []string {
    879 	return []string{"."}
    880 }
    881 
    882 func allFilesUnder(wildcard string) func(args []string) []string {
    883 	return func(args []string) []string {
    884 		dirs := []string{""}
    885 		if len(args) > 0 {
    886 			dirs = strings.Fields(args[0])
    887 		}
    888 
    889 		paths := make([]string, len(dirs))
    890 		for i := range paths {
    891 			paths[i] = fmt.Sprintf("%s/**/"+wildcard, dirs[i])
    892 		}
    893 		return paths
    894 	}
    895 }
    896 
    897 func allSubdirJavaFiles(args []string) []string {
    898 	return []string{"**/*.java"}
    899 }
    900 
    901 func includeIgnored(args []string) []string {
    902 	return []string{include_ignored}
    903 }
    904 
    905 var moduleTypes = map[string]string{
    906 	"BUILD_SHARED_LIBRARY":        "cc_library_shared",
    907 	"BUILD_STATIC_LIBRARY":        "cc_library_static",
    908 	"BUILD_HOST_SHARED_LIBRARY":   "cc_library_host_shared",
    909 	"BUILD_HOST_STATIC_LIBRARY":   "cc_library_host_static",
    910 	"BUILD_HEADER_LIBRARY":        "cc_library_headers",
    911 	"BUILD_EXECUTABLE":            "cc_binary",
    912 	"BUILD_HOST_EXECUTABLE":       "cc_binary_host",
    913 	"BUILD_NATIVE_TEST":           "cc_test",
    914 	"BUILD_HOST_NATIVE_TEST":      "cc_test_host",
    915 	"BUILD_NATIVE_BENCHMARK":      "cc_benchmark",
    916 	"BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
    917 
    918 	"BUILD_JAVA_LIBRARY":             "java_library_installable", // will be rewritten to java_library by bpfix
    919 	"BUILD_STATIC_JAVA_LIBRARY":      "java_library",
    920 	"BUILD_HOST_JAVA_LIBRARY":        "java_library_host",
    921 	"BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
    922 	"BUILD_PACKAGE":                  "android_app",
    923 
    924 	"BUILD_CTS_EXECUTABLE":          "cc_binary",               // will be further massaged by bpfix depending on the output path
    925 	"BUILD_CTS_SUPPORT_PACKAGE":     "cts_support_package",     // will be rewritten to android_test by bpfix
    926 	"BUILD_CTS_PACKAGE":             "cts_package",             // will be rewritten to android_test by bpfix
    927 	"BUILD_CTS_TARGET_JAVA_LIBRARY": "cts_target_java_library", // will be rewritten to java_library by bpfix
    928 	"BUILD_CTS_HOST_JAVA_LIBRARY":   "cts_host_java_library",   // will be rewritten to java_library_host by bpfix
    929 }
    930 
    931 var prebuiltTypes = map[string]string{
    932 	"SHARED_LIBRARIES": "cc_prebuilt_library_shared",
    933 	"STATIC_LIBRARIES": "cc_prebuilt_library_static",
    934 	"EXECUTABLES":      "cc_prebuilt_binary",
    935 	"JAVA_LIBRARIES":   "java_import",
    936 	"ETC":              "prebuilt_etc",
    937 }
    938 
    939 var soongModuleTypes = map[string]bool{}
    940 
    941 var includePathToModule = map[string]string{
    942 	"test/vts/tools/build/Android.host_config.mk": "vts_config",
    943 	// The rest will be populated dynamically in androidScope below
    944 }
    945 
    946 func mapIncludePath(path string) (string, bool) {
    947 	if path == clear_vars || path == include_ignored {
    948 		return path, true
    949 	}
    950 	module, ok := includePathToModule[path]
    951 	return module, ok
    952 }
    953 
    954 func androidScope() mkparser.Scope {
    955 	globalScope := mkparser.NewScope(nil)
    956 	globalScope.Set("CLEAR_VARS", clear_vars)
    957 	globalScope.SetFunc("my-dir", mydir)
    958 	globalScope.SetFunc("all-java-files-under", allFilesUnder("*.java"))
    959 	globalScope.SetFunc("all-proto-files-under", allFilesUnder("*.proto"))
    960 	globalScope.SetFunc("all-aidl-files-under", allFilesUnder("*.aidl"))
    961 	globalScope.SetFunc("all-Iaidl-files-under", allFilesUnder("I*.aidl"))
    962 	globalScope.SetFunc("all-logtags-files-under", allFilesUnder("*.logtags"))
    963 	globalScope.SetFunc("all-subdir-java-files", allSubdirJavaFiles)
    964 	globalScope.SetFunc("all-makefiles-under", includeIgnored)
    965 	globalScope.SetFunc("first-makefiles-under", includeIgnored)
    966 	globalScope.SetFunc("all-named-subdir-makefiles", includeIgnored)
    967 	globalScope.SetFunc("all-subdir-makefiles", includeIgnored)
    968 
    969 	// The scope maps each known variable to a path, and then includePathToModule maps a path
    970 	// to a module. We don't care what the actual path value is so long as the value in scope
    971 	// is mapped, so we might as well use variable name as key, too.
    972 	for varName, moduleName := range moduleTypes {
    973 		path := varName
    974 		globalScope.Set(varName, path)
    975 		includePathToModule[path] = moduleName
    976 	}
    977 	for varName, moduleName := range prebuiltTypes {
    978 		includePathToModule[varName] = moduleName
    979 	}
    980 
    981 	return globalScope
    982 }
    983