Merge changes I4c373b29,I9ccda6fc,I1b390b0e,I30151217
* changes:
Do not propagate strict updatability linting to libcore/
Create allowlist to skip strict updatability lint check
Propagate strict_updatability_linting to transitive deps of updatable apexes
Export LintDepSetsIntf from java package
diff --git a/Android.bp b/Android.bp
index 7c50047..380a388 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,6 +50,7 @@
name: "device_kernel_headers",
vendor: true,
recovery_available: true,
+ min_sdk_version: "apex_inherit",
}
cc_genrule {
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..caa2a95
--- /dev/null
+++ b/METADATA
@@ -0,0 +1 @@
+name: "Android"
diff --git a/OWNERS b/OWNERS
index 937a243..0cfb241 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,6 +6,7 @@
alexmarquez@google.com
asmundak@google.com
ccross@android.com
+colefaust@google.com
cparsons@google.com
delmerico@google.com
dwillemsen@google.com
diff --git a/README.md b/README.md
index 18c6604..caffd3d 100644
--- a/README.md
+++ b/README.md
@@ -550,6 +550,26 @@
and produces build rules. The build rules are collected by blueprint and
written to a [ninja](http://ninja-build.org) build file.
+## Environment Variables Config File
+
+Soong can optionally load environment variables from a pre-specified
+configuration file during startup. These environment variables can be used
+to control the behavior of the build. For example, these variables can determine
+whether remote-execution should be used for the build or not.
+
+The `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment variable specifies the
+directory in which the config file should be searched for. The
+`ANDROID_BUILD_ENVIRONMENT_CONFIG` variable determines the name of the config
+file to be searched for within the config directory. For example, the following
+build comand will load `ENV_VAR_1` and `ENV_VAR_2` environment variables from
+the `example_config.json` file inside the `build/soong` directory.
+
+```
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong \
+ ANDROID_BUILD_ENVIRONMENT_CONFIG=example_config \
+ build/soong/soong_ui.bash
+```
+
## Other documentation
* [Best Practices](docs/best_practices.md)
diff --git a/android/Android.bp b/android/Android.bp
index da36959..d3540b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -16,7 +16,9 @@
"soong-remoteexec",
"soong-response",
"soong-shared",
+ "soong-starlark-format",
"soong-ui-metrics_proto",
+
"golang-protobuf-proto",
"golang-protobuf-encoding-prototext",
diff --git a/android/api_levels.go b/android/api_levels.go
index 926d297..27a3b7f 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -18,6 +18,8 @@
"encoding/json"
"fmt"
"strconv"
+
+ "android/soong/starlark_fmt"
)
func init() {
@@ -321,6 +323,7 @@
"Q": 29,
"R": 30,
"S": 31,
+ "S-V2": 32,
}
// TODO: Differentiate "current" and "future".
@@ -364,6 +367,7 @@
"Q": 29,
"R": 30,
"S": 31,
+ "S-V2": 32,
}
for i, codename := range config.PlatformVersionActiveCodenames() {
apiLevelsMap[codename] = previewAPILevelBase + i
@@ -378,3 +382,21 @@
apiLevelsJson := GetApiLevelsJson(ctx)
createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
}
+
+func printApiLevelsStarlarkDict(config Config) string {
+ apiLevelsMap := GetApiLevelsMap(config)
+ valDict := make(map[string]string, len(apiLevelsMap))
+ for k, v := range apiLevelsMap {
+ valDict[k] = strconv.Itoa(v)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func StarlarkApiLevelConfigs(config Config) string {
+ return fmt.Sprintf(`# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+_api_levels = %s
+
+api_levels = _api_levels
+`, printApiLevelsStarlarkDict(config),
+ )
+}
\ No newline at end of file
diff --git a/android/arch.go b/android/arch.go
index 96a4cbf..6b81022 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -22,6 +22,7 @@
"strings"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
@@ -829,7 +830,7 @@
const maxArchTypeNameSize = 500
// Convert the type to a new set of types that contains only the arch-specific properties
- // (those that are tagged with `android:"arch_specific"`), and sharded into multiple types
+ // (those that are tagged with `android:"arch_variant"`), and sharded into multiple types
// to keep the runtime-generated names under the limit.
propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
@@ -864,6 +865,10 @@
archVariant := variantReplacer.Replace(archVariant)
variants = append(variants, proptools.FieldNameForProperty(archVariant))
}
+ for _, cpuVariant := range cpuVariants[arch] {
+ cpuVariant := variantReplacer.Replace(cpuVariant)
+ variants = append(variants, proptools.FieldNameForProperty(cpuVariant))
+ }
for _, feature := range archFeatures[arch] {
feature := variantReplacer.Replace(feature)
variants = append(variants, proptools.FieldNameForProperty(feature))
@@ -904,6 +909,7 @@
"Glibc",
"Musl",
"Linux",
+ "Host_linux",
"Not_windows",
"Arm_on_x86",
"Arm_on_x86_64",
@@ -917,19 +923,38 @@
for _, archType := range osArchTypeMap[os] {
targets = append(targets, GetCompoundTargetField(os, archType))
- // Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
+ // Also add the special "linux_<arch>", "bionic_<arch>" , "glibc_<arch>", and
+ // "musl_<arch>" property structs.
if os.Linux() {
target := "Linux_" + archType.Name
if !InList(target, targets) {
targets = append(targets, target)
}
}
+ if os.Linux() && os.Class == Host {
+ target := "Host_linux_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
if os.Bionic() {
target := "Bionic_" + archType.Name
if !InList(target, targets) {
targets = append(targets, target)
}
}
+ if os == Linux {
+ target := "Glibc_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
+ if os == LinuxMusl {
+ target := "Musl_" + archType.Name
+ if !InList(target, targets) {
+ targets = append(targets, target)
+ }
+ }
}
}
@@ -1144,6 +1169,14 @@
}
}
+ if os.Linux() && os.Class == Host {
+ field := "Host_linux"
+ prefix := "target.host_linux"
+ if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+ mergePropertyStruct(ctx, genProps, linuxProperties)
+ }
+ }
+
if os.Bionic() {
field := "Bionic"
prefix := "target.bionic"
@@ -1379,11 +1412,25 @@
result = append(result, osArchProperties)
}
+ if os == Linux {
+ field := "Glibc_" + archType.Name
+ userFriendlyField := "target.glibc_" + "_" + archType.Name
+ if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+ result = append(result, osArchProperties)
+ }
+ }
+
if os == LinuxMusl {
+ field := "Musl_" + archType.Name
+ userFriendlyField := "target.musl_" + "_" + archType.Name
+ if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+ result = append(result, osArchProperties)
+ }
+
// Special case: to ease the transition from glibc to musl, apply linux_glibc
// properties (which has historically mean host linux) to musl variants.
- field := "Linux_glibc_" + archType.Name
- userFriendlyField := "target.linux_glibc_" + archType.Name
+ field = "Linux_glibc_" + archType.Name
+ userFriendlyField = "target.linux_glibc_" + archType.Name
if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
result = append(result, osArchProperties)
}
@@ -1487,23 +1534,32 @@
targets := make(map[OsType][]Target)
var targetErr error
- addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
- nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
- nativeBridgeRelativePath *string) {
+ type targetConfig struct {
+ os OsType
+ archName string
+ archVariant *string
+ cpuVariant *string
+ abi []string
+ nativeBridgeEnabled NativeBridgeSupport
+ nativeBridgeHostArchName *string
+ nativeBridgeRelativePath *string
+ }
+
+ addTarget := func(target targetConfig) {
if targetErr != nil {
return
}
- arch, err := decodeArch(os, archName, archVariant, cpuVariant, abi)
+ arch, err := decodeArch(target.os, target.archName, target.archVariant, target.cpuVariant, target.abi)
if err != nil {
targetErr = err
return
}
- nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
- nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+ nativeBridgeRelativePathStr := String(target.nativeBridgeRelativePath)
+ nativeBridgeHostArchNameStr := String(target.nativeBridgeHostArchName)
// Use guest arch as relative install path by default
- if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+ if target.nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
nativeBridgeRelativePathStr = arch.ArchType.String()
}
@@ -1511,11 +1567,11 @@
// the currently configured build machine (either because the OS is different or because of
// the unsupported arch)
hostCross := false
- if os.Class == Host {
+ if target.os.Class == Host {
var osSupported bool
- if os == config.BuildOS {
+ if target.os == config.BuildOS {
osSupported = true
- } else if config.BuildOS.Linux() && os.Linux() {
+ } else if config.BuildOS.Linux() && target.os.Linux() {
// LinuxBionic and Linux are compatible
osSupported = true
} else {
@@ -1537,11 +1593,11 @@
}
}
- targets[os] = append(targets[os],
+ targets[target.os] = append(targets[target.os],
Target{
- Os: os,
+ Os: target.os,
Arch: arch,
- NativeBridge: nativeBridgeEnabled,
+ NativeBridge: target.nativeBridgeEnabled,
NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
NativeBridgeRelativePath: nativeBridgeRelativePathStr,
HostCross: hostCross,
@@ -1553,11 +1609,11 @@
}
// The primary host target, which must always exist.
- addTarget(config.BuildOS, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostArch, nativeBridgeEnabled: NativeBridgeDisabled})
// An optional secondary host target.
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
- addTarget(config.BuildOS, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
}
// Optional cross-compiled host targets, generally Windows.
@@ -1572,45 +1628,65 @@
}
// The primary cross-compiled host target.
- addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostArch, nativeBridgeEnabled: NativeBridgeDisabled})
// An optional secondary cross-compiled host target.
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
- addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
}
}
// Optional device targets
if variables.DeviceArch != nil && *variables.DeviceArch != "" {
// The primary device target.
- addTarget(Android, *variables.DeviceArch, variables.DeviceArchVariant,
- variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.DeviceArch,
+ archVariant: variables.DeviceArchVariant,
+ cpuVariant: variables.DeviceCpuVariant,
+ abi: variables.DeviceAbi,
+ nativeBridgeEnabled: NativeBridgeDisabled,
+ })
// An optional secondary device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
- addTarget(Android, *variables.DeviceSecondaryArch,
- variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
- variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.DeviceSecondaryArch,
+ archVariant: variables.DeviceSecondaryArchVariant,
+ cpuVariant: variables.DeviceSecondaryCpuVariant,
+ abi: variables.DeviceSecondaryAbi,
+ nativeBridgeEnabled: NativeBridgeDisabled,
+ })
}
// An optional NativeBridge device target.
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
- addTarget(Android, *variables.NativeBridgeArch,
- variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
- variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
- variables.NativeBridgeRelativePath)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.NativeBridgeArch,
+ archVariant: variables.NativeBridgeArchVariant,
+ cpuVariant: variables.NativeBridgeCpuVariant,
+ abi: variables.NativeBridgeAbi,
+ nativeBridgeEnabled: NativeBridgeEnabled,
+ nativeBridgeHostArchName: variables.DeviceArch,
+ nativeBridgeRelativePath: variables.NativeBridgeRelativePath,
+ })
}
// An optional secondary NativeBridge device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
variables.NativeBridgeSecondaryArch != nil && *variables.NativeBridgeSecondaryArch != "" {
- addTarget(Android, *variables.NativeBridgeSecondaryArch,
- variables.NativeBridgeSecondaryArchVariant,
- variables.NativeBridgeSecondaryCpuVariant,
- variables.NativeBridgeSecondaryAbi,
- NativeBridgeEnabled,
- variables.DeviceSecondaryArch,
- variables.NativeBridgeSecondaryRelativePath)
+ addTarget(targetConfig{
+ os: Android,
+ archName: *variables.NativeBridgeSecondaryArch,
+ archVariant: variables.NativeBridgeSecondaryArchVariant,
+ cpuVariant: variables.NativeBridgeSecondaryCpuVariant,
+ abi: variables.NativeBridgeSecondaryAbi,
+ nativeBridgeEnabled: NativeBridgeEnabled,
+ nativeBridgeHostArchName: variables.DeviceSecondaryArch,
+ nativeBridgeRelativePath: variables.NativeBridgeSecondaryRelativePath,
+ })
}
}
@@ -1670,11 +1746,11 @@
}
// decodeArchSettings converts a list of archConfigs into a list of Targets for the given OsType.
-func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
+func decodeAndroidArchSettings(archConfigs []archConfig) ([]Target, error) {
var ret []Target
for _, config := range archConfigs {
- arch, err := decodeArch(os, config.arch, &config.archVariant,
+ arch, err := decodeArch(Android, config.arch, &config.archVariant,
&config.cpuVariant, config.abi)
if err != nil {
return nil, err
@@ -1714,6 +1790,18 @@
a.CpuVariant = ""
}
+ if a.ArchVariant != "" {
+ if validArchVariants := archVariants[archType]; !InList(a.ArchVariant, validArchVariants) {
+ return Arch{}, fmt.Errorf("[%q] unknown arch variant %q, support variants: %q", archType, a.ArchVariant, validArchVariants)
+ }
+ }
+
+ if a.CpuVariant != "" {
+ if validCpuVariants := cpuVariants[archType]; !InList(a.CpuVariant, validCpuVariants) {
+ return Arch{}, fmt.Errorf("[%q] unknown cpu variant %q, support variants: %q", archType, a.CpuVariant, validCpuVariants)
+ }
+ }
+
// Filter empty ABIs out of the list.
for i := 0; i < len(a.Abi); i++ {
if a.Abi[i] == "" {
@@ -1722,14 +1810,9 @@
}
}
- if a.ArchVariant == "" {
- // Set ArchFeatures from the default arch features.
- if featureMap, ok := defaultArchFeatureMap[os]; ok {
- a.ArchFeatures = featureMap[archType]
- }
- } else {
- // Set ArchFeatures from the arch type.
- if featureMap, ok := archFeatureMap[archType]; ok {
+ // Set ArchFeatures from the arch type. for Android OS, other os-es do not specify features
+ if os == Android {
+ if featureMap, ok := androidArchFeatureMap[archType]; ok {
a.ArchFeatures = featureMap[a.ArchVariant]
}
}
@@ -2059,6 +2142,7 @@
linuxStructs := getTargetStructs(ctx, archProperties, "Linux")
bionicStructs := getTargetStructs(ctx, archProperties, "Bionic")
hostStructs := getTargetStructs(ctx, archProperties, "Host")
+ hostLinuxStructs := getTargetStructs(ctx, archProperties, "Host_linux")
hostNotWindowsStructs := getTargetStructs(ctx, archProperties, "Not_windows")
// For android, linux, ...
@@ -2079,6 +2163,9 @@
if os.Bionic() {
osStructs = append(osStructs, bionicStructs...)
}
+ if os.Linux() && os.Class == Host {
+ osStructs = append(osStructs, hostLinuxStructs...)
+ }
if os == LinuxMusl {
osStructs = append(osStructs, getTargetStructs(ctx, archProperties, "Musl")...)
@@ -2111,6 +2198,16 @@
targetStructs := getTargetStructs(ctx, archProperties, targetField)
osArchStructs = append(osArchStructs, targetStructs...)
}
+ if os == LinuxMusl {
+ targetField := "Musl_" + arch.Name
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
+ }
+ if os == Linux {
+ targetField := "Glibc_" + arch.Name
+ targetStructs := getTargetStructs(ctx, archProperties, targetField)
+ osArchStructs = append(osArchStructs, targetStructs...)
+ }
targetField := GetCompoundTargetField(os, arch)
targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
@@ -2167,3 +2264,40 @@
return value
}
+
+func printArchTypeStarlarkDict(dict map[ArchType][]string) string {
+ valDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ valDict[k.String()] = starlark_fmt.PrintStringList(v, 1)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func printArchTypeNestedStarlarkDict(dict map[ArchType]map[string][]string) string {
+ valDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ valDict[k.String()] = starlark_fmt.PrintStringListDict(v, 1)
+ }
+ return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func StarlarkArchConfigurations() string {
+ return fmt.Sprintf(`
+_arch_to_variants = %s
+
+_arch_to_cpu_variants = %s
+
+_arch_to_features = %s
+
+_android_arch_feature_for_arch_variant = %s
+
+arch_to_variants = _arch_to_variants
+arch_to_cpu_variants = _arch_to_cpu_variants
+arch_to_features = _arch_to_features
+android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
+`, printArchTypeStarlarkDict(archVariants),
+ printArchTypeStarlarkDict(cpuVariants),
+ printArchTypeStarlarkDict(archFeatures),
+ printArchTypeNestedStarlarkDict(androidArchFeatureMap),
+ )
+}
diff --git a/android/arch_list.go b/android/arch_list.go
index d68a0d1..cbf8e7a 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -14,46 +14,18 @@
package android
-import "fmt"
-
var archVariants = map[ArchType][]string{
Arm: {
"armv7-a",
"armv7-a-neon",
"armv8-a",
"armv8-2a",
- "cortex-a7",
- "cortex-a8",
- "cortex-a9",
- "cortex-a15",
- "cortex-a53",
- "cortex-a53-a57",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "krait",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
},
Arm64: {
- "armv8_a",
- "armv8_a_branchprot",
- "armv8_2a",
+ "armv8-a",
+ "armv8-a-branchprot",
+ "armv8-2a",
"armv8-2a-dotprod",
- "cortex-a53",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
},
X86: {
"amberlake",
@@ -87,6 +59,41 @@
},
}
+var cpuVariants = map[ArchType][]string{
+ Arm: {
+ "cortex-a7",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a15",
+ "cortex-a53",
+ "cortex-a53.a57",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "krait",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Arm64: {
+ "cortex-a53",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ X86: {},
+ X86_64: {},
+}
+
var archFeatures = map[ArchType][]string{
Arm: {
"neon",
@@ -119,7 +126,7 @@
},
}
-var archFeatureMap = map[ArchType]map[string][]string{
+var androidArchFeatureMap = map[ArchType]map[string][]string{
Arm: {
"armv7-a-neon": {
"neon",
@@ -270,6 +277,13 @@
},
},
X86_64: {
+ "" /*default */ : {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
"amberlake": {
"ssse3",
"sse4",
@@ -389,23 +403,3 @@
},
},
}
-
-var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
-
-// RegisterDefaultArchVariantFeatures is called by files that define Toolchains to specify the
-// arch features that are available for the default arch variant. It must be called from an
-// init() function.
-func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
- checkCalledFromInit()
-
- for _, feature := range features {
- if !InList(feature, archFeatures[arch]) {
- panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
- }
- }
-
- if defaultArchFeatureMap[os] == nil {
- defaultArchFeatureMap[os] = make(map[ArchType][]string)
- }
- defaultArchFeatureMap[os][arch] = features
-}
diff --git a/android/arch_test.go b/android/arch_test.go
index a828321..68dc7f5 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -491,11 +491,9 @@
arch: {
arm: {
a: ["arm"],
- armv7_a_neon: { a: ["armv7_a_neon"] },
},
arm64: {
a: ["arm64"],
- armv8_a: { a: ["armv8_a"] },
},
x86: { a: ["x86"] },
x86_64: { a: ["x86_64"] },
@@ -512,6 +510,7 @@
musl: { a: ["musl"] },
linux_bionic: { a: ["linux_bionic"] },
linux: { a: ["linux"] },
+ host_linux: { a: ["host_linux"] },
linux_glibc: { a: ["linux_glibc"] },
linux_musl: { a: ["linux_musl"] },
windows: { a: ["windows"], enabled: true },
@@ -552,12 +551,12 @@
{
module: "foo",
variant: "android_arm64_armv8-a",
- property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "armv8_a", "lib64", "android_arm64"},
+ property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "lib64", "android_arm64"},
},
{
module: "foo",
variant: "android_arm_armv7-a-neon",
- property: []string{"root", "linux", "bionic", "android", "android64", "arm", "armv7_a_neon", "lib32", "android_arm"},
+ property: []string{"root", "linux", "bionic", "android", "android64", "arm", "lib32", "android_arm"},
},
},
},
@@ -568,12 +567,12 @@
{
module: "foo",
variant: "linux_glibc_x86_64",
- property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
+ property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
},
{
module: "foo",
variant: "linux_glibc_x86",
- property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
+ property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
},
},
},
@@ -607,12 +606,12 @@
{
module: "foo",
variant: "linux_musl_x86_64",
- property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64", "linux_glibc_x86_64"},
+ property: []string{"root", "host", "linux", "host_linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64", "linux_glibc_x86_64"},
},
{
module: "foo",
variant: "linux_musl_x86",
- property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86", "linux_glibc_x86"},
+ property: []string{"root", "host", "linux", "host_linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86", "linux_glibc_x86"},
},
},
},
diff --git a/android/bazel.go b/android/bazel.go
index 3046edb..7e2727c 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -198,6 +198,7 @@
// build/bazel is not recursive. Instead list each subdirectory under
// build/bazel explicitly.
"build/bazel":/* recursive = */ false,
+ "build/bazel/ci/dist":/* recursive = */ false,
"build/bazel/examples/android_app":/* recursive = */ true,
"build/bazel/examples/java":/* recursive = */ true,
"build/bazel/bazel_skylib":/* recursive = */ true,
@@ -207,6 +208,7 @@
"build/bazel/tests":/* recursive = */ true,
"build/bazel/platforms":/* recursive = */ true,
"build/bazel/product_variables":/* recursive = */ true,
+ "build/bazel/vendor/google":/* recursive = */ true,
"build/bazel_common_rules":/* recursive = */ true,
// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
"build/make/tools":/* recursive = */ false,
@@ -224,8 +226,10 @@
"packages/apps/QuickSearchBox":/* recursive = */ true,
"packages/apps/WallpaperPicker":/* recursive = */ false,
+ "prebuilts/bundletool":/* recursive = */ true,
"prebuilts/gcc":/* recursive = */ true,
"prebuilts/build-tools":/* recursive = */ false,
+ "prebuilts/jdk/jdk11":/* recursive = */ false,
"prebuilts/sdk":/* recursive = */ false,
"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
"prebuilts/sdk/current/support":/* recursive = */ false,
@@ -244,6 +248,7 @@
"build/bazel/examples/soong_config_variables": Bp2BuildDefaultTrueRecursively,
"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
"build/make/tools/signapk": Bp2BuildDefaultTrue,
+ "build/make/target/product/security": Bp2BuildDefaultTrue,
"build/soong": Bp2BuildDefaultTrue,
"build/soong/cc/libbuildversion": Bp2BuildDefaultTrue, // Skip tests subdir
"build/soong/cc/ndkstubgen": Bp2BuildDefaultTrue,
@@ -288,10 +293,14 @@
"development/samples/WiFiDirectDemo": Bp2BuildDefaultTrue,
"development/sdk": Bp2BuildDefaultTrueRecursively,
"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+ "external/auto/android-annotation-stubs": Bp2BuildDefaultTrueRecursively,
+ "external/auto/common": Bp2BuildDefaultTrueRecursively,
+ "external/auto/service": Bp2BuildDefaultTrueRecursively,
"external/boringssl": Bp2BuildDefaultTrueRecursively,
"external/bouncycastle": Bp2BuildDefaultTrue,
"external/brotli": Bp2BuildDefaultTrue,
"external/conscrypt": Bp2BuildDefaultTrue,
+ "external/e2fsprogs": Bp2BuildDefaultTrueRecursively,
"external/error_prone": Bp2BuildDefaultTrueRecursively,
"external/fmtlib": Bp2BuildDefaultTrueRecursively,
"external/google-benchmark": Bp2BuildDefaultTrueRecursively,
@@ -300,6 +309,7 @@
"external/icu": Bp2BuildDefaultTrueRecursively,
"external/icu/android_icu4j": Bp2BuildDefaultFalse, // java rules incomplete
"external/icu/icu4j": Bp2BuildDefaultFalse, // java rules incomplete
+ "external/javapoet": Bp2BuildDefaultTrueRecursively,
"external/jemalloc_new": Bp2BuildDefaultTrueRecursively,
"external/jsoncpp": Bp2BuildDefaultTrueRecursively,
"external/libcap": Bp2BuildDefaultTrueRecursively,
@@ -321,6 +331,7 @@
"external/zstd": Bp2BuildDefaultTrueRecursively,
"frameworks/base/media/tests/MediaDump": Bp2BuildDefaultTrue,
"frameworks/base/startop/apps/test": Bp2BuildDefaultTrue,
+ "frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
"frameworks/native/libs/adbd_auth": Bp2BuildDefaultTrueRecursively,
"frameworks/native/opengl/tests/gl2_cameraeye": Bp2BuildDefaultTrue,
"frameworks/native/opengl/tests/gl2_java": Bp2BuildDefaultTrue,
@@ -375,6 +386,43 @@
"tools/platform-compat/java/android/compat": Bp2BuildDefaultTrueRecursively,
}
+ // Per-module allowlist to always opt modules in of both bp2build and mixed builds.
+ // These modules are usually in directories with many other modules that are not ready for
+ // conversion.
+ //
+ // A module can either be in this list or its directory allowlisted entirely
+ // in bp2buildDefaultConfig, but not both at the same time.
+ bp2buildModuleAlwaysConvertList = []string{
+ "prebuilt_junit-params-assertj-core",
+
+ //external/avb
+ "avbtool",
+ "libavb",
+ "avb_headers",
+
+ //external/fec
+ "libfec_rs",
+
+ //system/core/libsparse
+ "libsparse",
+
+ //system/extras/ext4_utils
+ "libext4_utils",
+
+ //system/extras/libfec
+ "libfec",
+
+ //system/extras/squashfs_utils
+ "libsquashfs_utils",
+
+ //system/extras/verity/fec
+ "fec",
+ }
+
+ // Per-module-type allowlist to always opt modules in of both bp2build and mixed builds
+ // when they have the same type as one listed.
+ bp2buildModuleTypeAlwaysConvertList = []string{}
+
// Per-module denylist to always opt modules out of both bp2build and mixed builds.
bp2buildModuleDoNotConvertList = []string{
"libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
@@ -441,11 +489,12 @@
"conscrypt", // b/210751803, we don't handle path property for filegroups
"conscrypt-for-host", // b/210751803, we don't handle path property for filegroups
- "host-libprotobuf-java-lite", // b/217236083, java_library cannot have deps without srcs
- "host-libprotobuf-java-micro", // b/217236083, java_library cannot have deps without srcs
- "host-libprotobuf-java-nano", // b/217236083, java_library cannot have deps without srcs
- "error_prone_core", // b/217236083, java_library cannot have deps without srcs
- "bouncycastle-host", // b/217236083, java_library cannot have deps without srcs
+ "host-libprotobuf-java-lite", // b/217236083, java_library cannot have deps without srcs
+ "host-libprotobuf-java-micro", // b/217236083, java_library cannot have deps without srcs
+ "host-libprotobuf-java-nano", // b/217236083, java_library cannot have deps without srcs
+ "error_prone_core", // b/217236083, java_library cannot have deps without srcs
+ "bouncycastle-host", // b/217236083, java_library cannot have deps without srcs
+ "mockito-robolectric-prebuilt", // b/217236083, java_library cannot have deps without srcs
"apex_manifest_proto_java", // b/215230097, we don't handle .proto files in java_library srcs attribute
@@ -490,10 +539,11 @@
// go deps:
"apex-protos", // depends on soong_zip, a go binary
"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
- "host_bionic_linker_asm", // depends on extract_linker, a go binary.
- "host_bionic_linker_script", // depends on extract_linker, a go binary.
- "robolectric-sqlite4java-native", // depends on soong_zip, a go binary
- "robolectric_tzdata", // depends on soong_zip, a go binary
+ "host_bionic_linker_asm", // depends on extract_linker, a go binary.
+ "host_bionic_linker_script", // depends on extract_linker, a go binary.
+ "robolectric-sqlite4java-native", // depends on soong_zip, a go binary
+ "robolectric_tzdata", // depends on soong_zip, a go binary
+ "libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
"android_icu4j_srcgen_binary", // Bazel build error: deps not allowed without srcs; move to runtime_deps
"core-icu4j-for-host", // Bazel build error: deps not allowed without srcs; move to runtime_deps
@@ -512,6 +562,10 @@
"art-script", // depends on unconverted modules: dalvikvm, dex2oat
"dex2oat-script", // depends on unconverted modules: dex2oat
+
+ "error_prone_checkerframework_dataflow_nullaway", // TODO(b/219908977): "Error in fail: deps not allowed without srcs; move to runtime_deps?"
+
+ "libprotobuf-java-nano", // b/220869005, depends on non-public_current SDK
}
// Per-module denylist of cc_library modules to only generate the static
@@ -534,15 +588,50 @@
"libadb_pairing_connection",
"libadb_pairing_connection_static",
"libadb_pairing_server", "libadb_pairing_server_static",
+
+ // TODO(b/204811222) support suffix in cc_binary
+ "acvp_modulewrapper",
+ "android.hardware.media.c2@1.0-service-v4l2",
+ "app_process",
+ "bar_test",
+ "bench_cxa_atexit",
+ "bench_noop",
+ "bench_noop_nostl",
+ "bench_noop_static",
+ "boringssl_self_test",
+ "boringssl_self_test_vendor",
+ "bssl",
+ "cavp",
+ "crash_dump",
+ "crasher",
+ "libcxx_test_template",
+ "linker",
+ "memory_replay",
+ "native_bridge_guest_linker",
+ "native_bridge_stub_library_defaults",
+ "noop",
+ "simpleperf_ndk",
+ "toybox-static",
+ "zlib_bench",
}
// Used for quicker lookups
- bp2buildModuleDoNotConvert = map[string]bool{}
- bp2buildCcLibraryStaticOnly = map[string]bool{}
- mixedBuildsDisabled = map[string]bool{}
+ bp2buildModuleDoNotConvert = map[string]bool{}
+ bp2buildModuleAlwaysConvert = map[string]bool{}
+ bp2buildModuleTypeAlwaysConvert = map[string]bool{}
+ bp2buildCcLibraryStaticOnly = map[string]bool{}
+ mixedBuildsDisabled = map[string]bool{}
)
func init() {
+ for _, moduleName := range bp2buildModuleAlwaysConvertList {
+ bp2buildModuleAlwaysConvert[moduleName] = true
+ }
+
+ for _, moduleType := range bp2buildModuleTypeAlwaysConvertList {
+ bp2buildModuleTypeAlwaysConvert[moduleType] = true
+ }
+
for _, moduleName := range bp2buildModuleDoNotConvertList {
bp2buildModuleDoNotConvert[moduleName] = true
}
@@ -618,7 +707,20 @@
}
func (b *BazelModuleBase) shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
- if bp2buildModuleDoNotConvert[module.Name()] {
+ moduleName := module.Name()
+ moduleNameAllowed := bp2buildModuleAlwaysConvert[moduleName]
+ moduleTypeAllowed := bp2buildModuleTypeAlwaysConvert[ctx.OtherModuleType(module)]
+ allowlistConvert := moduleNameAllowed || moduleTypeAllowed
+ if moduleNameAllowed && moduleTypeAllowed {
+ ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in bp2buildModuleAlwaysConvert and also be" +
+ " in bp2buildModuleTypeAlwaysConvert")
+ }
+
+ if bp2buildModuleDoNotConvert[moduleName] {
+ if moduleNameAllowed {
+ ctx.(BaseModuleContext).ModuleErrorf("a module cannot be in bp2buildModuleDoNotConvert" +
+ " and also be in bp2buildModuleAlwaysConvert")
+ }
return false
}
@@ -626,18 +728,34 @@
return false
}
- packagePath := ctx.OtherModuleDir(module)
- config := ctx.Config().bp2buildPackageConfig
-
- // This is a tristate value: true, false, or unset.
propValue := b.bazelProperties.Bazel_module.Bp2build_available
+ packagePath := ctx.OtherModuleDir(module)
+ // Modules in unit tests which are enabled in the allowlist by type or name
+ // trigger this conditional because unit tests run under the "." package path
+ isTestModule := packagePath == "." && proptools.BoolDefault(propValue, false)
+ if allowlistConvert && !isTestModule && ShouldKeepExistingBuildFileForDir(packagePath) {
+ if moduleNameAllowed {
+ ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory listed in bp2buildKeepExistingBuildFile"+
+ " and also be in bp2buildModuleAlwaysConvert. Directory: '%s'", packagePath)
+ }
+ return false
+ }
+
+ config := ctx.Config().bp2buildPackageConfig
+ // This is a tristate value: true, false, or unset.
if bp2buildDefaultTrueRecursively(packagePath, config) {
+ if moduleNameAllowed {
+ ctx.(BaseModuleContext).ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
+ " or Bp2BuildDefaultTrueRecursively and also be in bp2buildModuleAlwaysConvert. Directory: '%s'",
+ packagePath)
+ }
+
// Allow modules to explicitly opt-out.
return proptools.BoolDefault(propValue, true)
}
// Allow modules to explicitly opt-in.
- return proptools.BoolDefault(propValue, false)
+ return proptools.BoolDefault(propValue, allowlistConvert)
}
// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0052551..d851a98 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -50,8 +50,8 @@
// Portion of cquery map key to describe target configuration.
type configKey struct {
- archType ArchType
- osType OsType
+ arch string
+ osType OsType
}
// Map key to describe bazel cquery requests.
@@ -73,7 +73,7 @@
}
type BazelContext interface {
- // The below methods involve queuing cquery requests to be later invoked
+ // The methods below involve queuing cquery requests to be later invoked
// by bazel. If any of these methods return (_, false), then the request
// has been queued to be run later.
@@ -561,7 +561,7 @@
return id_string + ">>" + %s(target)
`
- for requestType, _ := range requestTypeToCqueryIdEntries {
+ for requestType := range requestTypeToCqueryIdEntries {
labelMapName := requestType.Name() + "_Labels"
functionName := requestType.Name() + "_Fn"
labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
@@ -664,7 +664,12 @@
if err != nil {
return err
}
-
+ if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
+ err = os.MkdirAll(metricsDir, 0777)
+ if err != nil {
+ return err
+ }
+ }
err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
if err != nil {
return err
@@ -716,9 +721,9 @@
}
}
- for val, _ := range context.requests {
+ for val := range context.requests {
if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
- context.results[val] = string(cqueryResult)
+ context.results[val] = cqueryResult
} else {
return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
getCqueryId(val), cqueryOutput, cqueryErr)
@@ -857,7 +862,7 @@
}
func getConfigString(key cqueryKey) string {
- arch := key.configKey.archType.Name
+ arch := key.configKey.arch
if len(arch) == 0 || arch == "common" {
// Use host platform, which is currently hardcoded to be x86_64.
arch = "x86_64"
@@ -871,5 +876,9 @@
}
func GetConfigKey(ctx ModuleContext) configKey {
- return configKey{archType: ctx.Arch().ArchType, osType: ctx.Os()}
+ return configKey{
+ // use string because Arch is not a valid key in go
+ arch: ctx.Arch().String(),
+ osType: ctx.Os(),
+ }
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index ad5b63b..e5cff90 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -9,9 +9,9 @@
func TestRequestResultsAfterInvokeBazel(t *testing.T) {
label := "//foo:bar"
- cfg := configKey{Arm64, Android}
+ cfg := configKey{"arm64_armv8-a", Android}
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64|android>>out/foo/bar.txt`,
+ bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
})
g, ok := bazelContext.GetOutputFiles(label, cfg)
if ok {
diff --git a/android/config.go b/android/config.go
index 10e074c..e8ca84c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -38,6 +38,7 @@
"android/soong/android/soongconfig"
"android/soong/bazel"
"android/soong/remoteexec"
+ "android/soong/starlark_fmt"
)
// Bool re-exports proptools.Bool for the android package.
@@ -286,14 +287,12 @@
}
}
- //TODO(b/216168792) should use common function to print Starlark code
- nonArchVariantProductVariablesJson, err := json.MarshalIndent(&nonArchVariantProductVariables, "", " ")
+ nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
}
- //TODO(b/216168792) should use common function to print Starlark code
- archVariantProductVariablesJson, err := json.MarshalIndent(&archVariantProductVariables, "", " ")
+ archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
if err != nil {
return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
}
@@ -351,18 +350,19 @@
config := &config{
productVariables: productVariables{
- DeviceName: stringPtr("test_device"),
- Platform_sdk_version: intPtr(30),
- Platform_sdk_codename: stringPtr("S"),
- Platform_version_active_codenames: []string{"S", "Tiramisu"},
- DeviceSystemSdkVersions: []string{"14", "15"},
- Platform_systemsdk_versions: []string{"29", "30"},
- AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
- AAPTPreferredConfig: stringPtr("xhdpi"),
- AAPTCharacteristics: stringPtr("nosdcard"),
- AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
- UncompressPrivAppDex: boolPtr(true),
- ShippingApiLevel: stringPtr("30"),
+ DeviceName: stringPtr("test_device"),
+ Platform_sdk_version: intPtr(30),
+ Platform_sdk_codename: stringPtr("S"),
+ Platform_base_sdk_extension_version: intPtr(1),
+ Platform_version_active_codenames: []string{"S", "Tiramisu"},
+ DeviceSystemSdkVersions: []string{"14", "15"},
+ Platform_systemsdk_versions: []string{"29", "30"},
+ AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+ AAPTPreferredConfig: stringPtr("xhdpi"),
+ AAPTCharacteristics: stringPtr("nosdcard"),
+ AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
+ UncompressPrivAppDex: boolPtr(true),
+ ShippingApiLevel: stringPtr("30"),
},
outDir: buildDir,
@@ -520,7 +520,7 @@
}
if archConfig != nil {
- androidTargets, err := decodeArchSettings(Android, archConfig)
+ androidTargets, err := decodeAndroidArchSettings(archConfig)
if err != nil {
return Config{}, err
}
@@ -743,6 +743,14 @@
return String(c.productVariables.Platform_sdk_codename)
}
+func (c *config) PlatformSdkExtensionVersion() int {
+ return *c.productVariables.Platform_sdk_extension_version
+}
+
+func (c *config) PlatformBaseSdkExtensionVersion() int {
+ return *c.productVariables.Platform_base_sdk_extension_version
+}
+
func (c *config) PlatformSecurityPatch() string {
return String(c.productVariables.Platform_security_patch)
}
@@ -1248,6 +1256,10 @@
return Bool(c.config.productVariables.ClangCoverage)
}
+func (c *deviceConfig) ClangCoverageContinuousMode() bool {
+ return Bool(c.config.productVariables.ClangCoverageContinuousMode)
+}
+
func (c *deviceConfig) GcovCoverageEnabled() bool {
return Bool(c.config.productVariables.GcovCoverage)
}
@@ -1531,6 +1543,18 @@
return c.config.productVariables.BoardProductPrivatePrebuiltDirs
}
+func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) ProductSepolicyPrebuiltApiDir() string {
+ return String(c.config.productVariables.ProductSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
+ return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
+}
+
func (c *deviceConfig) DirectedVendorSnapshot() bool {
return c.config.productVariables.DirectedVendorSnapshot
}
@@ -1631,6 +1655,10 @@
return c.config.productVariables.BuildBrokenVendorPropertyNamespace
}
+func (c *deviceConfig) BuildBrokenInputDir(name string) bool {
+ return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
+}
+
func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
}
@@ -1983,3 +2011,8 @@
func (c *config) RBEWrapper() string {
return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
}
+
+// UseHostMusl returns true if the host target has been configured to build against musl libc.
+func (c *config) UseHostMusl() bool {
+ return Bool(c.productVariables.HostMusl)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index c932ffa..50356d1 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -18,6 +18,8 @@
"strings"
"android/soong/bazel"
+
+ "github.com/google/blueprint"
)
func init() {
@@ -28,6 +30,11 @@
ctx.RegisterModuleType("filegroup", FileGroupFactory)
})
+// IsFilegroup checks that a module is a filegroup type
+func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
+ return ctx.OtherModuleType(m) == "filegroup"
+}
+
// https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct {
Srcs bazel.LabelListAttribute
@@ -112,12 +119,12 @@
return
}
- archVariant := ctx.Arch().ArchType
+ archVariant := ctx.Arch().String()
osVariant := ctx.Os()
if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
// This will be a regular file target, not filegroup, in Bazel.
// See FilegroupBp2Build for more information.
- archVariant = Common
+ archVariant = Common.String()
osVariant = CommonOS
}
diff --git a/android/hooks.go b/android/hooks.go
index bded764..5e3a4a7 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -15,7 +15,10 @@
package android
import (
+ "fmt"
+ "path"
"reflect"
+ "runtime"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -88,7 +91,19 @@
func (l *loadHookContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
inherited := []interface{}{&l.Module().base().commonProperties}
- module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), append(inherited, props...)...).(Module)
+
+ var typeName string
+ if typeNameLookup, ok := ModuleTypeByFactory()[reflect.ValueOf(factory)]; ok {
+ typeName = typeNameLookup
+ } else {
+ factoryPtr := reflect.ValueOf(factory).Pointer()
+ factoryFunc := runtime.FuncForPC(factoryPtr)
+ filePath, _ := factoryFunc.FileLine(factoryPtr)
+ typeName = fmt.Sprintf("%s_%s", path.Base(filePath), factoryFunc.Name())
+ }
+ typeName = typeName + "_loadHookModule"
+
+ module := l.bp.CreateModule(ModuleFactoryAdaptor(factory), typeName, append(inherited, props...)...).(Module)
if l.Module().base().variableProperties != nil && module.base().variableProperties != nil {
src := l.Module().base().variableProperties
diff --git a/android/license.go b/android/license.go
index 587cb36..ebee055 100644
--- a/android/license.go
+++ b/android/license.go
@@ -63,7 +63,7 @@
func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
// license modules have no licenses, but license_kinds must refer to license_kind modules
mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
- mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...)
+ namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
if lk, ok := module.(*licenseKindModule); ok {
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index 2ce921b..b17defe 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -90,7 +90,10 @@
// Populate the properties from the variant.
l := variant.(*licenseModule)
p.License_kinds = l.properties.License_kinds
- p.License_text = l.base().commonProperties.Effective_license_text
+ p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text))
+ for _, np := range l.base().commonProperties.Effective_license_text {
+ p.License_text = append(p.License_text, np.Path)
+ }
}
func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) {
diff --git a/android/licenses.go b/android/licenses.go
index b82d8d7..e60c7a2 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -213,7 +213,7 @@
m.base().commonProperties.Effective_package_name = l.properties.Package_name
}
mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
- mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
} else {
@@ -239,10 +239,24 @@
*prop = SortedUniqueStrings(*prop)
}
-// Update a property Path array with a distinct union of its values and a list of new values.
-func mergePathProps(prop *Paths, values ...Path) {
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func namePathProps(prop *NamedPaths, name *string, values ...Path) {
+ if name == nil {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, ""})
+ }
+ } else {
+ for _, value := range values {
+ *prop = append(*prop, NamedPath{value, *name})
+ }
+ }
+ *prop = SortedUniqueNamedPaths(*prop)
+}
+
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
*prop = append(*prop, values...)
- *prop = SortedUniquePaths(*prop)
+ *prop = SortedUniqueNamedPaths(*prop)
}
// Get the licenses property falling back to the package default.
@@ -319,4 +333,6 @@
ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
+ ctx.Strict("COMPLIANCENOTICE_BOM", ctx.Config().HostToolPath(ctx, "compliancenotice_bom").String())
+ ctx.Strict("COMPLIANCENOTICE_SHIPPEDLIBS", ctx.Config().HostToolPath(ctx, "compliancenotice_shippedlibs").String())
}
diff --git a/android/licenses_test.go b/android/licenses_test.go
index 70160fa..8a81e12 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -90,9 +90,9 @@
"libother": []string{"shownotice"},
},
effectiveNotices: map[string][]string{
- "libexample1": []string{"top/LICENSE", "top/NOTICE"},
- "libnested": []string{"top/LICENSE", "top/NOTICE"},
- "libother": []string{"top/LICENSE", "top/NOTICE"},
+ "libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libnested": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+ "libother": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
},
},
diff --git a/android/module.go b/android/module.go
index d0807c3..43509c0 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,11 +16,13 @@
import (
"fmt"
+ "net/url"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
+ "sort"
"strings"
"text/scanner"
@@ -454,6 +456,10 @@
// GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods,
// but do not exist.
GetMissingDependencies() []string
+
+ // LicenseMetadataFile returns the path where the license metadata for this module will be
+ // generated.
+ LicenseMetadataFile() Path
}
type Module interface {
@@ -616,6 +622,53 @@
Tag *string `android:"arch_variant"`
}
+// NamedPath associates a path with a name. e.g. a license text path with a package name
+type NamedPath struct {
+ Path Path
+ Name string
+}
+
+// String returns an escaped string representing the `NamedPath`.
+func (p NamedPath) String() string {
+ if len(p.Name) > 0 {
+ return p.Path.String() + ":" + url.QueryEscape(p.Name)
+ }
+ return p.Path.String()
+}
+
+// NamedPaths describes a list of paths each associated with a name.
+type NamedPaths []NamedPath
+
+// Strings returns a list of escaped strings representing each `NamedPath` in the list.
+func (l NamedPaths) Strings() []string {
+ result := make([]string, 0, len(l))
+ for _, p := range l {
+ result = append(result, p.String())
+ }
+ return result
+}
+
+// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset.
+func SortedUniqueNamedPaths(l NamedPaths) NamedPaths {
+ if len(l) == 0 {
+ return l
+ }
+ sort.Slice(l, func(i, j int) bool {
+ return l[i].String() < l[j].String()
+ })
+ k := 0
+ for i := 1; i < len(l); i++ {
+ if l[i].String() == l[k].String() {
+ continue
+ }
+ k++
+ if k < i {
+ l[k] = l[i]
+ }
+ }
+ return l[:k+1]
+}
+
type nameProperties struct {
// The name of the module. Must be unique across all modules.
Name *string
@@ -684,7 +737,7 @@
// Override of module name when reporting licenses
Effective_package_name *string `blueprint:"mutated"`
// Notice files
- Effective_license_text Paths `blueprint:"mutated"`
+ Effective_license_text NamedPaths `blueprint:"mutated"`
// License names
Effective_license_kinds []string `blueprint:"mutated"`
// License conditions
@@ -1801,7 +1854,11 @@
}
func (m *ModuleBase) EffectiveLicenseFiles() Paths {
- return m.commonProperties.Effective_license_text
+ result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
+ for _, p := range m.commonProperties.Effective_license_text {
+ result = append(result, p.Path)
+ }
+ return result
}
// computeInstallDeps finds the installed paths of all dependencies that have a dependency
@@ -3226,6 +3283,10 @@
return m.bp
}
+func (m *moduleContext) LicenseMetadataFile() Path {
+ return m.module.base().licenseMetadataFile
+}
+
// SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name"
// into the module name, or empty string if the input was not a module reference.
func SrcIsModule(s string) (module string) {
diff --git a/android/module_test.go b/android/module_test.go
index c35e66e..1dcddf7 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -15,12 +15,9 @@
package android
import (
- "bytes"
"path/filepath"
"runtime"
"testing"
-
- mkparser "android/soong/androidmk/parser"
)
func TestSrcIsModule(t *testing.T) {
@@ -475,21 +472,10 @@
prepareForModuleTests,
PrepareForTestWithArchMutator,
FixtureModifyConfig(SetKatiEnabledForTests),
- FixtureRegisterWithContext(func(ctx RegistrationContext) {
- ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
- }),
+ PrepareForTestWithMakevars,
).RunTestWithBp(t, bp)
- installs := result.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
- buf := bytes.NewBuffer(append([]byte(nil), installs...))
- parser := mkparser.NewParser("makevars", buf)
-
- nodes, errs := parser.Parse()
- if len(errs) > 0 {
- t.Fatalf("error parsing install rules: %s", errs[0])
- }
-
- rules := parseMkRules(t, result.Config, nodes)
+ rules := result.InstallMakeRulesForTesting(t)
module := func(name string, host bool) TestingModule {
variant := "android_common"
@@ -501,121 +487,78 @@
outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
- ruleForOutput := func(output string) installMakeRule {
+ ruleForOutput := func(output string) InstallMakeRule {
for _, rule := range rules {
- if rule.target == output {
+ if rule.Target == output {
return rule
}
}
t.Fatalf("no make install rule for %s", output)
- return installMakeRule{}
+ return InstallMakeRule{}
}
- installRule := func(name string) installMakeRule {
+ installRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
}
- symlinkRule := func(name string) installMakeRule {
+ symlinkRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
}
hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
- hostInstallRule := func(name string) installMakeRule {
+ hostInstallRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86", name))
}
- hostSymlinkRule := func(name string) installMakeRule {
+ hostSymlinkRule := func(name string) InstallMakeRule {
return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
}
- assertDeps := func(rule installMakeRule, deps ...string) {
+ assertDeps := func(rule InstallMakeRule, deps ...string) {
t.Helper()
- AssertArrayString(t, "expected inputs", deps, rule.deps)
+ AssertArrayString(t, "expected inputs", deps, rule.Deps)
}
- assertOrderOnlys := func(rule installMakeRule, orderonlys ...string) {
+ assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) {
t.Helper()
- AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.orderOnlyDeps)
+ AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps)
}
// Check host install rule dependencies
assertDeps(hostInstallRule("foo"),
hostOutputRule("foo").Output.String(),
- hostInstallRule("bar").target,
- hostSymlinkRule("bar").target,
- hostInstallRule("baz").target,
- hostSymlinkRule("baz").target,
- hostInstallRule("qux").target,
- hostSymlinkRule("qux").target,
+ hostInstallRule("bar").Target,
+ hostSymlinkRule("bar").Target,
+ hostInstallRule("baz").Target,
+ hostSymlinkRule("baz").Target,
+ hostInstallRule("qux").Target,
+ hostSymlinkRule("qux").Target,
)
assertOrderOnlys(hostInstallRule("foo"))
// Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an
// order-only dependency, so that the tool gets updated when the symlink is depended on.
- assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").target)
+ assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target)
assertOrderOnlys(hostSymlinkRule("foo"))
// Check device install rule dependencies
assertDeps(installRule("foo"), outputRule("foo").Output.String())
assertOrderOnlys(installRule("foo"),
- installRule("bar").target,
- symlinkRule("bar").target,
- installRule("baz").target,
- symlinkRule("baz").target,
- installRule("qux").target,
- symlinkRule("qux").target,
+ installRule("bar").Target,
+ symlinkRule("bar").Target,
+ installRule("baz").Target,
+ symlinkRule("baz").Target,
+ installRule("qux").Target,
+ symlinkRule("qux").Target,
)
// Check device symlink rule dependencies. Device symlinks could use an order-only dependency,
// but the current implementation uses a normal dependency.
- assertDeps(symlinkRule("foo"), installRule("foo").target)
+ assertDeps(symlinkRule("foo"), installRule("foo").Target)
assertOrderOnlys(symlinkRule("foo"))
}
-type installMakeRule struct {
- target string
- deps []string
- orderOnlyDeps []string
-}
-
-func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []installMakeRule {
- var rules []installMakeRule
- for _, node := range nodes {
- if mkParserRule, ok := node.(*mkparser.Rule); ok {
- var rule installMakeRule
-
- if targets := mkParserRule.Target.Words(); len(targets) == 0 {
- t.Fatalf("no targets for rule %s", mkParserRule.Dump())
- } else if len(targets) > 1 {
- t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
- } else if !targets[0].Const() {
- t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
- } else {
- rule.target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
- }
-
- prereqList := &rule.deps
- for _, prereq := range mkParserRule.Prerequisites.Words() {
- if !prereq.Const() {
- t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
- }
-
- if prereq.Value(nil) == "|" {
- prereqList = &rule.orderOnlyDeps
- continue
- }
-
- *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
- }
-
- rules = append(rules, rule)
- }
- }
-
- return rules
-}
-
type PropsTestModuleEmbedded struct {
Embedded_prop *string
}
@@ -816,3 +759,120 @@
})
}
}
+
+func TestSortedUniqueNamedPaths(t *testing.T) {
+ type np struct {
+ path, name string
+ }
+ makePaths := func(l []np) NamedPaths {
+ result := make(NamedPaths, 0, len(l))
+ for _, p := range l {
+ result = append(result, NamedPath{PathForTesting(p.path), p.name})
+ }
+ return result
+ }
+
+ tests := []struct {
+ name string
+ in []np
+ expectedOut []np
+ }{
+ {
+ name: "empty",
+ in: []np{},
+ expectedOut: []np{},
+ },
+ {
+ name: "all_same",
+ in: []np{
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ {"a.txt", "A"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ },
+ },
+ {
+ name: "same_path_different_names",
+ in: []np{
+ {"a.txt", "C"},
+ {"a.txt", "A"},
+ {"a.txt", "D"},
+ {"a.txt", "B"},
+ {"a.txt", "E"},
+ },
+ expectedOut: []np{
+ {"a.txt", "A"},
+ {"a.txt", "B"},
+ {"a.txt", "C"},
+ {"a.txt", "D"},
+ {"a.txt", "E"},
+ },
+ },
+ {
+ name: "different_paths_same_name",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "A"},
+ {"a/txt", "A"},
+ {"b", "A"},
+ {"a/b/d", "A"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "A"},
+ {"a/b/d", "A"},
+ {"a/txt", "A"},
+ {"b/b.txt", "A"},
+ {"b", "A"},
+ },
+ },
+ {
+ name: "all_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"b", "C"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ {
+ name: "some_different",
+ in: []np{
+ {"b/b.txt", "A"},
+ {"a/a.txt", "B"},
+ {"a/txt", "D"},
+ {"a/b/d", "E"},
+ {"b", "C"},
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ },
+ expectedOut: []np{
+ {"a/a.txt", "B"},
+ {"a/b/d", "E"},
+ {"a/txt", "D"},
+ {"b/b.txt", "A"},
+ {"b", "C"},
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ actual := SortedUniqueNamedPaths(makePaths(tt.in))
+ expected := makePaths(tt.expectedOut)
+ t.Logf("actual: %v", actual)
+ t.Logf("expected: %v", expected)
+ AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
+ })
+ }
+}
diff --git a/android/paths.go b/android/paths.go
index 4c69de7..e7829b9 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -405,6 +405,13 @@
return PathsForModuleSrcExcludes(ctx, paths, nil)
}
+type SourceInput struct {
+ Context ModuleMissingDepsPathContext
+ Paths []string
+ ExcludePaths []string
+ IncludeDirs bool
+}
+
// PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
// those listed in excludes. Elements of paths and excludes are resolved as:
// * filepath, relative to local module directory, resolves as a filepath relative to the local
@@ -423,12 +430,21 @@
// missing dependencies
// * otherwise, a ModuleError is thrown.
func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
- ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
- if ctx.Config().AllowMissingDependencies() {
- ctx.AddMissingDependencies(missingDeps)
+ return PathsRelativeToModuleSourceDir(SourceInput{
+ Context: ctx,
+ Paths: paths,
+ ExcludePaths: excludes,
+ IncludeDirs: true,
+ })
+}
+
+func PathsRelativeToModuleSourceDir(input SourceInput) Paths {
+ ret, missingDeps := PathsAndMissingDepsRelativeToModuleSourceDir(input)
+ if input.Context.Config().AllowMissingDependencies() {
+ input.Context.AddMissingDependencies(missingDeps)
} else {
for _, m := range missingDeps {
- ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+ input.Context.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
}
}
return ret
@@ -543,23 +559,31 @@
// Properties passed as the paths argument must have been annotated with struct tag
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
// path_deps mutator.
-func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
- prefix := pathForModuleSrc(ctx).String()
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) {
+ return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{
+ Context: ctx,
+ Paths: paths,
+ ExcludePaths: excludes,
+ IncludeDirs: true,
+ })
+}
+
+func PathsAndMissingDepsRelativeToModuleSourceDir(input SourceInput) (Paths, []string) {
+ prefix := pathForModuleSrc(input.Context).String()
var expandedExcludes []string
- if excludes != nil {
- expandedExcludes = make([]string, 0, len(excludes))
+ if input.ExcludePaths != nil {
+ expandedExcludes = make([]string, 0, len(input.ExcludePaths))
}
var missingExcludeDeps []string
-
- for _, e := range excludes {
+ for _, e := range input.ExcludePaths {
if m, t := SrcIsModuleWithTag(e); m != "" {
- modulePaths, err := getPathsFromModuleDep(ctx, e, m, t)
+ modulePaths, err := getPathsFromModuleDep(input.Context, e, m, t)
if m, ok := err.(missingDependencyError); ok {
missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...)
} else if err != nil {
- reportPathError(ctx, err)
+ reportPathError(input.Context, err)
} else {
expandedExcludes = append(expandedExcludes, modulePaths.Strings()...)
}
@@ -568,19 +592,24 @@
}
}
- if paths == nil {
+ if input.Paths == nil {
return nil, missingExcludeDeps
}
var missingDeps []string
- expandedSrcFiles := make(Paths, 0, len(paths))
- for _, s := range paths {
- srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
+ expandedSrcFiles := make(Paths, 0, len(input.Paths))
+ for _, s := range input.Paths {
+ srcFiles, err := expandOneSrcPath(sourcePathInput{
+ context: input.Context,
+ path: s,
+ expandedExcludes: expandedExcludes,
+ includeDirs: input.IncludeDirs,
+ })
if depErr, ok := err.(missingDependencyError); ok {
missingDeps = append(missingDeps, depErr.missingDeps...)
} else if err != nil {
- reportPathError(ctx, err)
+ reportPathError(input.Context, err)
}
expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
}
@@ -596,44 +625,59 @@
return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
}
+type sourcePathInput struct {
+ context ModuleWithDepsPathContext
+ path string
+ expandedExcludes []string
+ includeDirs bool
+}
+
// Expands one path string to Paths rooted from the module's local source
// directory, excluding those listed in the expandedExcludes.
// Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax.
-func expandOneSrcPath(ctx ModuleWithDepsPathContext, sPath string, expandedExcludes []string) (Paths, error) {
+func expandOneSrcPath(input sourcePathInput) (Paths, error) {
excludePaths := func(paths Paths) Paths {
- if len(expandedExcludes) == 0 {
+ if len(input.expandedExcludes) == 0 {
return paths
}
remainder := make(Paths, 0, len(paths))
for _, p := range paths {
- if !InList(p.String(), expandedExcludes) {
+ if !InList(p.String(), input.expandedExcludes) {
remainder = append(remainder, p)
}
}
return remainder
}
- if m, t := SrcIsModuleWithTag(sPath); m != "" {
- modulePaths, err := getPathsFromModuleDep(ctx, sPath, m, t)
+ if m, t := SrcIsModuleWithTag(input.path); m != "" {
+ modulePaths, err := getPathsFromModuleDep(input.context, input.path, m, t)
if err != nil {
return nil, err
} else {
return excludePaths(modulePaths), nil
}
- } else if pathtools.IsGlob(sPath) {
- paths := GlobFiles(ctx, pathForModuleSrc(ctx, sPath).String(), expandedExcludes)
- return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
} else {
- p := pathForModuleSrc(ctx, sPath)
- if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
- ReportPathErrorf(ctx, "%s: %s", p, err.Error())
- } else if !exists && !ctx.Config().TestAllowNonExistentPaths {
- ReportPathErrorf(ctx, "module source path %q does not exist", p)
- }
+ p := pathForModuleSrc(input.context, input.path)
+ if pathtools.IsGlob(input.path) {
+ paths := GlobFiles(input.context, p.String(), input.expandedExcludes)
+ return PathsWithModuleSrcSubDir(input.context, paths, ""), nil
+ } else {
+ if exists, _, err := input.context.Config().fs.Exists(p.String()); err != nil {
+ ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+ } else if !exists && !input.context.Config().TestAllowNonExistentPaths {
+ ReportPathErrorf(input.context, "module source path %q does not exist", p)
+ } else if !input.includeDirs {
+ if isDir, err := input.context.Config().fs.IsDir(p.String()); exists && err != nil {
+ ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+ } else if isDir {
+ ReportPathErrorf(input.context, "module source path %q is a directory", p)
+ }
+ }
- if InList(p.String(), expandedExcludes) {
- return nil, nil
+ if InList(p.String(), input.expandedExcludes) {
+ return nil, nil
+ }
+ return Paths{p}, nil
}
- return Paths{p}, nil
}
}
@@ -1315,7 +1359,7 @@
// validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
// reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
p := strings.Join(pathComponents, string(filepath.Separator))
- paths, err := expandOneSrcPath(ctx, p, nil)
+ paths, err := expandOneSrcPath(sourcePathInput{context: ctx, path: p, includeDirs: true})
if err != nil {
if depErr, ok := err.(missingDependencyError); ok {
if ctx.Config().AllowMissingDependencies() {
@@ -1430,14 +1474,11 @@
func PathForVndkRefAbiDump(ctx ModuleInstallPathContext, version, fileName string,
isNdk, isLlndkOrVndk, isGzip bool) OptionalPath {
- arches := ctx.DeviceConfig().Arches()
- if len(arches) == 0 {
- panic("device build with no primary arch")
- }
- currentArch := ctx.Arch()
- archNameAndVariant := currentArch.ArchType.String()
- if currentArch.ArchVariant != "" {
- archNameAndVariant += "_" + currentArch.ArchVariant
+ currentArchType := ctx.Arch().ArchType
+ primaryArchType := ctx.Config().DevicePrimaryArchType()
+ archName := currentArchType.String()
+ if currentArchType != primaryArchType {
+ archName += "_" + primaryArchType.String()
}
var dirName string
@@ -1459,7 +1500,7 @@
}
return ExistentPathForSource(ctx, "prebuilts", "abi-dumps", dirName,
- version, binderBitness, archNameAndVariant, "source-based",
+ version, binderBitness, archName, "source-based",
fileName+ext)
}
diff --git a/android/proto.go b/android/proto.go
index 64d4d05..c3759f8 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -16,6 +16,7 @@
import (
"android/soong/bazel"
+ "regexp"
"strings"
"github.com/google/blueprint"
@@ -26,6 +27,14 @@
canonicalPathFromRootDefault = true
)
+var (
+ // ignoring case, checks for proto or protos as an independent word in the name, whether at the
+ // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+ filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
+
+ ProtoSrcLabelPartition = bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup}
+)
+
// TODO(ccross): protos are often used to communicate between multiple modules. If the only
// way to convert a proto to source is to reference it as a source file, and external modules cannot
// reference source files in other modules, then every module that owns a proto file will need to
@@ -165,12 +174,11 @@
// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
// information necessary for language-specific handling.
-func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
+func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, m *ModuleBase, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
var info Bp2buildProtoInfo
if srcs.IsEmpty() {
return info, false
}
- m := module.base()
info.Name = m.Name() + "_proto"
attrs := protoAttrs{
@@ -187,7 +195,7 @@
if axis == bazel.NoConfigAxis {
info.Type = props.Proto.Type
- if proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
+ if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
// an empty string indicates to strips the package path
path := ""
attrs.Strip_import_prefix = &path
@@ -205,3 +213,13 @@
return info, true
}
+
+func isProtoFilegroup(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+ m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+ labelStr := label.Label
+ if !exists || !IsFilegroup(ctx, m) {
+ return labelStr, false
+ }
+ likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
+ return labelStr, likelyProtos
+}
diff --git a/android/register.go b/android/register.go
index 10e14e0..c505833 100644
--- a/android/register.go
+++ b/android/register.go
@@ -59,6 +59,7 @@
var moduleTypes []moduleType
var moduleTypesForDocs = map[string]reflect.Value{}
+var moduleTypeByFactory = map[reflect.Value]string{}
type singleton struct {
// True if this should be registered as a pre-singleton, false otherwise.
@@ -140,6 +141,7 @@
// RegisterModuleType was a lambda.
func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
moduleTypesForDocs[name] = factory
+ moduleTypeByFactory[factory] = name
}
func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -228,6 +230,10 @@
return moduleTypesForDocs
}
+func ModuleTypeByFactory() map[reflect.Value]string {
+ return moduleTypeByFactory
+}
+
// Interface for registering build components.
//
// Provided to allow registration of build components to be shared between the runtime
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 1c6b1c0..098c1fc 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -470,7 +470,7 @@
func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- BuiltTool("dep_fixer").
+ builtToolWithoutDeps("dep_fixer").
Inputs(depFiles.Paths())
}
@@ -638,7 +638,7 @@
}
sboxCmd.Text("rm -rf").Output(r.outDir)
sboxCmd.Text("&&")
- sboxCmd.BuiltTool("sbox").
+ sboxCmd.builtToolWithoutDeps("sbox").
Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Flag("--manifest").Input(r.sboxManifestPath)
@@ -1040,6 +1040,19 @@
// It is equivalent to:
// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+ if c.rule.ctx.Config().UseHostMusl() {
+ // If the host is using musl, assume that the tool was built against musl libc and include
+ // libc_musl.so in the sandbox.
+ // TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
+ // this could be a dependency + TransitivePackagingSpecs.
+ c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
+ }
+ return c.builtToolWithoutDeps(tool)
+}
+
+// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies. It is used
+// internally by RuleBuilder for helper tools that are known to be compiled statically.
+func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index eb5554c..9d98478 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -46,8 +46,8 @@
PrepareForTestWithAndroidMk,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
- ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
}),
+ PrepareForTestWithMakevars,
)
func TestSingletonModule(t *testing.T) {
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index acb9d18..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -386,6 +386,46 @@
})).RunTest(t)
}
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+ bp := `
+ soong_config_string_variable {
+ name: "board",
+ values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+ }
+
+ soong_config_module_type {
+ name: "acme_test",
+ module_type: "test",
+ config_namespace: "acme",
+ variables: ["board"],
+ properties: ["cflags", "srcs", "defaults"],
+ }
+ `
+
+ fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+ return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.VendorVars = vars
+ })
+ }
+
+ GroupFixturePreparers(
+ fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+ PrepareForTestWithDefaults,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ }),
+ FixtureWithRootAndroidBp(bp),
+ ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+ // TODO(b/171232169): improve the error message for non-existent properties
+ `Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+ })).RunTest(t)
+}
+
func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
config := TestConfig(buildDir, nil, bp, fs)
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 9bf3344..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,6 +10,7 @@
"blueprint-parser",
"blueprint-proptools",
"soong-bazel",
+ "soong-starlark-format",
],
srcs: [
"config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 09a5057..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -25,6 +25,8 @@
"github.com/google/blueprint"
"github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
+
+ "android/soong/starlark_fmt"
)
const conditionsDefault = "conditions_default"
@@ -177,10 +179,14 @@
return []error{fmt.Errorf("values property must be set")}
}
+ vals := make(map[string]bool, len(stringProps.Values))
for _, name := range stringProps.Values {
if err := checkVariableName(name); err != nil {
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ } else if _, ok := vals[name]; ok {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
}
+ vals[name] = true
}
v.variables[base.variable] = &stringVariable{
@@ -235,7 +241,12 @@
// string vars, bool vars and value vars created by every
// soong_config_module_type in this build.
type Bp2BuildSoongConfigDefinitions struct {
- StringVars map[string]map[string]bool
+ // varCache contains a cache of string variables namespace + property
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ varCache map[string]bool
+
+ StringVars map[string][]string
BoolVars map[string]bool
ValueVars map[string]bool
}
@@ -253,7 +264,7 @@
defer bp2buildSoongConfigVarsLock.Unlock()
if defs.StringVars == nil {
- defs.StringVars = make(map[string]map[string]bool)
+ defs.StringVars = make(map[string][]string)
}
if defs.BoolVars == nil {
defs.BoolVars = make(map[string]bool)
@@ -261,15 +272,24 @@
if defs.ValueVars == nil {
defs.ValueVars = make(map[string]bool)
}
+ if defs.varCache == nil {
+ defs.varCache = make(map[string]bool)
+ }
for _, moduleType := range mtDef.ModuleTypes {
for _, v := range moduleType.Variables {
key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+ // The same variable may be used in multiple module types (for example, if need support
+ // for cc_default and java_default), only need to process once
+ if _, keyInCache := defs.varCache[key]; keyInCache {
+ continue
+ } else {
+ defs.varCache[key] = true
+ }
+
if strVar, ok := v.(*stringVariable); ok {
- if _, ok := defs.StringVars[key]; !ok {
- defs.StringVars[key] = make(map[string]bool, 0)
- }
for _, value := range strVar.values {
- defs.StringVars[key][value] = true
+ defs.StringVars[key] = append(defs.StringVars[key], value)
}
} else if _, ok := v.(*boolVariable); ok {
defs.BoolVars[key] = true
@@ -302,29 +322,16 @@
// String emits the Soong config variable definitions as Starlark dictionaries.
func (defs Bp2BuildSoongConfigDefinitions) String() string {
ret := ""
- ret += "soong_config_bool_variables = {\n"
- for _, boolVar := range sortedStringKeys(defs.BoolVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", boolVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_bool_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+ ret += "\n\n"
- ret += "soong_config_value_variables = {\n"
- for _, valueVar := range sortedStringKeys(defs.ValueVars) {
- ret += fmt.Sprintf(" \"%s\": True,\n", valueVar)
- }
- ret += "}\n"
- ret += "\n"
+ ret += "soong_config_value_variables = "
+ ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+ ret += "\n\n"
- ret += "soong_config_string_variables = {\n"
- for _, stringVar := range sortedStringKeys(defs.StringVars) {
- ret += fmt.Sprintf(" \"%s\": [\n", stringVar)
- for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
- ret += fmt.Sprintf(" \"%s\",\n", choice)
- }
- ret += fmt.Sprintf(" ],\n")
- }
- ret += "}"
+ ret += "soong_config_string_variables = "
+ ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
return ret
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b14f8b4..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -367,19 +367,19 @@
func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
testCases := []struct {
+ desc string
defs Bp2BuildSoongConfigDefinitions
expected string
}{
{
+ desc: "all empty",
defs: Bp2BuildSoongConfigDefinitions{},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only bool",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var": true,
@@ -389,39 +389,35 @@
"bool_var": True,
}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only value vars",
defs: Bp2BuildSoongConfigDefinitions{
ValueVars: map[string]bool{
"value_var": true,
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
soong_config_value_variables = {
"value_var": True,
}
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+ desc: "only string vars",
defs: Bp2BuildSoongConfigDefinitions{
- StringVars: map[string]map[string]bool{
- "string_var": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
},
},
- expected: `soong_config_bool_variables = {
-}
+ expected: `soong_config_bool_variables = {}
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
soong_config_string_variables = {
"string_var": [
@@ -430,6 +426,7 @@
"choice3",
],
}`}, {
+ desc: "all vars",
defs: Bp2BuildSoongConfigDefinitions{
BoolVars: map[string]bool{
"bool_var_one": true,
@@ -438,15 +435,15 @@
"value_var_one": true,
"value_var_two": true,
},
- StringVars: map[string]map[string]bool{
- "string_var_one": map[string]bool{
- "choice1": true,
- "choice2": true,
- "choice3": true,
+ StringVars: map[string][]string{
+ "string_var_one": []string{
+ "choice1",
+ "choice2",
+ "choice3",
},
- "string_var_two": map[string]bool{
- "foo": true,
- "bar": true,
+ "string_var_two": []string{
+ "foo",
+ "bar",
},
},
},
@@ -466,15 +463,17 @@
"choice3",
],
"string_var_two": [
- "bar",
"foo",
+ "bar",
],
}`},
}
for _, test := range testCases {
- actual := test.defs.String()
- if actual != test.expected {
- t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
- }
+ t.Run(test.desc, func(t *testing.T) {
+ actual := test.defs.String()
+ if actual != test.expected {
+ t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+ }
+ })
}
}
diff --git a/android/testing.go b/android/testing.go
index 39864e1..a9632e9 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -15,6 +15,7 @@
package android
import (
+ "bytes"
"fmt"
"path/filepath"
"regexp"
@@ -23,6 +24,8 @@
"sync"
"testing"
+ mkparser "android/soong/androidmk/parser"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -115,6 +118,10 @@
ctx.PreArchMutators(RegisterNamespaceMutator)
})
+var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+})
+
// Test fixture preparer that will register most java build components.
//
// Singletons and mutators should only be added here if they are needed for a majority of java
@@ -602,6 +609,62 @@
"\nall singletons: %v", name, allSingletonNames))
}
+type InstallMakeRule struct {
+ Target string
+ Deps []string
+ OrderOnlyDeps []string
+}
+
+func parseMkRules(t *testing.T, config Config, nodes []mkparser.Node) []InstallMakeRule {
+ var rules []InstallMakeRule
+ for _, node := range nodes {
+ if mkParserRule, ok := node.(*mkparser.Rule); ok {
+ var rule InstallMakeRule
+
+ if targets := mkParserRule.Target.Words(); len(targets) == 0 {
+ t.Fatalf("no targets for rule %s", mkParserRule.Dump())
+ } else if len(targets) > 1 {
+ t.Fatalf("unsupported multiple targets for rule %s", mkParserRule.Dump())
+ } else if !targets[0].Const() {
+ t.Fatalf("unsupported non-const target for rule %s", mkParserRule.Dump())
+ } else {
+ rule.Target = normalizeStringRelativeToTop(config, targets[0].Value(nil))
+ }
+
+ prereqList := &rule.Deps
+ for _, prereq := range mkParserRule.Prerequisites.Words() {
+ if !prereq.Const() {
+ t.Fatalf("unsupported non-const prerequisite for rule %s", mkParserRule.Dump())
+ }
+
+ if prereq.Value(nil) == "|" {
+ prereqList = &rule.OrderOnlyDeps
+ continue
+ }
+
+ *prereqList = append(*prereqList, normalizeStringRelativeToTop(config, prereq.Value(nil)))
+ }
+
+ rules = append(rules, rule)
+ }
+ }
+
+ return rules
+}
+
+func (ctx *TestContext) InstallMakeRulesForTesting(t *testing.T) []InstallMakeRule {
+ installs := ctx.SingletonForTests("makevars").Singleton().(*makeVarsSingleton).installsForTesting
+ buf := bytes.NewBuffer(append([]byte(nil), installs...))
+ parser := mkparser.NewParser("makevars", buf)
+
+ nodes, errs := parser.Parse()
+ if len(errs) > 0 {
+ t.Fatalf("error parsing install rules: %s", errs[0])
+ }
+
+ return parseMkRules(t, ctx.config, nodes)
+}
+
func (ctx *TestContext) Config() Config {
return ctx.config
}
diff --git a/android/util.go b/android/util.go
index 0ee253e..47c4583 100644
--- a/android/util.go
+++ b/android/util.go
@@ -65,21 +65,55 @@
return buf.String()
}
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
func SortedStringKeys(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic(fmt.Sprintf("%#v is not a map", m))
}
- keys := v.MapKeys()
- s := make([]string, 0, len(keys))
- for _, key := range keys {
- s = append(s, key.String())
+ if v.Len() == 0 {
+ return nil
+ }
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Key().String())
}
sort.Strings(s)
return s
}
+// stringValues returns the values of the given string-valued map in randomized map order.
+func stringValues(m interface{}) []string {
+ v := reflect.ValueOf(m)
+ if v.Kind() != reflect.Map {
+ panic(fmt.Sprintf("%#v is not a map", m))
+ }
+ if v.Len() == 0 {
+ return nil
+ }
+ iter := v.MapRange()
+ s := make([]string, 0, v.Len())
+ for iter.Next() {
+ s = append(s, iter.Value().String())
+ }
+ return s
+}
+
+// SortedStringValues returns the values of the given string-valued map in the ascending order.
+func SortedStringValues(m interface{}) []string {
+ s := stringValues(m)
+ sort.Strings(s)
+ return s
+}
+
+// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
+// with duplicates removed.
+func SortedUniqueStringValues(m interface{}) []string {
+ s := stringValues(m)
+ return SortedUniqueStrings(s)
+}
+
// IndexList returns the index of the first occurrence of the given string in the list or -1
func IndexList(s string, list []string) int {
for i, l := range list {
diff --git a/android/util_test.go b/android/util_test.go
index 09bec01..9b9253b 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -640,3 +640,117 @@
})
}
}
+
+func TestSortedStringKeys(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"a": "foo", "b": "bar"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "interface values",
+ in: map[string]interface{}{"a": nil, "b": nil},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringKeys(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
+
+func TestSortedUniqueStringValues(t *testing.T) {
+ testCases := []struct {
+ name string
+ in interface{}
+ expected []string
+ }{
+ {
+ name: "nil",
+ in: map[string]string(nil),
+ expected: nil,
+ },
+ {
+ name: "empty",
+ in: map[string]string{},
+ expected: nil,
+ },
+ {
+ name: "simple",
+ in: map[string]string{"foo": "a", "bar": "b"},
+ expected: []string{"a", "b"},
+ },
+ {
+ name: "duplicates",
+ in: map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+ expected: []string{"a", "b"},
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ got := SortedUniqueStringValues(tt.in)
+ if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+ t.Errorf("wanted %q, got %q", w, g)
+ }
+ })
+ }
+}
diff --git a/android/variable.go b/android/variable.go
index ff77fef..37037eb 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -191,6 +191,7 @@
Platform_sdk_version_or_codename *string `json:",omitempty"`
Platform_sdk_final *bool `json:",omitempty"`
Platform_sdk_extension_version *int `json:",omitempty"`
+ Platform_base_sdk_extension_version *int `json:",omitempty"`
Platform_version_active_codenames []string `json:",omitempty"`
Platform_vndk_version *string `json:",omitempty"`
Platform_systemsdk_versions []string `json:",omitempty"`
@@ -305,10 +306,11 @@
JavaCoveragePaths []string `json:",omitempty"`
JavaCoverageExcludePaths []string `json:",omitempty"`
- GcovCoverage *bool `json:",omitempty"`
- ClangCoverage *bool `json:",omitempty"`
- NativeCoveragePaths []string `json:",omitempty"`
- NativeCoverageExcludePaths []string `json:",omitempty"`
+ GcovCoverage *bool `json:",omitempty"`
+ ClangCoverage *bool `json:",omitempty"`
+ NativeCoveragePaths []string `json:",omitempty"`
+ NativeCoverageExcludePaths []string `json:",omitempty"`
+ ClangCoverageContinuousMode *bool `json:",omitempty"`
// Set by NewConfig
Native_coverage *bool `json:",omitempty"`
@@ -364,6 +366,9 @@
PlatformSepolicyVersion *string `json:",omitempty"`
TotSepolicyVersion *string `json:",omitempty"`
+ SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
+ ProductSepolicyPrebuiltApiDir *string `json:",omitempty"`
+
PlatformSepolicyCompatVersions []string `json:",omitempty"`
VendorVars map[string]map[string]string `json:",omitempty"`
@@ -417,9 +422,10 @@
ShippingApiLevel *string `json:",omitempty"`
- BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
- BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
- BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+ BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
+ BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
+ BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+ BuildBrokenInputDirModules []string `json:",omitempty"`
BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 6fac79d..954f8d0 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -17,6 +17,7 @@
import (
"fmt"
"sort"
+ "strconv"
"strings"
mkparser "android/soong/androidmk/parser"
@@ -68,6 +69,8 @@
"LOCAL_MODULE_PATH": prebuiltModulePath,
"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
+ "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG": invert("auto_gen_config"),
+
// composite functions
"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -621,6 +624,16 @@
return err
}
+// Assigns a given boolean value to a given variable in the result bp file. See
+// setVariable documentation for more information about prefix and name.
+func makeBlueprintBoolAssignment(ctx variableAssignmentContext, prefix, name string, value bool) error {
+ expressionValue, err := stringToBoolValue(strconv.FormatBool(value))
+ if err == nil {
+ err = setVariable(ctx.file, false, prefix, name, expressionValue, 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 {
@@ -645,7 +658,11 @@
varname := ""
fixed := ""
val := ctx.mkvalue
+
if len(val.Variables) == 1 && varLiteralName(val.Variables[0]) != "" && len(val.Strings) == 2 && val.Strings[0] == "" {
+ if varLiteralName(val.Variables[0]) == "PRODUCT_OUT" && val.Strings[1] == "/system/priv-app" {
+ return makeBlueprintBoolAssignment(ctx, "", "privileged", true)
+ }
fixed = val.Strings[1]
varname = val.Variables[0].Name.Strings[0]
// TARGET_OUT_OPTIONAL_EXECUTABLES puts the artifact in xbin, which is
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index b8316a3..aaafdc7 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -411,6 +411,24 @@
return exp, nil
}
+// If local is set to true, then the variable will be added as a part of the
+// variable at file.bpPos. For example, if file.bpPos references a module,
+// then calling this method will set a property on that module if local is set
+// to true. Otherwise, the Variable will be created at the root of the file.
+//
+// prefix should be populated with the top level value to be assigned, and
+// name with a sub-value. If prefix is empty, then name is the top level value.
+// For example, if prefix is "foo" and name is "bar" with a value of "baz", then
+// the following variable will be generated:
+//
+// foo {
+// bar: "baz"
+// }
+//
+// If prefix is the empty string and name is "foo" with a value of "bar", the
+// following variable will be generated (if it is a property):
+//
+// foo: "bar"
func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
if prefix != "" {
name = prefix + "." + name
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 81b5c30..2176361 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1645,6 +1645,51 @@
}
`,
},
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is true",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: false,
+}
+`,
+ },
+ {
+ desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is false",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := false
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ auto_gen_config: true,
+}
+`,
+ },
+ {
+ desc: "privileged app",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
+include $(BUILD_PACKAGE)
+ `,
+ expected: `
+android_app {
+ name: "foo",
+ privileged: true
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index aac4c4e..8030326 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -24,14 +24,24 @@
// A MakeString is a string that may contain variable substitutions in it.
// It can be considered as an alternating list of raw Strings and variable
// substitutions, where the first and last entries in the list must be raw
-// Strings (possibly empty). A MakeString that starts with a variable
-// will have an empty first raw string, and a MakeString that ends with a
-// variable will have an empty last raw string. Two sequential Variables
-// will have an empty raw string between them.
+// Strings (possibly empty). The entirety of the text before the first variable,
+// between two variables, and after the last variable will be considered a
+// single String value. A MakeString that starts with a variable will have an
+// empty first raw string, and a MakeString that ends with a variable will have
+// an empty last raw string. Two sequential Variables will have an empty raw
+// string between them.
//
// The MakeString is stored as two lists, a list of raw Strings and a list
// of Variables. The raw string list is always one longer than the variable
// list.
+//
+// For example, "$(FOO)/bar/baz" will be represented as the
+// following lists:
+//
+// {
+// Strings: ["", "/bar/baz"],
+// Variables: ["FOO"]
+// }
type MakeString struct {
StringPos Pos
Strings []string
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index d24efc1..fb6be38 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -222,7 +222,7 @@
if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
d = "el" + d
p.ignoreSpaces()
- expression = p.parseExpression()
+ expression = p.parseExpression('#')
expression.TrimRightSpaces()
} else {
p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
@@ -232,7 +232,7 @@
expression, endPos = p.parseDefine()
default:
p.ignoreSpaces()
- expression = p.parseExpression()
+ expression = p.parseExpression('#')
}
p.nodes = append(p.nodes, &Directive{
@@ -338,9 +338,6 @@
value.appendString(`\` + string(p.tok))
}
p.accept(p.tok)
- case '#':
- p.parseComment()
- break loop
case '$':
var variable Variable
variable = p.parseVariable()
@@ -522,7 +519,7 @@
// non-whitespace character after the = until the end of the logical line,
// which may included escaped newlines
p.accept('=')
- value := p.parseExpression()
+ value := p.parseExpression('#')
value.TrimLeftSpaces()
if ident.EndsWith('+') && t == "=" {
ident.TrimRightOne()
diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go
index f562c29..9efebf8 100644
--- a/androidmk/parser/parser_test.go
+++ b/androidmk/parser/parser_test.go
@@ -34,6 +34,56 @@
},
},
},
+ {
+ name: "Simple warning",
+ in: `$(warning A warning)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("warning A warning", NoPos),
+ },
+ },
+ },
+ {
+ name: "Warning with #",
+ in: `$(warning # A warning)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("warning # A warning", NoPos),
+ },
+ },
+ },
+ {
+ name: "Findstring with #",
+ in: `$(findstring x,x a #)`,
+ out: []Node{
+ &Variable{
+ Name: SimpleMakeString("findstring x,x a #", NoPos),
+ },
+ },
+ },
+ {
+ name: "If statement",
+ in: `ifeq (a,b) # comment
+endif`,
+ out: []Node{
+ &Directive{
+ NamePos: NoPos,
+ Name: "ifeq",
+ Args: SimpleMakeString("(a,b) ", NoPos),
+ EndPos: NoPos,
+ },
+ &Comment{
+ CommentPos: NoPos,
+ Comment: " comment",
+ },
+ &Directive{
+ NamePos: NoPos,
+ Name: "endif",
+ Args: SimpleMakeString("", NoPos),
+ EndPos: NoPos,
+ },
+ },
+ },
}
func TestParse(t *testing.T) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 8cca137..059b4d7 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -309,7 +309,14 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, moduleNames []string) {
+ if len(moduleNames) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
+ }
+ if len(a.requiredDeps) > 0 {
+ fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
+ }
+
var required []string
var targetRequired []string
var hostRequired []string
@@ -349,10 +356,7 @@
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
data.Entries.WriteLicenseVariables(w)
- if len(moduleNames) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
- }
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, moduleNames)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
@@ -369,8 +373,10 @@
}
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
- fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
- fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+ if a.installable() {
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", a.installedFile.String())
+ fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", a.outputFile.String()+":"+a.installedFile.String())
+ }
// Because apex writes .mk with Custom(), we need to write manually some common properties
// which are available via data.Entries
@@ -388,13 +394,7 @@
if len(a.overridableProperties.Overrides) > 0 {
fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
}
- if len(moduleNames) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
- }
- if len(a.requiredDeps) > 0 {
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
- }
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, moduleNames)
if a.mergedNotices.Merged.Valid() {
fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
diff --git a/apex/apex.go b/apex/apex.go
index fc94542..5631371 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -110,15 +110,6 @@
Multilib apexMultilibProperties
- // List of bootclasspath fragments that are embedded inside this APEX bundle.
- Bootclasspath_fragments []string
-
- // List of systemserverclasspath fragments that are embedded inside this APEX bundle.
- Systemserverclasspath_fragments []string
-
- // List of java libraries that are embedded inside this APEX bundle.
- Java_libs []string
-
// List of sh binaries that are embedded inside this APEX bundle.
Sh_binaries []string
@@ -318,6 +309,15 @@
// List of BPF programs inside this APEX bundle.
Bpfs []string
+ // List of bootclasspath fragments that are embedded inside this APEX bundle.
+ Bootclasspath_fragments []string
+
+ // List of systemserverclasspath fragments that are embedded inside this APEX bundle.
+ Systemserverclasspath_fragments []string
+
+ // List of java libraries that are embedded inside this APEX bundle.
+ Java_libs []string
+
// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
// Soong). This does not completely prevent installation of the overridden binaries, but if
// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -418,8 +418,12 @@
mergedNotices android.NoticeOutputs
// The built APEX file. This is the main product.
+ // Could be .apex or .capex
outputFile android.WritablePath
+ // The built uncompressed .apex file.
+ outputApexFile android.WritablePath
+
// The built APEX file in app bundle format. This file is not directly installed to the
// device. For an APEX, multiple app bundles are created each of which is for a specific ABI
// like arm, arm64, x86, etc. Then they are processed again (outside of the Android build
@@ -785,9 +789,6 @@
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
- ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
- ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
- ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
@@ -815,6 +816,9 @@
ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+ ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.overridableProperties.Bootclasspath_fragments...)
+ ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.overridableProperties.Systemserverclasspath_fragments...)
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.overridableProperties.Java_libs...)
if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
// regardless of the TARGET_PREFER_* setting. See b/144532908
@@ -1332,6 +1336,12 @@
case "", android.DefaultDistTag:
// This is the default dist path.
return android.Paths{a.outputFile}, nil
+ case imageApexSuffix:
+ // uncompressed one
+ if a.outputApexFile != nil {
+ return android.Paths{a.outputApexFile}, nil
+ }
+ fallthrough
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -1453,7 +1463,7 @@
for _, target := range ctx.MultiTargets() {
if target.Arch.ArchType.Multilib == "lib64" {
addDependenciesForNativeModules(ctx, ApexNativeDependencies{
- Native_shared_libs: []string{"libclang_rt.hwasan-aarch64-android"},
+ Native_shared_libs: []string{"libclang_rt.hwasan"},
Tests: nil,
Jni_libs: nil,
Binaries: nil,
@@ -1793,6 +1803,7 @@
fi := apexFileForRustLibrary(ctx, r)
fi.isJniLib = isJniLib
filesInfo = append(filesInfo, fi)
+ return true // track transitive dependencies
} else {
propertyName := "native_shared_libs"
if isJniLib {
@@ -2632,9 +2643,9 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Java_libs...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 1062e75..85bd595 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -971,6 +971,9 @@
rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+
+ apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+ ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
}
func TestApexCanUsePrivateApis(t *testing.T) {
@@ -1412,13 +1415,14 @@
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
no_libcrt: true,
nocrt: true,
stl: "none",
system_shared_libs: [],
srcs: [""],
stubs: { versions: ["1"] },
+ stem: "libclang_rt.hwasan-aarch64-android",
sanitize: {
never: true,
@@ -1431,7 +1435,7 @@
"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
})
- hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+ hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
installed := hwasan.Description("install libclang_rt.hwasan")
ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -1459,13 +1463,14 @@
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
no_libcrt: true,
nocrt: true,
stl: "none",
system_shared_libs: [],
srcs: [""],
stubs: { versions: ["1"] },
+ stem: "libclang_rt.hwasan-aarch64-android",
sanitize: {
never: true,
@@ -1479,7 +1484,7 @@
"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
})
- hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+ hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared")
installed := hwasan.Description("install libclang_rt.hwasan")
ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
@@ -6088,6 +6093,9 @@
apps: ["app"],
bpfs: ["bpf"],
prebuilts: ["myetc"],
+ bootclasspath_fragments: ["mybootclasspath_fragment"],
+ systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
+ java_libs: ["myjava_library"],
overrides: ["oldapex"],
updatable: false,
}
@@ -6098,6 +6106,9 @@
apps: ["override_app"],
bpfs: ["override_bpf"],
prebuilts: ["override_myetc"],
+ bootclasspath_fragments: ["override_bootclasspath_fragment"],
+ systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
+ java_libs: ["override_java_library"],
overrides: ["unknownapex"],
logging_parent: "com.foo.bar",
package_name: "test.overridden.package",
@@ -6156,6 +6167,72 @@
name: "override_myetc",
src: "override_myprebuilt",
}
+
+ java_library {
+ name: "bcplib",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["bcp.lib"],
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspath_fragment",
+ contents: ["bcplib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "override_bcplib",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["override.bcp.lib"],
+ }
+
+ bootclasspath_fragment {
+ name: "override_bootclasspath_fragment",
+ contents: ["override_bcplib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "systemserverlib",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspath_fragment",
+ standalone_contents: ["systemserverlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "override_systemserverlib",
+ srcs: ["a.java"],
+ apex_available: ["myapex"],
+ }
+
+ systemserverclasspath_fragment {
+ name: "override_systemserverclasspath_fragment",
+ standalone_contents: ["override_systemserverlib"],
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "myjava_library",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ }
+
+ java_library {
+ name: "override_java_library",
+ srcs: ["a.java"],
+ compile_dex: true,
+ apex_available: ["myapex"],
+ }
`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6190,6 +6267,13 @@
t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
}
+ android.AssertArrayString(t, "Bootclasspath_fragments does not match",
+ []string{"override_bootclasspath_fragment"}, apexBundle.overridableProperties.Bootclasspath_fragments)
+ android.AssertArrayString(t, "Systemserverclasspath_fragments does not match",
+ []string{"override_systemserverclasspath_fragment"}, apexBundle.overridableProperties.Systemserverclasspath_fragments)
+ android.AssertArrayString(t, "Java_libs does not match",
+ []string{"override_java_library"}, apexBundle.overridableProperties.Java_libs)
+
optFlags := apexRule.Args["opt_flags"]
ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
@@ -6204,12 +6288,18 @@
ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
+ ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_bcplib.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.myapex")
+ ensureNotContains(t, androidMk, "LOCAL_MODULE := override_java_library.pb.myapex")
ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
}
@@ -6830,7 +6920,7 @@
apex {
name: "myapex",
key: "myapex.key",
- jni_libs: ["mylib"],
+ jni_libs: ["mylib", "libfoo.rust"],
updatable: false,
}
@@ -6856,15 +6946,41 @@
stl: "none",
apex_available: [ "myapex" ],
}
+
+ rust_ffi_shared {
+ name: "libfoo.rust",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ shared_libs: ["libfoo.shared_from_rust"],
+ prefer_rlib: true,
+ apex_available: ["myapex"],
+ }
+
+ cc_library_shared {
+ name: "libfoo.shared_from_rust",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["10", "11", "12"],
+ },
+ }
+
`)
rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
// Notice mylib2.so (transitive dep) is not added as a jni_lib
- ensureEquals(t, rule.Args["opt"], "-a jniLibs mylib.so")
+ ensureEquals(t, rule.Args["opt"], "-a jniLibs libfoo.rust.so mylib.so")
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
"lib64/mylib.so",
"lib64/mylib2.so",
+ "lib64/libfoo.rust.so",
+ "lib64/libc++.so", // auto-added to libfoo.rust by Soong
+ "lib64/liblog.so", // auto-added to libfoo.rust by Soong
})
+
+ // b/220397949
+ ensureListContains(t, names(rule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so")
}
func TestApexMutatorsDontRunIfDisabled(t *testing.T) {
@@ -8661,6 +8777,86 @@
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
}
+func TestAndroidMk_RequiredDeps(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+
+ bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+ data := android.AndroidMkDataForTest(t, ctx, bundle)
+ var builder strings.Builder
+ data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += foo")
+
+ flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+ flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+ flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
+ var flattenedBuilder strings.Builder
+ flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
+ flattenedAndroidMk := flattenedBuilder.String()
+ ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES += foo")
+}
+
+func TestApexOutputFileProducer(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ ref string
+ expected_data []string
+ }{
+ {
+ name: "test_using_output",
+ ref: ":myapex",
+ expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.capex:myapex.capex"},
+ },
+ {
+ name: "test_using_apex",
+ ref: ":myapex{.apex}",
+ expected_data: []string{"out/soong/.intermediates/myapex/android_common_myapex_image/myapex.apex:myapex.apex"},
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ compressible: true,
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_test {
+ name: "`+tc.name+`",
+ srcs: ["a.java"],
+ data: ["`+tc.ref+`"],
+ }
+ `,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.CompressedApex = proptools.BoolPtr(true)
+ }))
+ javaTest := ctx.ModuleForTests(tc.name, "android_common").Module().(*java.Test)
+ data := android.AndroidMkEntriesForTest(t, ctx, javaTest)[0].EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ android.AssertStringPathsRelativeToTopEquals(t, "data", ctx.Config(), tc.expected_data, data)
+ })
+ }
+}
+
func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
preparer := android.GroupFixturePreparers(
PrepareForTestWithApexBuildComponents,
diff --git a/apex/builder.go b/apex/builder.go
index 1a1f22b..8c5f99b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -398,18 +398,8 @@
}
func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
- return java.ManifestFixer(java.ManifestFixerParams{
- Ctx: ctx,
- Manifest: androidManifestFile,
- SdkContext: nil,
- ClassLoaderContexts: nil,
- IsLibrary: false,
- UseEmbeddedNativeLibs: false,
- UsesNonSdkApis: false,
- UseEmbeddedDex: false,
- HasNoCode: false,
- TestOnly: true,
- LoggingParent: "",
+ return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
+ TestOnly: true,
})
}
@@ -618,6 +608,8 @@
implicitInputs = append(implicitInputs, androidManifestFile)
optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+ } else if a.testApex {
+ optFlags = append(optFlags, "--test_only")
}
// Determine target/min sdk version from the context
@@ -818,6 +810,9 @@
Implicits: implicits,
Args: args,
})
+ if suffix == imageApexSuffix {
+ a.outputApexFile = signedOutputFile
+ }
a.outputFile = signedOutputFile
if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
@@ -861,6 +856,10 @@
installSuffix = imageCapexSuffix
}
+ if !a.installable() {
+ a.SkipInstall()
+ }
+
// Install to $OUT/soong/{target,host}/.../apex.
a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile,
a.compatSymlinks.Paths()...)
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 4dc0e4c..5d00b0b 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -114,7 +114,7 @@
rootStaticArchives = []
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
-static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo"
+static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
if static_info_tag in providers(target):
static_info = providers(target)[static_info_tag]
ccObjectFiles = [f.path for f in static_info.objects]
@@ -149,7 +149,7 @@
rootSharedLibraries.append(path)
toc_file = ""
-toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
if toc_file_tag in providers(target):
toc_file = providers(target)[toc_file_tag].toc.path
else:
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 4bcfa61..8a171d4 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -28,6 +28,7 @@
"soong-genrule",
"soong-python",
"soong-sh",
+ "soong-starlark-format",
"soong-ui-metrics",
],
testSrcs: [
@@ -43,9 +44,16 @@
"cc_library_shared_conversion_test.go",
"cc_library_static_conversion_test.go",
"cc_object_conversion_test.go",
+ "cc_prebuilt_library_shared_test.go",
"conversion_test.go",
"filegroup_conversion_test.go",
"genrule_conversion_test.go",
+ "java_binary_host_conversion_test.go",
+ "java_import_conversion_test.go",
+ "java_library_conversion_test.go",
+ "java_library_host_conversion_test.go",
+ "java_plugin_conversion_test.go",
+ "java_proto_conversion_test.go",
"performance_test.go",
"prebuilt_etc_conversion_test.go",
"python_binary_conversion_test.go",
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 28de06c..b6095b2 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -51,6 +51,7 @@
"srcs": `["app.java"]`,
"manifest": `"AndroidManifest.xml"`,
"resource_files": `["res/res.png"]`,
+ "deps": `["//prebuilts/sdk:public_current_android_sdk_java_import"]`,
}),
}})
}
@@ -86,7 +87,49 @@
"resb/res.png",
]`,
"custom_package": `"com.google"`,
- "deps": `[":static_lib_dep"]`,
+ "deps": `[
+ "//prebuilts/sdk:public_current_android_sdk_java_import",
+ ":static_lib_dep",
+ ]`,
+ }),
+ }})
+}
+
+func TestAndroidAppArchVariantSrcs(t *testing.T) {
+ runAndroidAppTestCase(t, bp2buildTestCase{
+ description: "Android app - arch variant srcs",
+ moduleTypeUnderTest: "android_app",
+ moduleTypeUnderTestFactory: java.AndroidAppFactory,
+ filesystem: map[string]string{
+ "arm.java": "",
+ "x86.java": "",
+ "res/res.png": "",
+ "AndroidManifest.xml": "",
+ },
+ blueprint: `
+android_app {
+ name: "TestApp",
+ sdk_version: "current",
+ arch: {
+ arm: {
+ srcs: ["arm.java"],
+ },
+ x86: {
+ srcs: ["x86.java"],
+ }
+ }
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("android_binary", "TestApp", attrNameToString{
+ "srcs": `select({
+ "//build/bazel/platforms/arch:arm": ["arm.java"],
+ "//build/bazel/platforms/arch:x86": ["x86.java"],
+ "//conditions:default": [],
+ })`,
+ "manifest": `"AndroidManifest.xml"`,
+ "resource_files": `["res/res.png"]`,
+ "deps": `["//prebuilts/sdk:public_current_android_sdk_java_import"]`,
}),
}})
}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index b3bec65..1d3b105 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -559,48 +560,27 @@
return "", nil
}
- var ret string
switch propertyValue.Kind() {
case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ return starlark_fmt.PrintBool(propertyValue.Bool()), nil
case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
+ return fmt.Sprintf("%v", propertyValue.Interface()), nil
case reflect.Ptr:
return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
case reflect.Slice:
- if propertyValue.Len() == 0 {
- return "[]", nil
- }
-
- if propertyValue.Len() == 1 {
- // Single-line list for list with only 1 element
- ret += "["
- indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
+ elements := make([]string, 0, propertyValue.Len())
+ for i := 0; i < propertyValue.Len(); i++ {
+ val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
if err != nil {
return "", err
}
- ret += indexedValue
- ret += "]"
- } else {
- // otherwise, use a multiline list.
- ret += "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
+ if val != "" {
+ elements = append(elements, val)
}
- ret += makeIndent(indent)
- ret += "]"
}
+ return starlark_fmt.PrintList(elements, indent, "%s"), nil
case reflect.Struct:
// Special cases where the bp2build sends additional information to the codegenerator
@@ -611,18 +591,12 @@
return fmt.Sprintf("%q", label.Label), nil
}
- ret = "{\n"
// Sort and print the struct props by the key.
structProps := extractStructProperties(propertyValue, indent)
if len(structProps) == 0 {
return "", nil
}
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
+ return starlark_fmt.PrintDict(structProps, indent), nil
case reflect.Interface:
// TODO(b/164227191): implement pretty print for interfaces.
// Interfaces are used for for arch, multilib and target properties.
@@ -631,7 +605,6 @@
return "", fmt.Errorf(
"unexpected kind for property struct field: %s", propertyValue.Kind())
}
- return ret, nil
}
// Converts a reflected property struct value into a map of property names and property values,
@@ -736,13 +709,6 @@
return strings.ReplaceAll(s, "\"", "\\\"")
}
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
-}
-
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
name := ""
if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index f9adc78..17337f0 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -15,12 +15,13 @@
package bp2build
import (
- "android/soong/android"
- "android/soong/cc"
- "android/soong/genrule"
"fmt"
"strings"
"testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
)
const (
@@ -127,6 +128,8 @@
keep_symbols_list: ["symbol"],
none: true,
},
+ sdk_version: "current",
+ min_sdk_version: "29",
}
`,
targets: []testBazelTarget{
@@ -150,6 +153,8 @@
"keep_symbols_list": ["symbol"],
"none": True,
}`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
},
},
},
@@ -459,7 +464,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
},
include_build_directory: false,
}`,
@@ -483,7 +487,6 @@
srcs: ["foo.proto"],
static_executable: true,
proto: {
- canonical_path_from_root: false,
},
include_build_directory: false,
}`,
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 4c4953d..d84e156 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -115,6 +115,8 @@
},
},
include_build_directory: false,
+ sdk_version: "current",
+ min_sdk_version: "29",
}
`,
expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
@@ -140,6 +142,8 @@
"//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"],
"//conditions:default": [],
})`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
})
}
@@ -1998,8 +2002,7 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
- "strip_import_prefix": `""`,
+ "srcs": `["foo.proto"]`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2024,7 +2027,8 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
+ "srcs": `["foo.proto"]`,
+ "strip_import_prefix": `""`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2049,8 +2053,7 @@
}`,
expectedBazelTargets: []string{
makeBazelTarget("proto_library", "foo_proto", attrNameToString{
- "srcs": `["foo.proto"]`,
- "strip_import_prefix": `""`,
+ "srcs": `["foo.proto"]`,
}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
"deps": `[":foo_proto"]`,
}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
@@ -2071,7 +2074,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
type: "full",
},
include_build_directory: false,
@@ -2099,7 +2101,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
type: "lite",
},
include_build_directory: false,
@@ -2127,7 +2128,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2161,7 +2161,6 @@
name: "a",
srcs: [":a_fg_proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2171,7 +2170,6 @@
name: "b",
srcs: [":b_protos"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2181,7 +2179,6 @@
name: "c",
srcs: [":c-proto-srcs"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2191,7 +2188,6 @@
name: "d",
srcs: [":proto-srcs-d"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
@@ -2437,3 +2433,18 @@
},
)
}
+
+func TestCcLibraryEscapeLdflags(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ blueprint: soongCcProtoPreamble + `cc_library {
+ name: "foo",
+ ldflags: ["-Wl,--rpath,${ORIGIN}"],
+ include_build_directory: false,
+}`,
+ expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+ "linkopts": `["-Wl,--rpath,$${ORIGIN}"]`,
+ }),
+ })
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index e4cfa35..e5bb120 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -112,6 +112,8 @@
export_include_dirs: ["arch_x86_64_exported_include_dir"],
},
},
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -130,6 +132,8 @@
":lib-1",
":lib-2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index e8ba573..22c9dfe 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -136,6 +136,8 @@
"header_lib_1",
"header_lib_2"
],
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -174,6 +176,8 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
@@ -431,7 +435,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index f1684c4..be10e86 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -166,6 +166,8 @@
"header_lib_1",
"header_lib_2"
],
+ sdk_version: "current",
+ min_sdk_version: "29",
// TODO: Also support export_header_lib_headers
}`,
@@ -202,6 +204,8 @@
":whole_static_lib_1",
":whole_static_lib_2",
]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
@@ -1436,7 +1440,6 @@
name: "foo",
srcs: ["foo.proto"],
proto: {
- canonical_path_from_root: false,
export_proto_headers: true,
},
include_build_directory: false,
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 0a6c317..ea58086 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -55,6 +55,8 @@
"a/b/*.c"
],
exclude_srcs: ["a/b/exclude.c"],
+ sdk_version: "current",
+ min_sdk_version: "29",
}
`,
expectedBazelTargets: []string{
@@ -71,6 +73,8 @@
]`,
"srcs": `["a/b/c.c"]`,
"system_dynamic_deps": `[]`,
+ "sdk_version": `"current"`,
+ "min_sdk_version": `"29"`,
}),
},
})
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index dfbb265..d37a523 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -6,6 +6,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/starlark_fmt"
)
// Configurability support for bp2build.
@@ -250,10 +251,10 @@
} else if defaultValue != nil {
// Print an explicit empty list (the default value) even if the value is
// empty, to avoid errors about not finding a configuration that matches.
- ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
+ ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
}
- ret += makeIndent(indent)
+ ret += starlark_fmt.Indention(indent)
ret += "})"
return ret, nil
@@ -262,7 +263,7 @@
// prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
// with a provided key.
func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
- s := makeIndent(indent + 1)
+ s := starlark_fmt.Indention(indent + 1)
v, err := prettyPrint(value, indent+1, emitZeroValues)
if err != nil {
return "", err
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 96c12d3..91e614d 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -28,12 +28,15 @@
files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
+ files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
+
apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
if err != nil {
panic(err)
}
files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
+ files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
return files
}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 629ca9b..d65ece8 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -103,6 +103,10 @@
basename: "soong_config_variables.bzl",
},
{
+ dir: "product_config",
+ basename: "arch_configuration.bzl",
+ },
+ {
dir: "api_levels",
basename: GeneratedBuildFileName,
},
@@ -110,6 +114,10 @@
dir: "api_levels",
basename: "api_levels.json",
},
+ {
+ dir: "api_levels",
+ basename: "api_levels.bzl",
+ },
}
if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
new file mode 100644
index 0000000..61a398c
--- /dev/null
+++ b/bp2build/java_proto_conversion_test.go
@@ -0,0 +1,122 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "fmt"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func runJavaProtoTestCase(t *testing.T, tc bp2buildTestCase) {
+ t.Helper()
+ (&tc).moduleTypeUnderTest = "java_library_static"
+ (&tc).moduleTypeUnderTestFactory = java.LibraryFactory
+ runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaProto(t *testing.T) {
+ testCases := []struct {
+ protoType string
+ javaLibraryType string
+ javaLibraryNameExtension string
+ }{
+ {
+ protoType: "nano",
+ javaLibraryType: "java_nano_proto_library",
+ javaLibraryNameExtension: "java_proto_nano",
+ },
+ {
+ protoType: "micro",
+ javaLibraryType: "java_micro_proto_library",
+ javaLibraryNameExtension: "java_proto_micro",
+ },
+ {
+ protoType: "lite",
+ javaLibraryType: "java_lite_proto_library",
+ javaLibraryNameExtension: "java_proto_lite",
+ },
+ {
+ protoType: "stream",
+ javaLibraryType: "java_stream_proto_library",
+ javaLibraryNameExtension: "java_proto_stream",
+ },
+ {
+ protoType: "full",
+ javaLibraryType: "java_proto_library",
+ javaLibraryNameExtension: "java_proto",
+ },
+ }
+
+ bp := `java_library_static {
+ name: "java-protos",
+ proto: {
+ type: "%s",
+ },
+ srcs: ["a.proto"],
+}`
+
+ protoLibrary := makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+ "srcs": `["a.proto"]`,
+ })
+
+ for _, tc := range testCases {
+ javaLibraryName := fmt.Sprintf("java-protos_%s", tc.javaLibraryNameExtension)
+
+ runJavaProtoTestCase(t, bp2buildTestCase{
+ description: fmt.Sprintf("java_proto %s", tc.protoType),
+ blueprint: fmt.Sprintf(bp, tc.protoType),
+ expectedBazelTargets: []string{
+ protoLibrary,
+ makeBazelTarget(
+ tc.javaLibraryType,
+ javaLibraryName,
+ attrNameToString{
+ "deps": `[":java-protos_proto"]`,
+ }),
+ makeBazelTarget("java_library", "java-protos", attrNameToString{
+ "deps": fmt.Sprintf(`[":%s"]`, javaLibraryName),
+ }),
+ },
+ })
+ }
+}
+
+func TestJavaProtoDefault(t *testing.T) {
+ runJavaProtoTestCase(t, bp2buildTestCase{
+ description: "java_proto",
+ blueprint: `java_library_static {
+ name: "java-protos",
+ srcs: ["a.proto"],
+}
+`,
+ expectedBazelTargets: []string{
+ makeBazelTarget("proto_library", "java-protos_proto", attrNameToString{
+ "srcs": `["a.proto"]`,
+ }),
+ makeBazelTarget(
+ "java_lite_proto_library",
+ "java-protos_java_proto_lite",
+ attrNameToString{
+ "deps": `[":java-protos_proto"]`,
+ }),
+ makeBazelTarget("java_library", "java-protos", attrNameToString{
+ "deps": `[":java-protos_java_proto_lite"]`,
+ }),
+ },
+ })
+}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index a4999e5..14b2d84 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -20,7 +20,6 @@
"strings"
"android/soong/android"
- _ "android/soong/cc/config"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -74,7 +73,10 @@
Include_dirs []string
Sub_dir string
// If set to true, generate BTF debug info for maps & programs
- Btf *bool
+ Btf *bool
+ Vendor *bool
+
+ VendorInternal bool `blueprint:"mutated"`
}
type bpf struct {
@@ -85,6 +87,41 @@
objs android.Paths
}
+var _ android.ImageInterface = (*bpf)(nil)
+
+func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return !proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+ if proptools.Bool(bpf.properties.Vendor) {
+ return []string{"vendor"}
+ }
+ return nil
+}
+
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+ bpf.properties.VendorInternal = variation == "vendor"
+}
+
func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
cflags := []string{
"-nostdlibinc",
@@ -132,8 +169,8 @@
if proptools.Bool(bpf.properties.Btf) {
objStripped := android.ObjPathWithExt(ctx, "", src, "o")
ctx.Build(pctx, android.BuildParams{
- Rule: stripRule,
- Input: obj,
+ Rule: stripRule,
+ Input: obj,
Output: objStripped,
Args: map[string]string{
"stripCmd": "${config.ClangBin}/llvm-strip",
@@ -154,7 +191,12 @@
fmt.Fprintln(w)
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w)
- localModulePath := "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+ var localModulePath string
+ if bpf.properties.VendorInternal {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf"
+ } else {
+ localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+ }
if len(bpf.properties.Sub_dir) > 0 {
localModulePath += "/" + bpf.properties.Sub_dir
}
diff --git a/build_test.bash b/build_test.bash
index b6d00e2..1dc6660 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -25,7 +25,9 @@
# Products that are broken or otherwise don't work with multiproduct_kati
SKIPPED_PRODUCTS=(
+ # Both of these products are for soong-only builds, and will fail the kati stage.
mainline_sdk
+ ndk
)
# To track how long we took to startup. %N isn't supported on Darwin, but
diff --git a/cc/afdo.go b/cc/afdo.go
index d7cce77..c888213 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -32,7 +32,7 @@
var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
-const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
+const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
func getAfdoProfileProjects(config android.DeviceConfig) []string {
return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index b56d689..318cd7c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -108,6 +108,9 @@
if len(c.Properties.AndroidMkHeaderLibs) > 0 {
entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
}
+ if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+ entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+ }
entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
if c.UseVndk() {
entries.SetBool("LOCAL_USE_VNDK", true)
@@ -396,6 +399,9 @@
}
entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+ if len(test.Properties.Data_bins) > 0 {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
AndroidMkWriteTestData(test.data, entries)
@@ -554,10 +560,6 @@
func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
entries.SubName = c.baseProperties.Androidmk_suffix
-
- entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
- })
}
func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/binary.go b/cc/binary.go
index 05923b1..89e7262 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -70,6 +70,7 @@
// cc_binary produces a binary that is runnable on a device.
func BinaryFactory() android.Module {
module, _ := newBinary(android.HostAndDeviceSupported, true)
+ module.bazelHandler = &ccBinaryBazelHandler{module: module}
return module.Init()
}
@@ -219,18 +220,18 @@
func (binary *binaryDecorator) linkerInit(ctx BaseModuleContext) {
binary.baseLinker.linkerInit(ctx)
- if !ctx.toolchain().Bionic() && !ctx.toolchain().Musl() {
- if ctx.Os() == android.Linux {
- // Unless explicitly specified otherwise, host static binaries are built with -static
- // if HostStaticBinaries is true for the product configuration.
- if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
- binary.Properties.Static_executable = BoolPtr(true)
- }
- } else {
- // Static executables are not supported on Darwin or Windows
- binary.Properties.Static_executable = nil
+ if ctx.Os().Linux() && ctx.Host() {
+ // Unless explicitly specified otherwise, host static binaries are built with -static
+ // if HostStaticBinaries is true for the product configuration.
+ if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
+ binary.Properties.Static_executable = BoolPtr(true)
}
}
+
+ if ctx.Darwin() || ctx.Windows() {
+ // Static executables are not supported on Darwin or Windows
+ binary.Properties.Static_executable = nil
+ }
}
func (binary *binaryDecorator) static() bool {
@@ -440,6 +441,16 @@
// Need to determine symlinks early since some targets (ie APEX) need this
// information but will not call 'install'
+ binary.setSymlinkList(ctx)
+
+ return ret
+}
+
+func (binary *binaryDecorator) unstrippedOutputFilePath() android.Path {
+ return binary.unstrippedOutputFile
+}
+
+func (binary *binaryDecorator) setSymlinkList(ctx ModuleContext) {
for _, symlink := range binary.Properties.Symlinks {
binary.symlinks = append(binary.symlinks,
symlink+String(binary.Properties.Suffix)+ctx.toolchain().ExecutableSuffix())
@@ -456,12 +467,6 @@
binary.preferredArchSymlink = symlinkName
}
}
-
- return ret
-}
-
-func (binary *binaryDecorator) unstrippedOutputFilePath() android.Path {
- return binary.unstrippedOutputFile
}
func (binary *binaryDecorator) symlinkList() []string {
@@ -556,6 +561,28 @@
})
}
+type ccBinaryBazelHandler struct {
+ android.BazelHandler
+
+ module *Module
+}
+
+func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if ok {
+ if len(filePaths) != 1 {
+ ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
+ return false
+ }
+ outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ // TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
+ handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
+ }
+ return ok
+}
+
func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
baseAttrs := bp2BuildParseBaseProps(ctx, m)
binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
@@ -603,11 +630,13 @@
},
Features: baseAttrs.features,
+
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
Rule_class: "cc_binary",
- Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
},
android.CommonAttributes{Name: m.Name()},
attrs)
@@ -646,4 +675,6 @@
Strip stripAttributes
Features bazel.StringListAttribute
+
+ sdkAttributes
}
diff --git a/cc/binary_test.go b/cc/binary_test.go
new file mode 100644
index 0000000..8ec3871
--- /dev/null
+++ b/cc/binary_test.go
@@ -0,0 +1,51 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestCcBinaryWithBazel(t *testing.T) {
+ bp := `
+cc_binary {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//foo/bar:bar": []string{"foo"},
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+ producer := binMod.(android.OutputFileProducer)
+ outputFiles, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+ }
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+ expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+ android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 30c3c50..93d11c7 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,7 +16,6 @@
import (
"fmt"
"path/filepath"
- "regexp"
"strings"
"android/soong/android"
@@ -34,12 +33,6 @@
protoSrcPartition = "proto"
)
-var (
- // ignoring case, checks for proto or protos as an independent word in the name, whether at the
- // beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
- filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
-)
-
// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
// properties which apply to either the shared or static version of a cc_library module.
type staticOrSharedAttributes struct {
@@ -59,48 +52,36 @@
System_dynamic_deps bazel.LabelListAttribute
Enabled bazel.BoolAttribute
+
+ sdkAttributes
}
+// groupSrcsByExtension partitions `srcs` into groups based on file extension.
func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
- // Check that a module is a filegroup type
- isFilegroup := func(m blueprint.Module) bool {
- return ctx.OtherModuleType(m) == "filegroup"
- }
-
// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
// macro.
addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
m, exists := ctx.ModuleFromName(label.OriginalModuleName)
labelStr := label.Label
- if !exists || !isFilegroup(m) {
+ if !exists || !android.IsFilegroup(ctx, m) {
return labelStr, false
}
return labelStr + suffix, true
}
}
- isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
- m, exists := ctx.ModuleFromName(label.OriginalModuleName)
- labelStr := label.Label
- if !exists || !isFilegroup(m) {
- return labelStr, false
- }
- likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
- return labelStr, likelyProtos
- }
-
// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
- partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
- protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup},
+ labels := bazel.LabelPartitions{
+ protoSrcPartition: android.ProtoSrcLabelPartition,
cSrcPartition: bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
// C++ is the "catch-all" group, and comprises generated sources because we don't
// know the language of these sources until the genrule is executed.
cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
- })
+ }
- return partitioned
+ return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
}
// bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
@@ -560,6 +541,18 @@
}
}
+func bp2BuildParseSdkAttributes(module *Module) sdkAttributes {
+ return sdkAttributes {
+ Sdk_version: module.Properties.Sdk_version,
+ Min_sdk_version: module.Properties.Min_sdk_version,
+ }
+}
+
+type sdkAttributes struct {
+ Sdk_version *string
+ Min_sdk_version *string
+}
+
// Convenience struct to hold all attributes parsed from linker properties.
type linkerAttributes struct {
deps bazel.LabelListAttribute
@@ -644,7 +637,7 @@
var linkerFlags []string
if len(props.Ldflags) > 0 {
- linkerFlags = append(linkerFlags, props.Ldflags...)
+ linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
// binaries remove static flag if -shared is in the linker flags
if isBinary && android.InList("-shared", linkerFlags) {
axisFeatures = append(axisFeatures, "-static_flag")
diff --git a/cc/builder.go b/cc/builder.go
index a5e5406..525b1a1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -203,25 +203,34 @@
"clangBin", "format")
// Rule for invoking clang-tidy (a clang-based linter).
+ clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
+ blueprint.RuleParams{
+ Depfile: "$out",
+ Deps: blueprint.DepsGCC,
+ Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
+ "-MQ $tidyFile -MD -MF $out",
+ CommandDeps: []string{"$ccCmd"},
+ },
+ &remoteexec.REParams{
+ Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+ ExecStrategy: "${config.REClangTidyExecStrategy}",
+ Inputs: []string{"$in"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+ }, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
+
clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
blueprint.RuleParams{
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- // Pick bash because some machines with old /bin/sh cannot handle arrays.
- // All $cFlags and $tidyFlags should have single quotes escaped.
- // Assume no single quotes in other parameters like $in, $out, $ccCmd.
- Command: "/bin/bash -c 'SRCF=$in; TIDYF=$out; CLANGFLAGS=($cFlags); " +
- "rm -f $$TIDYF $${TIDYF}.d && " +
- "${config.CcWrapper}$ccCmd \"$${CLANGFLAGS[@]}\" -E -o /dev/null $$SRCF " +
- "-MQ $$TIDYF -MD -MF $${TIDYF}.d && " +
- "$tidyVars $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $$SRCF " +
- "-- \"$${CLANGFLAGS[@]}\" && touch $$TIDYF'",
- CommandDeps: []string{"${config.ClangBin}/clang-tidy", "$ccCmd"},
+ Command: "cp ${out}.dep ${out}.d && " +
+ "$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
+ "touch $out",
+ CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
},
&remoteexec.REParams{
Labels: map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
ExecStrategy: "${config.REClangTidyExecStrategy}",
- Inputs: []string{"$in"},
+ Inputs: []string{"$in", "${out}.dep"},
EnvironmentVariables: []string{"TIDY_TIMEOUT"},
// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
// under parallel compilation and RBE. So we assume no OutputFiles here.
@@ -230,7 +239,7 @@
// (1) New timestamps trigger clang and clang-tidy compilations again.
// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
- }, []string{"ccCmd", "cFlags", "tidyFlags", "tidyVars"}, []string{})
+ }, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -449,28 +458,26 @@
}
}
-func escapeSingleQuotes(s string) string {
- // Replace single quotes to work when embedded in a single quoted string for bash.
- // Relying on string concatenation of bash to get A'B from quoted 'A'\''B'.
- return strings.Replace(s, `'`, `'\''`, -1)
-}
-
// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs android.Paths,
flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
objFiles := make(android.Paths, len(srcFiles))
var tidyFiles android.Paths
- noTidySrcsMap := make(map[android.Path]bool)
+ noTidySrcsMap := make(map[string]bool)
var tidyVars string
if flags.tidy {
tidyFiles = make(android.Paths, 0, len(srcFiles))
for _, path := range noTidySrcs {
- noTidySrcsMap[path] = true
+ noTidySrcsMap[path.String()] = true
}
tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
if len(tidyTimeout) > 0 {
- tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+ tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
+ // add timeoutTidySrcs into noTidySrcsMap if TIDY_TIMEOUT is set
+ for _, path := range timeoutTidySrcs {
+ noTidySrcsMap[path.String()] = true
+ }
}
}
var coverageFiles android.Paths
@@ -672,26 +679,46 @@
}
// Even with tidy, some src file could be skipped by noTidySrcsMap.
- if tidy && !noTidySrcsMap[srcFile] {
+ if tidy && !noTidySrcsMap[srcFile.String()] {
tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
+ tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
tidyFiles = append(tidyFiles, tidyFile)
+ ruleDep := clangTidyDep
rule := clangTidy
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+ ruleDep = clangTidyDepRE
rule = clangTidyRE
}
+ sharedCFlags := shareFlags("cFlags", moduleFlags)
+ srcRelPath := srcFile.Rel()
+
+ // Add the .tidy.d rule
ctx.Build(pctx, android.BuildParams{
- Rule: rule,
- Description: "clang-tidy " + srcFile.Rel(),
- Output: tidyFile,
+ Rule: ruleDep,
+ Description: "clang-tidy-dep " + srcRelPath,
+ Output: tidyDepFile,
Input: srcFile,
Implicits: cFlagsDeps,
OrderOnly: pathDeps,
Args: map[string]string{
- "ccCmd": ccCmd,
- "cFlags": shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
- "tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+ "ccCmd": ccCmd,
+ "cFlags": sharedCFlags,
+ "tidyFile": tidyFile.String(),
+ },
+ })
+ // Add the .tidy rule with order only dependency on the .tidy.d file
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: "clang-tidy " + srcRelPath,
+ Output: tidyFile,
+ Input: srcFile,
+ Implicits: cFlagsDeps,
+ OrderOnly: append(android.Paths{}, tidyDepFile),
+ Args: map[string]string{
+ "cFlags": sharedCFlags,
+ "tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
"tidyVars": tidyVars, // short and not shared
},
})
diff --git a/cc/cc.go b/cc/cc.go
index 2a84f55..58ab28c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -505,6 +505,7 @@
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
+ isAfdoCompile() bool
isPgoCompile() bool
isNDKStubLibrary() bool
useClangLld(actx ModuleContext) bool
@@ -1259,6 +1260,13 @@
return false
}
+func (c *Module) isAfdoCompile() bool {
+ if afdo := c.afdo; afdo != nil {
+ return afdo.Properties.AfdoTarget != nil
+ }
+ return false
+}
+
func (c *Module) isPgoCompile() bool {
if pgo := c.pgo; pgo != nil {
return pgo.Properties.PgoCompile
@@ -1375,7 +1383,7 @@
}
func InstallToBootstrap(name string, config android.Config) bool {
- if name == "libclang_rt.hwasan-aarch64-android" {
+ if name == "libclang_rt.hwasan" {
return true
}
return isBionic(name)
@@ -1536,6 +1544,10 @@
return ctx.mod.IsVndk()
}
+func (ctx *moduleContextImpl) isAfdoCompile() bool {
+ return ctx.mod.isAfdoCompile()
+}
+
func (ctx *moduleContextImpl) isPgoCompile() bool {
return ctx.mod.isPgoCompile()
}
@@ -2795,6 +2807,8 @@
// dependency.
depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
}
+ depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+ staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
} else {
switch libDepTag.Order {
case earlyLibraryDependency:
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 31d91b6..278efa1 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2944,13 +2944,13 @@
// Check the shared version of lib2.
variant := "android_arm64_armv8-a_shared"
module := ctx.ModuleForTests("lib2", variant).Module().(*Module)
- checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++demangle", "libclang_rt.builtins"}, module)
// Check the static version of lib2.
variant = "android_arm64_armv8-a_static"
module = ctx.ModuleForTests("lib2", variant).Module().(*Module)
// libc++_static is linked additionally.
- checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins-aarch64-android"}, module)
+ checkStaticLibs(t, []string{"lib1", "libc++_static", "libc++demangle", "libclang_rt.builtins"}, module)
}
var compilerFlagsTestCases = []struct {
@@ -3943,7 +3943,6 @@
"${config.ArmGenericCflags}",
"-target",
"armv7a-linux-androideabi20",
- "-B${config.ArmGccRoot}/arm-linux-androideabi/bin",
}
expectedIncludes := []string{
diff --git a/cc/compiler.go b/cc/compiler.go
index 9dbf2d1..eb5458f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -42,6 +42,9 @@
// list of source files that should not be compiled with clang-tidy.
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ // list of source files that should not be compiled by clang-tidy when TIDY_TIMEOUT is set.
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
// list of source files that should not be used to build the C/C++ module.
// This is most useful in the arch/multilib variants to remove non-common files
Exclude_srcs []string `android:"path,arch_variant"`
@@ -403,6 +406,10 @@
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
}
+ if ctx.inRecovery() || ctx.inRamdisk() || ctx.inVendorRamdisk() {
+ flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RAMDISK__")
+ }
+
if ctx.apexVariationName() != "" {
flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_APEX__")
if ctx.Device() {
@@ -450,11 +457,9 @@
}
}
- gccPrefix := "-B" + config.ToolPath(tc)
-
- flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
- flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
- flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
+ flags.Global.CFlags = append(flags.Global.CFlags, target)
+ flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+ flags.Global.LdFlags = append(flags.Global.LdFlags, target)
hod := "Host"
if ctx.Os().Class == android.Device {
@@ -665,6 +670,7 @@
// Compile files listed in c.Properties.Srcs into objects
objs := compileObjs(ctx, buildFlags, "", srcs,
android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
pathDeps, compiler.cFlagsDeps)
if ctx.Failed() {
@@ -676,9 +682,9 @@
// Compile a list of source files into objects a specified subdirectory
func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
- srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+ srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
- return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
+ return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
}
// Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 7b7ee28..e1b0605 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -8,6 +8,7 @@
deps: [
"soong-android",
"soong-remoteexec",
+ "soong-starlark-format",
],
srcs: [
"bp2build.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 979c825..4d0ae1a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -104,19 +104,22 @@
exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
- exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
- exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
+ exportedVariableReferenceDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
+ exportedVariableReferenceDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
+ exportedVariableReferenceDictVars.Set("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
- pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
- pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
- pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
- pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
+ exportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
+ exportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
+ exportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
+ exportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
- pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
- pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
- pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
- pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
- pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
+ exportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
+ exportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
+ exportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
+ exportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
+ exportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
+
+ exportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
}
var (
@@ -128,7 +131,6 @@
}
arm64CpuVariantCflagsVar = map[string]string{
- "": "",
"cortex-a53": "${config.Arm64CortexA53Cflags}",
"cortex-a55": "${config.Arm64CortexA55Cflags}",
"cortex-a72": "${config.Arm64CortexA53Cflags}",
@@ -140,6 +142,15 @@
"exynos-m1": "${config.Arm64ExynosM1Cflags}",
"exynos-m2": "${config.Arm64ExynosM2Cflags}",
}
+
+ arm64CpuVariantLdflags = map[string]string{
+ "cortex-a53": "${config.Arm64FixCortexA53Ldflags}",
+ "cortex-a72": "${config.Arm64FixCortexA53Ldflags}",
+ "cortex-a73": "${config.Arm64FixCortexA53Ldflags}",
+ "kryo": "${config.Arm64FixCortexA53Ldflags}",
+ "exynos-m1": "${config.Arm64FixCortexA53Ldflags}",
+ "exynos-m2": "${config.Arm64FixCortexA53Ldflags}",
+ }
)
type toolchainArm64 struct {
@@ -214,12 +225,7 @@
toolchainCflags = append(toolchainCflags,
variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant))
- var extraLdflags string
- switch arch.CpuVariant {
- case "cortex-a53", "cortex-a72", "cortex-a73", "kryo", "exynos-m1", "exynos-m2":
- extraLdflags = "-Wl,--fix-cortex-a53-843419"
- }
-
+ extraLdflags := variantOrDefault(arm64CpuVariantLdflags, arch.CpuVariant)
return &toolchainArm64{
ldflags: strings.Join([]string{
"${config.Arm64Ldflags}",
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 0fe5e68..4466632 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -39,6 +39,10 @@
armLldflags = armLdflags
+ armFixCortexA8LdFlags = []string{"-Wl,--fix-cortex-a8"}
+
+ armNoFixCortexA8LdFlags = []string{"-Wl,--no-fix-cortex-a8"}
+
armArmCflags = []string{
"-fstrict-aliasing",
}
@@ -179,6 +183,9 @@
exportStringListStaticVariable("ArmLdflags", armLdflags)
exportStringListStaticVariable("ArmLldflags", armLldflags)
+ exportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
+ exportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
+
// Clang cflags
exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
exportStringListStaticVariable("ArmCflags", armCflags)
@@ -188,8 +195,8 @@
exportStringListStaticVariable("ArmArmCflags", armArmCflags)
exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
- exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
- exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
+ exportedVariableReferenceDictVars.Set("ArmArchVariantCflags", armArchVariantCflagsVar)
+ exportedVariableReferenceDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflagsVar)
// Clang arch variant cflags
exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
@@ -324,12 +331,12 @@
switch arch.CpuVariant {
case "cortex-a8", "":
// Generic ARM might be a Cortex A8 -- better safe than sorry
- fixCortexA8 = "-Wl,--fix-cortex-a8"
+ fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
default:
- fixCortexA8 = "-Wl,--no-fix-cortex-a8"
+ fixCortexA8 = "${config.ArmNoFixCortexA8LdFlags}"
}
case "armv7-a":
- fixCortexA8 = "-Wl,--fix-cortex-a8"
+ fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
case "armv8-a", "armv8-2a":
// Nothing extra for armv8-a/armv8-2a
default:
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index 982b436..73f65f5 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -22,14 +22,11 @@
"strings"
"android/soong/android"
+ "android/soong/starlark_fmt"
"github.com/google/blueprint"
)
-const (
- bazelIndent = 4
-)
-
type bazelVarExporter interface {
asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
}
@@ -41,6 +38,8 @@
exportedStringListVars = exportedStringListVariables{}
exportedStringVars = exportedStringVariables{}
exportedStringListDictVars = exportedStringListDictVariables{}
+ // Note: these can only contain references to other variables and must be printed last
+ exportedVariableReferenceDictVars = exportedVariableReferenceDictVariables{}
/// Maps containing variables that are dependent on the build config.
exportedConfigDependingVars = exportedConfigDependingVariables{}
@@ -65,6 +64,7 @@
type bazelConstant struct {
variableName string
internalDefinition string
+ sortLast bool
}
type exportedStringVariables map[string]string
@@ -73,21 +73,6 @@
m[k] = v
}
-func bazelIndention(level int) string {
- return strings.Repeat(" ", level*bazelIndent)
-}
-
-func printBazelList(items []string, indentLevel int) string {
- list := make([]string, 0, len(items)+2)
- list = append(list, "[")
- innerIndent := bazelIndention(indentLevel + 1)
- for _, item := range items {
- list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
- }
- list = append(list, bazelIndention(indentLevel)+"]")
- return strings.Join(list, "\n")
-}
-
func (m exportedStringVariables) asBazel(config android.Config,
stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
ret := make([]bazelConstant, 0, len(m))
@@ -139,7 +124,7 @@
// out through a constants struct later.
ret = append(ret, bazelConstant{
variableName: k,
- internalDefinition: printBazelList(expandedVars, 0),
+ internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
})
}
return ret
@@ -173,17 +158,6 @@
m[k] = v
}
-func printBazelStringListDict(dict map[string][]string) string {
- bazelDict := make([]string, 0, len(dict)+2)
- bazelDict = append(bazelDict, "{")
- for k, v := range dict {
- bazelDict = append(bazelDict,
- fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
- }
- bazelDict = append(bazelDict, "}")
- return strings.Join(bazelDict, "\n")
-}
-
// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
@@ -191,7 +165,37 @@
for k, dict := range m {
ret = append(ret, bazelConstant{
variableName: k,
- internalDefinition: printBazelStringListDict(dict),
+ internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
+ })
+ }
+ return ret
+}
+
+type exportedVariableReferenceDictVariables map[string]map[string]string
+
+func (m exportedVariableReferenceDictVariables) Set(k string, v map[string]string) {
+ m[k] = v
+}
+
+func (m exportedVariableReferenceDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
+ _ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
+ ret := make([]bazelConstant, 0, len(m))
+ for n, dict := range m {
+ for k, v := range dict {
+ matches, err := variableReference(v)
+ if err != nil {
+ panic(err)
+ } else if !matches.matches {
+ panic(fmt.Errorf("Expected a variable reference, got %q", v))
+ } else if len(matches.fullVariableReference) != len(v) {
+ panic(fmt.Errorf("Expected only a variable reference, got %q", v))
+ }
+ dict[k] = "_" + matches.variable
+ }
+ ret = append(ret, bazelConstant{
+ variableName: n,
+ internalDefinition: starlark_fmt.PrintDict(dict, 0),
+ sortLast: true,
})
}
return ret
@@ -204,7 +208,8 @@
config,
exportedStringListDictVars,
exportedStringListVars,
- exportedStringVars)
+ exportedStringVars,
+ exportedVariableReferenceDictVars)
}
func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
@@ -215,7 +220,12 @@
results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
}
- sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
+ sort.Slice(results, func(i, j int) bool {
+ if results[i].sortLast != results[j].sortLast {
+ return !results[i].sortLast
+ }
+ return results[i].variableName < results[j].variableName
+ })
definitions := make([]string, 0, len(results))
constants := make([]string, 0, len(results))
@@ -223,7 +233,7 @@
definitions = append(definitions,
fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
constants = append(constants,
- fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
+ fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
}
// Build the exported constants struct.
@@ -236,6 +246,32 @@
return ret
}
+type match struct {
+ matches bool
+ fullVariableReference string
+ variable string
+}
+
+func variableReference(input string) (match, error) {
+ // e.g. "${ExternalCflags}"
+ r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
+
+ matches := r.FindStringSubmatch(input)
+ if len(matches) == 0 {
+ return match{}, nil
+ }
+ if len(matches) != 2 {
+ return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
+ }
+ return match{
+ matches: true,
+ fullVariableReference: matches[0],
+ // Index 1 of FindStringSubmatch contains the subexpression match
+ // (variable name) of the capture group.
+ variable: matches[1],
+ }, nil
+}
+
// expandVar recursively expand interpolated variables in the exportedVars scope.
//
// We're using a string slice to track the seen variables to avoid
@@ -245,8 +281,6 @@
// interpolation stacks are deep (n > 1).
func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
- // e.g. "${ExternalCflags}"
- r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
// Internal recursive function.
var expandVarInternal func(string, map[string]bool) (string, error)
@@ -254,20 +288,18 @@
var ret string
remainingString := toExpand
for len(remainingString) > 0 {
- matches := r.FindStringSubmatch(remainingString)
- if len(matches) == 0 {
+ matches, err := variableReference(remainingString)
+ if err != nil {
+ panic(err)
+ }
+ if !matches.matches {
return ret + remainingString, nil
}
- if len(matches) != 2 {
- panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
- }
- matchIndex := strings.Index(remainingString, matches[0])
+ matchIndex := strings.Index(remainingString, matches.fullVariableReference)
ret += remainingString[:matchIndex]
- remainingString = remainingString[matchIndex+len(matches[0]):]
+ remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
- // Index 1 of FindStringSubmatch contains the subexpression match
- // (variable name) of the capture group.
- variable := matches[1]
+ variable := matches.variable
// toExpand contains a variable.
if _, ok := seenVars[variable]; ok {
return ret, fmt.Errorf(
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 3118df1..9a8178a 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -48,6 +48,14 @@
expectedValues: []string{"bar"},
},
{
+ description: "single level expansion with short-name for string var",
+ stringScope: exportedStringVariables{
+ "foo": "bar",
+ },
+ toExpand: "${config.foo}",
+ expectedValues: []string{"bar"},
+ },
+ {
description: "single level expansion string list var",
stringListScope: exportedStringListVariables{
"foo": []string{"bar"},
@@ -211,15 +219,11 @@
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
_a = {
- "b1": [
- "b2",
- ],
+ "b1": ["b2"],
}
_c = {
- "d1": [
- "d2",
- ],
+ "d1": ["d2"],
}
constants = struct(
@@ -228,7 +232,30 @@
)`,
},
{
- name: "sorts across types",
+ name: "exports dict with var refs",
+ vars: []bazelVarExporter{
+ exportedVariableReferenceDictVariables{
+ "a": map[string]string{"b1": "${b2}"},
+ "c": map[string]string{"d1": "${config.d2}"},
+ },
+ },
+ expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+ "b1": _b2,
+}
+
+_c = {
+ "d1": _d2,
+}
+
+constants = struct(
+ a = _a,
+ c = _c,
+)`,
+ },
+ {
+ name: "sorts across types with variable references last",
vars: []bazelVarExporter{
exportedStringVariables{
"b": "b-val",
@@ -242,31 +269,35 @@
"a": map[string][]string{"a1": []string{"a2"}},
"f": map[string][]string{"f1": []string{"f2"}},
},
+ exportedVariableReferenceDictVariables{
+ "aa": map[string]string{"b1": "${b}"},
+ "cc": map[string]string{"d1": "${config.d}"},
+ },
},
expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
_a = {
- "a1": [
- "a2",
- ],
+ "a1": ["a2"],
}
_b = "b-val"
-_c = [
- "c-val",
-]
+_c = ["c-val"]
_d = "d-val"
-_e = [
- "e-val",
-]
+_e = ["e-val"]
_f = {
- "f1": [
- "f2",
- ],
+ "f1": ["f2"],
+}
+
+_aa = {
+ "b1": _b,
+}
+
+_cc = {
+ "d1": _d,
}
constants = struct(
@@ -276,6 +307,8 @@
d = _d,
e = _e,
f = _f,
+ aa = _aa,
+ cc = _cc,
)`,
},
}
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 206bec1..5e3f7c7 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -258,8 +258,12 @@
return darwinAvailableLibraries
}
-func (t *toolchainDarwin) ToolPath() string {
- return "${config.MacToolPath}"
+func (t *toolchainDarwin) ToolchainCflags() string {
+ return "-B${config.MacToolPath}"
+}
+
+func (t *toolchainDarwin) ToolchainLdflags() string {
+ return "-B${config.MacToolPath}"
}
var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
diff --git a/cc/config/global.go b/cc/config/global.go
index 48a8b48..400be31 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -107,6 +107,9 @@
// This macro allows the bionic versioning.h to indirectly determine whether the
// option -Wunguarded-availability is on or not.
"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
+
+ // Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
+ "-ffp-contract=off",
}
commonGlobalConlyflags = []string{}
@@ -238,6 +241,8 @@
// http://b/197240255
"-Wno-unused-but-set-variable",
"-Wno-unused-but-set-parameter",
+ // http://b/215753485
+ "-Wno-bitwise-instead-of-logical",
}
// Extra cflags for external third-party projects to disable warnings that
@@ -282,8 +287,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r437112b"
- ClangDefaultShortVersion = "14.0.1"
+ ClangDefaultVersion = "clang-r445002"
+ ClangDefaultShortVersion = "14.0.2"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6320dbb..7175fdc 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "path/filepath"
"android/soong/android"
)
@@ -77,7 +76,6 @@
GccTriple() string
// GccVersion should return a real value, not a ninja reference
GccVersion() string
- ToolPath() string
IncludeFlags() string
@@ -198,10 +196,6 @@
return false
}
-func (t toolchainBase) ToolPath() string {
- return ""
-}
-
type toolchain64Bit struct {
toolchainBase
}
@@ -233,14 +227,7 @@
}
func LibclangRuntimeLibrary(t Toolchain, library string) string {
- arch := t.LibclangRuntimeLibraryArch()
- if arch == "" {
- return ""
- }
- if !t.Bionic() {
- return "libclang_rt." + library + "-" + arch
- }
- return "libclang_rt." + library + "-" + arch + "-android"
+ return "libclang_rt." + library
}
func BuiltinsRuntimeLibrary(t Toolchain) string {
@@ -283,11 +270,4 @@
return LibclangRuntimeLibrary(t, "fuzzer")
}
-func ToolPath(t Toolchain) string {
- if p := t.ToolPath(); p != "" {
- return p
- }
- return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
var inList = android.InList
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00f07ff..164e7a6 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -36,10 +37,10 @@
"": []string{
"-march=x86-64",
},
+
"broadwell": []string{
"-march=broadwell",
},
-
"haswell": []string{
"-march=core-avx2",
},
@@ -77,14 +78,6 @@
"popcnt": []string{"-mpopcnt"},
"aes_ni": []string{"-maes"},
}
-
- x86_64DefaultArchVariantFeatures = []string{
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- }
)
const (
@@ -92,8 +85,6 @@
)
func init() {
- android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
- exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
@@ -190,6 +181,11 @@
}
func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86_64ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86_64 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86_64ToolchainCflags}",
"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 29f0593..e32e1bd 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -15,6 +15,7 @@
package config
import (
+ "fmt"
"strings"
"android/soong/android"
@@ -186,6 +187,11 @@
}
func x86ToolchainFactory(arch android.Arch) Toolchain {
+ // Error now rather than having a confusing Ninja error
+ if _, ok := x86ArchVariantCflags[arch.ArchVariant]; !ok {
+ panic(fmt.Sprintf("Unknown x86 architecture version: %q", arch.ArchVariant))
+ }
+
toolchainCflags := []string{
"${config.X86ToolchainCflags}",
"${config.X86" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 43333fa..ce6836b 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -47,6 +47,7 @@
"-D_LIBCPP_HAS_MUSL_LIBC",
"-DANDROID_HOST_MUSL",
"-nostdlibinc",
+ "--sysroot /dev/null",
}
linuxLdflags = []string{
@@ -65,6 +66,7 @@
linuxMuslLdflags = []string{
"-nostdlib",
"-lgcc", "-lgcc_eh",
+ "--sysroot /dev/null",
}
// Extended cflags
@@ -194,10 +196,6 @@
return ""
}
-func (t *toolchainLinuxX86) ClangTriple() string {
- return "i686-linux-gnu"
-}
-
func (t *toolchainLinuxX86) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
}
@@ -206,10 +204,6 @@
return ""
}
-func (t *toolchainLinuxX8664) ClangTriple() string {
- return "x86_64-linux-gnu"
-}
-
func (t *toolchainLinuxX8664) Cflags() string {
return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
}
@@ -283,6 +277,10 @@
toolchainGlibc
}
+func (t *toolchainLinuxGlibcX86) ClangTriple() string {
+ return "i686-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -295,6 +293,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
}
+func (t *toolchainLinuxGlibcX8664) ClangTriple() string {
+ return "x86_64-linux-gnu"
+}
+
func (t *toolchainLinuxGlibcX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
}
@@ -356,6 +358,10 @@
toolchainMusl
}
+func (t *toolchainLinuxMuslX86) ClangTriple() string {
+ return "i686-linux-musl"
+}
+
func (t *toolchainLinuxMuslX86) Cflags() string {
return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
}
@@ -368,6 +374,10 @@
return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
}
+func (t *toolchainLinuxMuslX8664) ClangTriple() string {
+ return "x86_64-linux-musl"
+}
+
func (t *toolchainLinuxMuslX8664) Cflags() string {
return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 9daf40f..2c83211 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -15,6 +15,7 @@
package config
import (
+ "path/filepath"
"strings"
"android/soong/android"
@@ -180,6 +181,14 @@
return "${config.WindowsGccTriple}"
}
+func (t *toolchainWindows) ToolchainCflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
+func (t *toolchainWindows) ToolchainLdflags() string {
+ return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
func (t *toolchainWindows) GccVersion() string {
return windowsGccVersion
}
diff --git a/cc/coverage.go b/cc/coverage.go
index f2b5425..d0902ea 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -77,6 +77,10 @@
return deps
}
+func EnableContinuousCoverage(ctx android.BaseModuleContext) bool {
+ return ctx.DeviceConfig().ClangCoverageContinuousMode()
+}
+
func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
@@ -101,6 +105,9 @@
// Override -Wframe-larger-than. We can expect frame size increase after
// coverage instrumentation.
flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
+ if EnableContinuousCoverage(ctx) {
+ flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
+ }
}
}
@@ -152,6 +159,9 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
} else if clangCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
+ if EnableContinuousCoverage(ctx) {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
+ }
coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
diff --git a/cc/library.go b/cc/library.go
index 5d40820..035a90e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,7 +32,7 @@
"github.com/google/blueprint/pathtools"
)
-// LibraryProperties is a collection of properties shared by cc library rules.
+// LibraryProperties is a collection of properties shared by cc library rules/cc.
type LibraryProperties struct {
// local file name to pass to the linker as -unexported_symbols_list
Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -145,6 +145,8 @@
Tidy_disabled_srcs []string `android:"path,arch_variant"`
+ Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
Sanitized Sanitized `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
@@ -314,6 +316,7 @@
Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
Whole_archive_deps: *linkerAttrs.wholeArchiveDeps.Clone().Append(staticAttrs.Whole_archive_deps),
System_dynamic_deps: *linkerAttrs.systemDynamicDeps.Clone().Append(staticAttrs.System_dynamic_deps),
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
sharedCommonAttrs := staticOrSharedAttributes{
@@ -329,6 +332,7 @@
Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(sharedAttrs.Implementation_dynamic_deps),
Whole_archive_deps: *linkerAttrs.wholeArchiveDeps.Clone().Append(sharedAttrs.Whole_archive_deps),
System_dynamic_deps: *linkerAttrs.systemDynamicDeps.Clone().Append(sharedAttrs.System_dynamic_deps),
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
staticTargetAttrs := &bazelCcLibraryStaticAttributes{
@@ -403,11 +407,11 @@
staticProps := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_static",
- Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_static.bzl",
}
sharedProps := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_shared",
- Bzl_load_location: "//build/bazel/rules:cc_library_shared.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
}
ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
@@ -586,7 +590,8 @@
stripper Stripper
// For whole_static_libs
- objects Objects
+ objects Objects
+ wholeStaticLibsFromPrebuilts android.Paths
// Uses the module's name if empty, but can be overridden. Does not include
// shlib suffix.
@@ -1078,11 +1083,13 @@
srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
} else if library.shared() {
srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs,
android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+ android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
}
@@ -1343,6 +1350,7 @@
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
+ library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
fileName := ctx.ModuleName() + staticLibraryExtension
outputFile := android.PathForModuleOut(ctx, fileName)
@@ -1368,9 +1376,10 @@
if library.static() {
ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
- ReuseObjects: library.reuseObjects,
- Objects: library.objects,
+ StaticLibrary: outputFile,
+ ReuseObjects: library.reuseObjects,
+ Objects: library.objects,
+ WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
Direct(outputFile).
@@ -2474,6 +2483,7 @@
Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+ sdkAttributes: bp2BuildParseSdkAttributes(module),
}
var attrs interface{}
@@ -2552,7 +2562,7 @@
}
props := bazel.BazelTargetModuleProperties{
Rule_class: modType,
- Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+ Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 064e2b8..41ebcc7 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -117,6 +117,7 @@
Deps bazel.LabelListAttribute
Implementation_deps bazel.LabelListAttribute
System_dynamic_deps bazel.LabelListAttribute
+ sdkAttributes
}
func libraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
@@ -132,11 +133,12 @@
Deps: linkerAttrs.deps,
System_dynamic_deps: linkerAttrs.systemDynamicDeps,
Hdrs: baseAttributes.hdrs,
+ sdkAttributes: bp2BuildParseSdkAttributes(module),
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_library_headers",
- Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/library_test.go b/cc/library_test.go
index d220e19..6d5eda2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -379,3 +379,77 @@
gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
}
+
+func TestWholeStaticLibPrebuilts(t *testing.T) {
+ result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+ cc_prebuilt_library_static {
+ name: "libprebuilt",
+ srcs: ["foo.a"],
+ }
+
+ cc_library_static {
+ name: "libdirect",
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive",
+ whole_static_libs: ["libdirect"],
+ }
+
+ cc_library_static {
+ name: "libdirect_with_srcs",
+ srcs: ["bar.c"],
+ whole_static_libs: ["libprebuilt"],
+ }
+
+ cc_library_static {
+ name: "libtransitive_with_srcs",
+ srcs: ["baz.c"],
+ whole_static_libs: ["libdirect_with_srcs"],
+ }
+ `)
+
+ libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+ libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+ barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+ bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirect.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirect.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitive.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitive.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libdirectWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libdirectWithSrcs.Args["arLibs"], "foo.a")
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libdirectWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libdirectWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on foo.a",
+ libtransitiveWithSrcs.Inputs.Strings(), "foo.a")
+ android.AssertStringDoesContain(t, "missing flag for foo.a",
+ libtransitiveWithSrcs.Args["arLibs"], "foo.a")
+
+ android.AssertStringListContains(t, "missing dependency on bar.o",
+ libtransitiveWithSrcs.Inputs.Strings(), barObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for bar.o",
+ libtransitiveWithSrcs.Args["arObjs"], barObj.Output.String())
+
+ android.AssertStringListContains(t, "missing dependency on baz.o",
+ libtransitiveWithSrcs.Inputs.Strings(), bazObj.Output.String())
+ android.AssertStringDoesContain(t, "missing flag for baz.o",
+ libtransitiveWithSrcs.Args["arObjs"], bazObj.Output.String())
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index 02d7047..d4b4770 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -352,6 +352,11 @@
Objects Objects
ReuseObjects Objects
+ // A static library may contain prebuilt static libraries included with whole_static_libs
+ // that won't appear in Objects. They are transitively available in
+ // WholeStaticLibsFromPrebuilts.
+ WholeStaticLibsFromPrebuilts android.Paths
+
// This isn't the actual transitive DepSet, shared library dependencies have been
// converted into static library analogues. It is only used to order the static
// library dependencies that were specified for the current module.
diff --git a/cc/lto.go b/cc/lto.go
index 6d55579..2c274bd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -123,7 +123,7 @@
// If the module does not have a profile, be conservative and limit cross TU inline
// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
- if !ctx.isPgoCompile() {
+ if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
flags.Local.LdFlags = append(flags.Local.LdFlags,
"-Wl,-plugin-opt,-import-instr-limit=5")
}
diff --git a/cc/makevars.go b/cc/makevars.go
index b7aaaad..6752f8c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -227,7 +227,6 @@
}
clangPrefix := secondPrefix + "CLANG_" + typePrefix
- clangExtras := "-B" + config.ToolPath(toolchain)
ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
@@ -235,7 +234,6 @@
"${config.CommonGlobalCflags}",
fmt.Sprintf("${config.%sGlobalCflags}", hod),
toolchain.ToolchainCflags(),
- clangExtras,
productExtraCflags,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
@@ -248,25 +246,52 @@
toolchain.Ldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
fmt.Sprintf("${config.%sGlobalLldflags}", hod),
toolchain.Lldflags(),
toolchain.ToolchainLdflags(),
productExtraLdflags,
- clangExtras,
}, " "))
if target.Os.Class == android.Device {
- ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
- ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
- ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
+ sanitizerVariables := map[string]string{
+ "ADDRESS_SANITIZER_RUNTIME_LIBRARY": config.AddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
+ "HWADDRESS_SANITIZER_STATIC_LIBRARY": config.HWAddressSanitizerStaticLibrary(toolchain),
+ "UBSAN_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
+ "UBSAN_MINIMAL_RUNTIME_LIBRARY": config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
+ "TSAN_RUNTIME_LIBRARY": config.ThreadSanitizerRuntimeLibrary(toolchain),
+ "SCUDO_RUNTIME_LIBRARY": config.ScudoRuntimeLibrary(toolchain),
+ "SCUDO_MINIMAL_RUNTIME_LIBRARY": config.ScudoMinimalRuntimeLibrary(toolchain),
+ }
+
+ for variable, value := range sanitizerVariables {
+ ctx.Strict(secondPrefix+variable, value)
+ }
+
+ sanitizerLibs := android.SortedStringValues(sanitizerVariables)
+ var sanitizerLibStems []string
+ ctx.VisitAllModules(func(m android.Module) {
+ if !m.Enabled() {
+ return
+ }
+
+ ccModule, _ := m.(*Module)
+ if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+ return
+ }
+
+ if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
+ m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+ outputFile := ccModule.outputFile
+ if outputFile.Valid() {
+ sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+ }
+ }
+ })
+ sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+ ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
}
// This is used by external/gentoo/...
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 7efe134..fc682ff 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -279,7 +279,7 @@
func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
return compileObjs(ctx, flagsToBuilderFlags(flags), "",
- android.Paths{src}, nil, nil, nil)
+ android.Paths{src}, nil, nil, nil, nil)
}
func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
diff --git a/cc/object.go b/cc/object.go
index 24f6ed4..bd5bd45 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -133,6 +133,7 @@
Absolute_includes bazel.StringListAttribute
Stl *string
Linker_script bazel.LabelAttribute
+ sdkAttributes
}
// objectBp2Build is the bp2build converter from cc_object modules to the
@@ -191,11 +192,12 @@
Absolute_includes: compilerAttrs.absoluteIncludes,
Stl: compilerAttrs.stl,
Linker_script: linkerScript,
+ sdkAttributes: bp2BuildParseSdkAttributes(m),
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "cc_object",
- Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index c928ed9..5980319 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,6 +16,7 @@
import (
"path/filepath"
+ "strings"
"android/soong/android"
"android/soong/bazel"
@@ -188,6 +189,16 @@
TableOfContents: p.tocFile,
})
+ // TODO(b/220898484): Mainline module sdk prebuilts of stub libraries use a stub
+ // library as their source and must not be installed, but libclang_rt.* libraries
+ // have stubs because they are LLNDK libraries, but use an implementation library
+ // as their source and need to be installed. This discrepancy should be resolved
+ // without the prefix hack below.
+ if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() &&
+ !strings.HasPrefix(ctx.baseModuleName(), "libclang_rt.") {
+ ctx.Module().MakeUninstallable()
+ }
+
return outputFile
}
}
@@ -339,7 +350,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "prebuilt_library_static",
- Bzl_load_location: "//build/bazel/rules:prebuilt_library_static.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
@@ -359,7 +370,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "prebuilt_library_shared",
- Bzl_load_location: "//build/bazel/rules:prebuilt_library_shared.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(module.Name())
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 94f75fe..901f458 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -20,6 +20,7 @@
"android/soong/android"
"android/soong/bazel/cquery"
+
"github.com/google/blueprint"
)
@@ -29,6 +30,7 @@
)
func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
+ t.Helper()
result := android.GroupFixturePreparers(
prepareForPrebuiltTest,
fs.AddToFixture(),
@@ -449,3 +451,72 @@
expectedOutputFiles := []string{pathPrefix + "foo.so"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
}
+
+func TestPrebuiltStubNoinstall(t *testing.T) {
+ testFunc := func(t *testing.T, bp string) {
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
+ android.PrepareForTestWithMakevars,
+ ).RunTestWithBp(t, bp)
+
+ installRules := result.InstallMakeRulesForTesting(t)
+ var installedlibRule *android.InstallMakeRule
+ for i, rule := range installRules {
+ if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" {
+ if installedlibRule != nil {
+ t.Errorf("Duplicate install rules for %s", rule.Target)
+ }
+ installedlibRule = &installRules[i]
+ }
+ }
+ if installedlibRule == nil {
+ t.Errorf("No install rule found for installedlib")
+ return
+ }
+
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has install dependency on stub",
+ installedlibRule.Deps,
+ "out/target/product/test_device/system/lib/stublib.so")
+ android.AssertStringListDoesNotContain(t,
+ "installedlib has order-only install dependency on stub",
+ installedlibRule.OrderOnlyDeps,
+ "out/target/product/test_device/system/lib/stublib.so")
+ }
+
+ const prebuiltStublibBp = `
+ cc_prebuilt_library {
+ name: "stublib",
+ prefer: true,
+ srcs: ["foo.so"],
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ const installedlibBp = `
+ cc_library {
+ name: "installedlib",
+ shared_libs: ["stublib"],
+ }
+ `
+
+ t.Run("prebuilt without source", func(t *testing.T) {
+ testFunc(t, prebuiltStublibBp+installedlibBp)
+ })
+
+ const disabledSourceStublibBp = `
+ cc_library {
+ name: "stublib",
+ enabled: false,
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ t.Run("prebuilt with disabled source", func(t *testing.T) {
+ testFunc(t, disabledSourceStublibBp+prebuiltStublibBp+installedlibBp)
+ })
+}
diff --git a/cc/proto.go b/cc/proto.go
index f3410bc..8e6d5ed 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -177,7 +177,7 @@
func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
var ret bp2buildProtoDeps
- protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs)
+ protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
if !ok {
return ret
}
@@ -210,7 +210,7 @@
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{
Rule_class: rule_class,
- Bzl_load_location: "//build/bazel/rules:cc_proto.bzl",
+ Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
},
android.CommonAttributes{Name: name},
&protoAttrs)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 6c68822..f8661a6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -42,6 +42,7 @@
"-fno-omit-frame-pointer",
"-Wno-frame-larger-than=",
"-fsanitize-hwaddress-abi=platform",
+ "-mllvm", "-hwasan-use-after-scope=1",
}
// ThinLTO performs codegen during link time, thus these flags need to
@@ -582,20 +583,12 @@
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
- minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
- builtinsRuntimeLib := config.BuiltinsRuntimeLibrary(ctx.toolchain()) + ".a"
- builtinsRuntimePath := "${config.ClangAsanLibDir}/" + builtinsRuntimeLib
if sanitize.Properties.MinimalRuntimeDep {
flags.Local.LdFlags = append(flags.Local.LdFlags,
- minimalRuntimePath,
"-Wl,--exclude-libs,"+minimalRuntimeLib)
}
- if sanitize.Properties.BuiltinsDep {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
-
if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
return flags
}
@@ -705,28 +698,27 @@
if len(sanitize.Properties.Sanitizers) > 0 {
sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
-
flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
- if ctx.Host() {
+ flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
+
+ if ctx.toolchain().Bionic() {
+ // Bionic sanitizer runtimes have already been added as dependencies so that
+ // the right variant of the runtime will be used (with the "-android"
+ // suffix), so don't let clang the runtime library.
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
+ } else {
// Host sanitizers only link symbols in the final executable, so
// there will always be undefined symbols in intermediate libraries.
_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
- flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
- // non-Bionic toolchain prebuilts are missing UBSan's vptr and function sanitizers
- if !ctx.toolchain().Bionic() {
- flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
- }
+ // non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
}
if enableMinimalRuntime(sanitize) {
flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
- flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
- if !ctx.toolchain().Bionic() {
- flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
- }
}
if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -1195,6 +1187,36 @@
}
}
+ addStaticDeps := func(deps ...string) {
+ // If we're using snapshots, redirect to snapshot whenever possible
+ snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+ for idx, dep := range deps {
+ if lib, ok := snapshot.StaticLibs[dep]; ok {
+ deps[idx] = lib
+ }
+ }
+
+ // static executable gets static runtime libs
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, deps...)
+
+ }
+ if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
+ addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+ }
+ if c.sanitize.Properties.BuiltinsDep {
+ addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+ }
+
if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
// UBSan is supported on non-bionic linux host builds as well
@@ -1206,23 +1228,8 @@
// Note that by adding dependency with {static|shared}DepTag, the lib is
// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
if c.staticBinary() {
- deps := append(extraStaticDeps, runtimeLibrary)
- // If we're using snapshots, redirect to snapshot whenever possible
- snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
- for idx, dep := range deps {
- if lib, ok := snapshot.StaticLibs[dep]; ok {
- deps[idx] = lib
- }
- }
-
- // static executable gets static runtime libs
- depTag := libraryDependencyTag{Kind: staticLibraryDependency}
- variations := append(mctx.Target().Variations(),
- blueprint.Variation{Mutator: "link", Variation: "static"})
- if c.Device() {
- variations = append(variations, c.ImageVariation())
- }
- mctx.AddFarVariationDependencies(variations, depTag, deps...)
+ addStaticDeps(runtimeLibrary)
+ addStaticDeps(extraStaticDeps...)
} else if !c.static() && !c.Header() {
// If we're using snapshots, redirect to snapshot whenever possible
snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
@@ -1247,6 +1254,10 @@
if c.Device() {
variations = append(variations, c.ImageVariation())
}
+ if c.UseSdk() {
+ variations = append(variations,
+ blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+ }
AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
}
// static lib does not have dependency to the runtime library. The
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 0070e40..c1ca034 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -24,11 +24,7 @@
var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(`
cc_library_shared {
- name: "libclang_rt.asan-aarch64-android",
- }
-
- cc_library_shared {
- name: "libclang_rt.asan-arm-android",
+ name: "libclang_rt.asan",
}
`))
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 753d74c..9d40ad0 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -680,6 +680,9 @@
Input: in,
})
+ // binary snapshots need symlinking
+ p.setSymlinkList(ctx)
+
return outputFile
}
diff --git a/cc/testing.go b/cc/testing.go
index 3d0c10a..32f7c60 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -86,42 +86,20 @@
}
cc_prebuilt_library_static {
- name: "libclang_rt.builtins-arm-android",
+ name: "libclang_rt.builtins",
defaults: ["toolchain_libs_defaults"],
- native_bridge_supported: true,
+ host_supported: true,
+ vendor_available: true,
vendor_ramdisk_available: true,
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.builtins-aarch64-android",
- defaults: ["toolchain_libs_defaults"],
native_bridge_supported: true,
- vendor_ramdisk_available: true,
}
cc_prebuilt_library_shared {
- name: "libclang_rt.hwasan-aarch64-android",
+ name: "libclang_rt.hwasan",
defaults: ["toolchain_libs_defaults"],
}
cc_prebuilt_library_static {
- name: "libclang_rt.builtins-i686-android",
- defaults: ["toolchain_libs_defaults"],
- vendor_ramdisk_available: true,
- native_bridge_supported: true,
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: [
- "linux_bionic_supported",
- "toolchain_libs_defaults",
- ],
- native_bridge_supported: true,
- vendor_ramdisk_available: true,
- }
-
- cc_prebuilt_library_static {
name: "libunwind",
defaults: [
"linux_bionic_supported",
@@ -132,30 +110,7 @@
}
cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-arm-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-aarch64-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-i686-android",
- defaults: ["toolchain_libs_defaults"],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-x86_64-android",
- defaults: [
- "linux_bionic_supported",
- "toolchain_libs_defaults",
- ],
- }
-
- cc_prebuilt_library_static {
- name: "libclang_rt.fuzzer-x86_64",
+ name: "libclang_rt.fuzzer",
defaults: [
"linux_bionic_supported",
"toolchain_libs_defaults",
@@ -164,7 +119,12 @@
// Needed for sanitizer
cc_prebuilt_library_shared {
- name: "libclang_rt.ubsan_standalone-aarch64-android",
+ name: "libclang_rt.ubsan_standalone",
+ defaults: ["toolchain_libs_defaults"],
+ }
+
+ cc_prebuilt_library_static {
+ name: "libclang_rt.ubsan_minimal",
defaults: ["toolchain_libs_defaults"],
}
diff --git a/cc/tidy.go b/cc/tidy.go
index 1f5f56d..750e9de 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -102,6 +102,12 @@
}
flags.TidyFlags = append(flags.TidyFlags, headerFilter)
}
+ // Work around RBE bug in parsing clang-tidy flags, replace "--flag" with "-flag".
+ // Some C/C++ modules added local tidy flags like --header-filter= and --extra-arg-before=.
+ doubleDash := regexp.MustCompile("^('?)--(.*)$")
+ for i, s := range flags.TidyFlags {
+ flags.TidyFlags[i] = doubleDash.ReplaceAllString(s, "$1-$2")
+ }
// If clang-tidy is not enabled globally, add the -quiet flag.
if !ctx.Config().ClangTidy() {
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 8a17e2e..e7c05ac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -146,6 +146,7 @@
// binary flags
Symlinks []string `json:",omitempty"`
StaticExecutable bool `json:",omitempty"`
+ InstallInRoot bool `json:",omitempty"`
// dependencies
SharedLibs []string `json:",omitempty"`
@@ -320,6 +321,7 @@
// binary flags
prop.Symlinks = m.Symlinks()
prop.StaticExecutable = m.StaticExecutable()
+ prop.InstallInRoot = m.InstallInRoot()
prop.SharedLibs = m.SnapshotSharedLibs()
// static libs dependencies are required to collect the NOTICE files.
prop.StaticLibs = m.SnapshotStaticLibs()
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 645b2cc..2bb43ab 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -741,6 +741,7 @@
src: "bin",
},
},
+ symlinks: ["binfoo", "binbar"],
}
vendor_snapshot_binary {
@@ -920,7 +921,21 @@
ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
// bin is installed by bin.vendor_binary.31.arm64
- ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant).Output("bin")
+ bin64Module := ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant)
+ bin64Module.Output("bin")
+
+ // also test symlinks
+ bin64MkEntries := android.AndroidMkEntriesForTest(t, ctx, bin64Module.Module())
+ bin64KatiSymlinks := bin64MkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"]
+
+ // Either AndroidMk entries contain symlinks, or symlinks should be installed by Soong
+ for _, symlink := range []string{"binfoo", "binbar"} {
+ if inList(symlink, bin64KatiSymlinks) {
+ continue
+ }
+
+ bin64Module.Output(symlink)
+ }
// bin32 is installed by bin32.vendor_binary.31.arm64
ctx.ModuleForTests("bin32.vendor_binary.31.arm64", binary32Variant).Output("bin32")
diff --git a/cc/vndk.go b/cc/vndk.go
index c9c9f2c..bf6148b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -450,7 +450,7 @@
// Therefore, by removing the library here, we cause it to only be installed if libc
// depends on it.
func llndkLibrariesTxtFactory() android.SingletonModule {
- return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan-")
+ return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan")
}
// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 1280553..aaca1dd 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -114,6 +114,10 @@
fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
+ // Discard the PT_INTERP section so that the linker doesn't need to be passed the
+ // --no-dynamic-linker flag.
+ fmt.Fprintln(script, " /DISCARD/ : { *(.interp) }")
+
fmt.Fprintln(script, "}")
fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0577c86..7cb8ab7 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -407,10 +407,10 @@
s.Finish()
- if failures == 1 {
+ if failures.count == 1 {
log.Fatal("1 failure")
- } else if failures > 1 {
- log.Fatalf("%d failures", failures)
+ } else if failures.count > 1 {
+ log.Fatalf("%d failures %q", failures.count, failures.fails)
} else {
fmt.Fprintln(output, "Success")
}
@@ -522,19 +522,23 @@
})
}
-type failureCount int
+type failureCount struct {
+ count int
+ fails []string
+}
func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
if result.Error != nil {
- *f += 1
+ f.count += 1
+ f.fails = append(f.fails, result.Action.Description)
}
}
func (f *failureCount) Message(level status.MsgLevel, message string) {
if level >= status.ErrorLvl {
- *f += 1
+ f.count += 1
}
}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d80051c..a03a86a 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -205,14 +205,7 @@
buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
- {
- var limits syscall.Rlimit
- err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
- if err != nil {
- buildCtx.Verbosef("Failed to get file limit:", err)
- }
- buildCtx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
- }
+ setMaxFiles(buildCtx)
{
// The order of the function calls is important. The last defer function call
@@ -228,7 +221,6 @@
}
defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
defer met.Dump(soongMetricsFile)
- defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
}
// Read the time at the starting point.
@@ -614,3 +606,24 @@
}
}
}
+
+func setMaxFiles(ctx build.Context) {
+ var limits syscall.Rlimit
+
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to get file limit:", err)
+ return
+ }
+
+ ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
+ if limits.Cur == limits.Max {
+ return
+ }
+
+ limits.Cur = limits.Max
+ err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
+ if err != nil {
+ ctx.Println("Failed to increase file limit:", err)
+ }
+}
diff --git a/dexpreopt/DEXPREOPT_IMPLEMENTATION.md b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
new file mode 100644
index 0000000..c3a1730
--- /dev/null
+++ b/dexpreopt/DEXPREOPT_IMPLEMENTATION.md
@@ -0,0 +1,258 @@
+## Dexpreopt implementation
+
+### Introduction
+
+All dexpreopted Java code falls into three categories:
+
+- bootclasspath
+- system server
+- apps and libraries
+
+Dexpreopt implementation for bootclasspath libraries (boot images) is located in
+[soong/java] (see e.g. [soong/java/dexpreopt_bootjars.go]), and install rules
+are in [make/core/dex_preopt.mk].
+
+Dexpreopt implementation for system server, libraries and apps is located in
+[soong/dexpreopt]. For the rest of this section we focus primarily on it (and
+not boot images).
+
+Dexpeopt implementation is split across the Soong part and the Make part. The
+core logic is in Soong, and Make only generates configs and scripts to pass
+information to Soong.
+
+### Global and module dexpreopt.config
+
+The build system generates a global JSON dexpreopt config that is populated from
+product variables. This is static configuration that is passed to both Soong and
+Make. The `$OUT/soong/dexpreopt.config` file is generated in
+[make/core/dex_preopt_config.mk]. Soong reads it in [soong/dexpreopt/config.go]
+and makes a device-specific copy (this is needed to ensure incremental build
+correctness). The global config contains lists of bootclasspath jars, system
+server jars, dex2oat options, global switches that enable and disable parts of
+dexpreopt and so on.
+
+The build system also generates a module config for each dexpreopted package. It
+contains package-specific configuration that is derived from the global
+configuration and Android.bp or Android.mk module for the package.
+
+Module configs for Make packages are generated in
+[make/core/dex_preopt_odex_install.mk]; they are materialized as per-package
+JSON dexpreopt.config files.
+
+Module configs in Soong are not materialized as dexpreopt.config files and exist
+as Go structures in memory, unless it is necessary to materialize them as a file
+for dependent Make packages or for post-dexpreopting. Module configs are defined
+in [soong/dexpreopt/config.go].
+
+### Dexpreopt in Soong
+
+The Soong implementation of dexpreopt consists roughly of the following steps:
+
+- Read global dexpreopt config passed from Make ([soong/dexpreopt/config.go]).
+
+- Construct a static boot image config ([soong/java/dexpreopt_config.go]).
+
+- During dependency mutator pass, for each suitable module:
+ - add uses-library dependencies (e.g. for apps: [soong/java/app.go:deps])
+
+- During rule generation pass, for each suitable module:
+ - compute transitive uses-library dependency closure
+ ([soong/java/java.go:addCLCFromDep])
+
+ - construct CLC from the dependency closure
+ ([soong/dexpreopt/class_loader_context.go])
+
+ - construct module config with CLC, boot image locations, etc.
+ ([soong/java/dexpreopt.go])
+
+ - generate build rules to verify build-time CLC against the manifest (e.g.
+ for apps: [soong/java/app.go:verifyUsesLibraries])
+
+ - generate dexpreopt build rule ([soong/dexpreopt/dexpreopt.go])
+
+- At the end of rule generation pass:
+ - generate build rules for boot images ([soong/java/dexpreopt_bootjars.go],
+ [soong/java/bootclasspath_fragment.go] and
+ [soong/java/platform_bootclasspath.go])
+
+### Dexpreopt in Make - dexpreopt_gen
+
+In order to reuse the same dexpreopt implementation for both Soong and Make
+packages, part of Soong is compiled into a standalone binary dexpreopt_gen. It
+runs during the Ninja stage of the build and generates shell scripts with
+dexpreopt build rules for Make packages, and then executes them.
+
+This setup causes many inconveniences. To name a few:
+
+- Errors in the build rules are only revealed at the late stage of the build.
+
+- These rules are not tested by the presubmit builds that run `m nothing` on
+ many build targets/products.
+
+- It is impossible to find dexpreopt build rules in the generated Ninja files.
+
+However all these issues are a lesser evil compared to having a duplicate
+dexpreopt implementation in Make. Also note that it would be problematic to
+reimplement the logic in Make anyway, because Android.mk modules are not
+processed in the order of uses-library dependencies and propagating dependency
+information from one module to another would require a similar workaround with
+a script.
+
+Dexpreopt for Make packages involves a few steps:
+
+- At Soong phase (during `m nothing`), see dexpreopt_gen:
+ - generate build rules for dexpreopt_gen binary
+
+- At Make/Kati phase (during `m nothing`), see
+ [make/core/dex_preopt_odex_install.mk]:
+ - generate build rules for module dexpreopt.config
+
+ - generate build rules for merging dependency dexpreopt.config files (see
+ [make/core/dex_preopt_config_merger.py])
+
+ - generate build rules for dexpreopt_gen invocation
+
+ - generate build rules for executing dexpreopt.sh scripts
+
+- At Ninja phase (during `m`):
+ - generate dexpreopt.config files
+
+ - execute dexpreopt_gen rules (generate dexpreopt.sh scripts)
+
+ - execute dexpreopt.sh scripts (this runs the actual dexpreopt)
+
+The Make/Kati phase adds all the necessary dependencies that trigger
+dexpreopt_gen and dexpreopt.sh rules. The real dexpreopt command (dex2oat
+invocation that will be executed to AOT-compile a package) is in the
+dexpreopt.sh script, which is generated close to the end of the build.
+
+### Indirect build rules
+
+The process described above for Make packages involves "indirect build rules",
+i.e. build rules that are generated not at the time when the build system is
+created (which is a small step at the very beginning of the build triggered with
+`m nothing`), but at the time when the actual build is done (`m` phase).
+
+Some build systems, such as Make, allow modifications of the build graph during
+the build. Other build systems, such as Soong, have a clear separation into the
+first "generation phase" (this is when build rules are created) and the second
+"build phase" (this is when the build rules are executed), and they do not allow
+modifications of the dependency graph during the second phase. The Soong
+approach is better from performance standpoint, because with the Make approach
+there are no guarantees regarding the time of the build --- recursive build
+graph modfications continue until fixpoint. However the Soong approach is also
+more restictive, as it can only generate build rules from the information that
+is passed to the build system via global configuration, Android.bp files or
+encoded in the Go code. Any other information (such as the contents of the Java
+manifest files) are not accessible and cannot be used to generate build rules.
+
+Hence the need for the "indirect build rules": during the generation phase only
+stubs of the build rules are generated, and the real rules are generated by the
+stub rules during the build phase (and executed immediately). Note that the
+build system still has to add all the necessary dependencies during the
+generation phase, because it will not be possible to change build order during
+the build phase.
+
+Indirect buils rules are used in a couple of places in dexpreopt:
+
+- [soong/scripts/manifest_check.py]: first to extract targetSdkVersion from the
+ manifest, and later to extract `<uses-library/>` tags from the manifest and
+ compare them to the uses-library list known to the build system
+
+- [soong/scripts/construct_context.py]: to trim compatibility libraries in CLC
+
+- [make/core/dex_preopt_config_merger.py]: to merge information from
+ dexpreopt.config files for uses-library dependencies into the dependent's
+ dexpreopt.config file (mostly the CLC)
+
+- autogenerated dexpreopt.sh scripts: to call dexpreopt_gen
+
+### Consistency check - manifest_check.py
+
+Because the information from the manifests has to be duplicated in the
+Android.bp/Android.mk files, there is a danger that it may get out of sync. To
+guard against that, the build system generates a rule that verifies
+uses-libraries: checks the metadata in the build files against the contents of a
+manifest. The manifest can be available as a source file, or as part of a
+prebuilt APK.
+
+The check is implemented in [soong/scripts/manifest_check.py].
+
+It is possible to turn off the check globally for a product by setting
+`PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true` in a product makefile, or for a
+particular build by setting `RELAX_USES_LIBRARY_CHECK=true`.
+
+### Compatibility libraries - construct_context.py
+
+Compatibility libraries are libraries that didn’t exist prior to a certain SDK
+version (say, `N`), but classes in them were in the bootclasspath jars, etc.,
+and in version `N` they have been separated into a standalone uses-library.
+Compatibility libraries should only be in the CLC of an app if its
+`targetSdkVersion` in the manifest is less than `N`.
+
+Currently compatibility libraries only affect apps (but not other libraries).
+
+The build system cannot see `targetSdkVersion` of an app at the time it
+generates dexpreopt build rules, so it doesn't know whether to add compatibility
+libaries to CLC or not. As a workaround, the build system includes all
+compatibility libraries regardless of the app version, and appends some extra
+logic to the dexpreopt rule that will extract `targetSdkVersion` from the
+manifest and filter CLC based on that version during Ninja stage of the build,
+immediately before executing the dexpreopt command (see the
+soong/scripts/construct_context.py script).
+
+As of the time of writing (January 2022), there are the following compatibility
+libraries:
+
+- org.apache.http.legacy (SDK 28)
+- android.hidl.base-V1.0-java (SDK 29)
+- android.hidl.manager-V1.0-java (SDK 29)
+- android.test.base (SDK 30)
+- android.test.mock (SDK 30)
+
+### Manifest fixer
+
+Sometimes uses-library tags are missing from the source manifest of a
+library/app. This may happen for example if one of the transitive dependencies
+of the library/app starts using another uses-library, and the library/app's
+manifest isn't updated to include it.
+
+Soong can compute some of the missing uses-library tags for a given library/app
+automatically as SDK libraries in the transitive dependency closure of the
+library/app. The closure is needed because a library/app may depend on a static
+library that may in turn depend on an SDK library (possibly transitively via
+another library).
+
+Not all uses-library tags can be computed in this way, because some of the
+uses-library dependencies are not SDK libraries, or they are not reachable via
+transitive dependency closure. But when possible, allowing Soong to calculate
+the manifest entries is less prone to errors and simplifies maintenance. For
+example, consider a situation when many apps use some static library that adds a
+new uses-library dependency -- all the apps will have to be updated. That is
+difficult to maintain.
+
+There is also a manifest merger, because sometimes the final manifest of an app
+is merged from a few dependency manifests, so the final manifest installed on
+devices contains a superset of uses-library tags of the source manifest of the
+app.
+
+
+[make/core/dex_preopt.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt.mk
+[make/core/dex_preopt_config.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config.mk
+[make/core/dex_preopt_config_merger.py]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_config_merger.py
+[make/core/dex_preopt_odex_install.mk]: https://cs.android.com/android/platform/superproject/+/master:build/make/core/dex_preopt_odex_install.mk
+[soong/dexpreopt]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt
+[soong/dexpreopt/class_loader_context.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/class_loader_context.go
+[soong/dexpreopt/config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/config.go
+[soong/dexpreopt/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/dexpreopt/dexpreopt.go
+[soong/java]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java
+[soong/java/app.go:deps]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20deps%22
+[soong/java/app.go:verifyUsesLibraries]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/app.go?q=%22func%20\(u%20*usesLibrary\)%20verifyUsesLibraries%22
+[soong/java/bootclasspath_fragment.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/bootclasspath_fragment.go
+[soong/java/dexpreopt.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt.go
+[soong/java/dexpreopt_bootjars.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_bootjars.go
+[soong/java/dexpreopt_config.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/dexpreopt_config.go
+[soong/java/java.go:addCLCFromDep]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/java.go?q=%22func%20addCLCfromDep%22
+[soong/java/platform_bootclasspath.go]: https://cs.android.com/android/platform/superproject/+/master:build/soong/java/platform_bootclasspath.go
+[soong/scripts/construct_context.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/construct_context.py
+[soong/scripts/manifest_check.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/scripts/manifest_check.py
diff --git a/docs/rbe.json b/docs/rbe.json
new file mode 100644
index 0000000..f6ff107
--- /dev/null
+++ b/docs/rbe.json
@@ -0,0 +1,24 @@
+{
+ "env": {
+ "USE_RBE": "1",
+
+ "RBE_R8_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_CXX_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_D8_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_JAVAC_EXEC_STRATEGY": "remote_local_fallback",
+ "RBE_JAVAC": "1",
+ "RBE_R8": "1",
+ "RBE_D8": "1",
+
+ "RBE_instance": "[replace with your RBE instance]",
+ "RBE_service": "[replace with your RBE service endpoint]",
+
+ "RBE_DIR": "prebuilts/remoteexecution-client/live",
+
+ "RBE_use_application_default_credentials": "true",
+
+ "RBE_log_dir": "/tmp",
+ "RBE_output_dir": "/tmp",
+ "RBE_proxy_log_dir": "/tmp"
+ }
+}
diff --git a/docs/rbe.md b/docs/rbe.md
new file mode 100644
index 0000000..cfe86d7
--- /dev/null
+++ b/docs/rbe.md
@@ -0,0 +1,70 @@
+# Build Android Platform on Remote Build Execution
+
+Soong is integrated with Google's Remote Build Execution(RBE) service, which
+implements the
+[Remote Executaion API](https://github.com/bazelbuild/remote-apis).
+
+With RBE enabled, it can speed up the Android Platform builds by distributing
+build actions through a worker pool sharing a central cache of build results.
+
+## Configuration
+
+To enable RBE, you need to set several environment variables before triggering
+the build. You can set them through a
+[environment variables config file](https://android.googlesource.com/platform/build/soong/+/master/README.md#environment-variables-config-file).
+As an example, [build/soong/docs/rbe.json](rbe.json) is a config that enables
+RBE in the build. Once the config file is created, you need to let Soong load
+the config file by specifying `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment
+variable and `ANDROID_BUILD_ENVIRONMENT_CONFIG` environment variable. The
+following command starts Soong with [build/soong/docs/rbe.json](rbe.json)
+loaded:
+
+```shell
+ANDROID_BUILD_ENVIRONMENT_CONFIG=rbe \
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong/doc \
+ build/soong/soong_ui.bash
+```
+
+### Configuration Explanation
+
+Below a brief explanation of each field in
+[build/soong/docs/rbe.json](rbe.json):
+
+##### USE\_RBE:
+If set to 1, enable RBE for the build.
+
+##### RBE\_CXX\_EXEC\_STRATEGY / RBE\_JAVAC\_EXEC\_STRATEGY / RBE\_R8\_EXEC\_STRATEGY / RBE\_D8\_EXEC\_STRATEGY:
+
+Sets strategies for C++/javac/r8/d8 action types. Available options are
+(**Note**: all options will update the remote cache if the right permissions to
+update cache are given to the user.):
+
+* **local**: Only execute locally.
+* **remote**: Only execute remotely.
+* **remote_local_fallback**: Try executing remotely and fall back to local
+ execution if failed.
+* **racing**: Race remote execution and local execution and use the earlier
+ result.
+
+##### RBE\_JAVAC / RBE\_R8 / RBE\_D8
+
+If set to 1, enable javac/r8/d8 support. C++ compilation is enabled by default.
+
+##### RBE\_service / RBE\_instance
+
+The remote execution service endpoint and instance ID to target when calling
+remote execution via gRPC to execute actions.
+
+##### RBE\_DIR
+
+Where to find remote client binaries (rewrapper, reproxy)
+
+##### RBE\_use\_application\_default\_credentials
+
+reclient uses
+[application default credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login)
+for autentication, as generated by `gcloud auth application-default login`
+
+##### RBE\_log\_dir/RBE\_proxy\_log\_dir/RBE\_output\_dir
+
+Logs generated by rewrapper and reproxy will go here.
diff --git a/docs/tidy.md b/docs/tidy.md
index 3140198..890c3a0 100644
--- a/docs/tidy.md
+++ b/docs/tidy.md
@@ -225,6 +225,16 @@
in several Android continuous builds where `WITH_TIDY=1` and
`CLANG_ANALYZER_CHECKS=1` are set.
+Similar to `tidy_disabled_srcs` a `tidy_timeout_srcs` list
+can be used to include all source files that took too much time to compile
+with clang-tidy. Files listed in `tidy_timeout_srcs` will not
+be compiled by clang-tidy when `TIDY_TIMEOUT` is defined.
+This can save global build time, when it is necessary to set some
+time limit globally to finish in an acceptable time.
+For developers who want to find all clang-tidy warnings and
+are willing to spend more time on all files in a project,
+they should not define `TIDY_TIMEOUT` and build only the wanted project directories.
+
## Capabilities for Android.bp and Android.mk
Some of the previously mentioned features are defined only
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..7489840
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "ENV_VAR_1": "Value1",
+ "ENV_VAR_2": "Value2"
+ }
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 1679a57..c52ddee 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -294,7 +294,7 @@
if _, exists := locationLabels[label]; !exists {
locationLabels[label] = loc
} else {
- ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+ ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)",
label, locationLabels[label], loc)
}
}
@@ -382,9 +382,12 @@
addLocationLabel(toolFile, toolLocation{paths})
}
+ includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
var srcFiles android.Paths
for _, in := range g.properties.Srcs {
- paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
+ paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
+ Context: ctx, Paths: []string{in}, ExcludePaths: g.properties.Exclude_srcs, IncludeDirs: includeDirInPaths,
+ })
if len(missingDeps) > 0 {
if !ctx.Config().AllowMissingDependencies() {
panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
@@ -496,7 +499,7 @@
}
return paths[0], nil
} else {
- return reportError("unknown location label %q", label)
+ return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
}
} else if strings.HasPrefix(name, "locations ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
@@ -507,7 +510,7 @@
}
return strings.Join(paths, " "), nil
} else {
- return reportError("unknown locations label %q", label)
+ return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
}
} else {
return reportError("unknown variable '$(%s)'", name)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 04c97fd..1b5cef2 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -341,7 +341,7 @@
out: ["out"],
cmd: "echo foo > $(location missing)",
`,
- err: `unknown location label "missing"`,
+ err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
},
{
name: "error locations",
@@ -349,7 +349,7 @@
out: ["out"],
cmd: "echo foo > $(locations missing)",
`,
- err: `unknown locations label "missing"`,
+ err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
},
{
name: "error location no files",
diff --git a/java/Android.bp b/java/Android.bp
index c062941..4bcae4f 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -81,6 +81,7 @@
"app_test.go",
"bootclasspath_fragment_test.go",
"device_host_converter_test.go",
+ "dex_test.go",
"dexpreopt_test.go",
"dexpreopt_bootjars_test.go",
"droiddoc_test.go",
diff --git a/java/aar.go b/java/aar.go
index 51aad8d..8e10253 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -280,9 +280,7 @@
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
- manifestPath := ManifestFixer(ManifestFixerParams{
- Ctx: ctx,
- Manifest: manifestSrcPath,
+ manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
SdkContext: sdkContext,
ClassLoaderContexts: classLoaderContexts,
IsLibrary: a.isLibrary,
@@ -290,7 +288,6 @@
UsesNonSdkApis: a.usesNonSdkApis,
UseEmbeddedDex: a.useEmbeddedDex,
HasNoCode: a.hasNoCode,
- TestOnly: false,
LoggingParent: a.LoggingParent,
})
diff --git a/java/android_manifest.go b/java/android_manifest.go
index a5d5b97..7772b70 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -56,8 +56,6 @@
}
type ManifestFixerParams struct {
- Ctx android.ModuleContext
- Manifest android.Path
SdkContext android.SdkContext
ClassLoaderContexts dexpreopt.ClassLoaderContextMap
IsLibrary bool
@@ -70,20 +68,21 @@
}
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func ManifestFixer(params ManifestFixerParams) android.Path {
+func ManifestFixer(ctx android.ModuleContext, manifest android.Path,
+ params ManifestFixerParams) android.Path {
var args []string
if params.IsLibrary {
args = append(args, "--library")
} else if params.SdkContext != nil {
- minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersion(params.Ctx)
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
if err != nil {
- params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
if minSdkVersion.FinalOrFutureInt() >= 23 {
args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs))
} else if params.UseEmbeddedNativeLibs {
- params.Ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
+ ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
minSdkVersion)
}
}
@@ -124,38 +123,38 @@
var argsMapper = make(map[string]string)
if params.SdkContext != nil {
- targetSdkVersion := targetSdkVersionForManifestFixer(params.Ctx, params.SdkContext)
+ targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext)
args = append(args, "--targetSdkVersion ", targetSdkVersion)
- if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" {
- targetSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String())
- deps = append(deps, ApiFingerprintPath(params.Ctx))
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+ targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+ deps = append(deps, ApiFingerprintPath(ctx))
}
- minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersionString(params.Ctx)
+ minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
if err != nil {
- params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
- if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" {
- minSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String())
- deps = append(deps, ApiFingerprintPath(params.Ctx))
+ if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+ minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+ deps = append(deps, ApiFingerprintPath(ctx))
}
if err != nil {
- params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+ ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
}
args = append(args, "--minSdkVersion ", minSdkVersion)
args = append(args, "--raise-min-sdk-version")
}
- fixedManifest := android.PathForModuleOut(params.Ctx, "manifest_fixer", "AndroidManifest.xml")
+ fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
argsMapper["args"] = strings.Join(args, " ")
- params.Ctx.Build(pctx, android.BuildParams{
+ ctx.Build(pctx, android.BuildParams{
Rule: manifestFixerRule,
Description: "fix manifest",
- Input: params.Manifest,
+ Input: manifest,
Implicits: deps,
Output: fixedManifest,
Args: argsMapper,
diff --git a/java/app.go b/java/app.go
index e4432ff..96fd61a 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1443,7 +1443,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "android_app_certificate",
- Bzl_load_location: "//build/bazel/rules:android_app_certificate.bzl",
+ Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
}
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -1451,9 +1451,11 @@
type bazelAndroidAppAttributes struct {
*javaLibraryAttributes
- Manifest bazel.Label
- Custom_package *string
- Resource_files bazel.LabelListAttribute
+ Manifest bazel.Label
+ Custom_package *string
+ Resource_files bazel.LabelListAttribute
+ Certificate *bazel.Label
+ Certificate_name *string
}
// ConvertWithBp2build is used to convert android_app to Bazel.
@@ -1470,15 +1472,30 @@
resourceFiles.Includes = append(resourceFiles.Includes, files...)
}
+ var certificate *bazel.Label
+ certificateNamePtr := a.overridableAppProperties.Certificate
+ certificateName := proptools.StringDefault(certificateNamePtr, "")
+ certModule := android.SrcIsModule(certificateName)
+ if certModule != "" {
+ c := android.BazelLabelForModuleDepSingle(ctx, certificateName)
+ certificate = &c
+ certificateNamePtr = nil
+ }
+
attrs := &bazelAndroidAppAttributes{
libAttrs,
android.BazelLabelForModuleSrcSingle(ctx, manifest),
// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
a.overridableAppProperties.Package_name,
bazel.MakeLabelListAttribute(resourceFiles),
+ certificate,
+ certificateNamePtr,
}
- props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary",
- Bzl_load_location: "@rules_android//rules:rules.bzl"}
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "android_binary",
+ Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
+ }
ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
diff --git a/java/base.go b/java/base.go
index 42d7733..2f425cd 100644
--- a/java/base.go
+++ b/java/base.go
@@ -227,6 +227,12 @@
// whether to generate Binder#GetTransaction name method.
Generate_get_transaction_name *bool
+ // whether all interfaces should be annotated with required permissions.
+ Enforce_permissions *bool
+
+ // allowlist for interfaces that (temporarily) do not require annotation for permissions.
+ Enforce_permissions_exceptions []string `android:"path"`
+
// list of flags that will be passed to the AIDL compiler
Flags []string
}
@@ -418,7 +424,8 @@
outputFile android.Path
extraOutputFiles android.Paths
- exportAidlIncludeDirs android.Paths
+ exportAidlIncludeDirs android.Paths
+ ignoredAidlPermissionList android.Paths
logtagsSrcs android.Paths
@@ -474,6 +481,8 @@
sdkVersion android.SdkSpec
minSdkVersion android.SdkSpec
maxSdkVersion android.SdkSpec
+
+ sourceExtensions []string
}
func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -772,6 +781,17 @@
return hasSrcExt(j.properties.Srcs, ext)
}
+func (j *Module) individualAidlFlags(ctx android.ModuleContext, aidlFile android.Path) string {
+ var flags string
+
+ if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+ if !android.InList(aidlFile.String(), j.ignoredAidlPermissionList.Strings()) {
+ flags = "-Wmissing-permission-annotation -Werror"
+ }
+ }
+ return flags
+}
+
func (j *Module) aidlFlags(ctx android.ModuleContext, aidlPreprocess android.OptionalPath,
aidlIncludeDirs android.Paths) (string, android.Paths) {
@@ -814,6 +834,11 @@
flags = append(flags, "--transaction_names")
}
+ if Bool(j.deviceProperties.Aidl.Enforce_permissions) {
+ exceptions := j.deviceProperties.Aidl.Enforce_permissions_exceptions
+ j.ignoredAidlPermissionList = android.PathsForModuleSrcExcludes(ctx, exceptions, nil)
+ }
+
aidlMinSdkVersion := j.MinSdkVersion(ctx).ApiLevel.String()
flags = append(flags, "--min_sdk_version="+aidlMinSdkVersion)
@@ -839,7 +864,7 @@
}
errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
- flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+ flags.errorProneExtraJavacFlags = "${config.ErrorProneHeapFlags} ${config.ErrorProneFlags} " +
"'" + strings.Join(errorProneFlags, " ") + "'"
flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
}
@@ -847,6 +872,7 @@
// classpath
flags.bootClasspath = append(flags.bootClasspath, deps.bootClasspath...)
flags.classpath = append(flags.classpath, deps.classpath...)
+ flags.dexClasspath = append(flags.dexClasspath, deps.dexClasspath...)
flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
flags.processorPath = append(flags.processorPath, deps.processorPath...)
flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...)
@@ -959,6 +985,14 @@
return flags
}
+func (j *Module) AddJSONData(d *map[string]interface{}) {
+ (&j.ModuleBase).AddJSONData(d)
+ (*d)["Java"] = map[string]interface{}{
+ "SourceExtensions": j.sourceExtensions,
+ }
+
+}
+
func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
@@ -970,6 +1004,12 @@
}
srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+ j.sourceExtensions = []string{}
+ for _, ext := range []string{".kt", ".proto", ".aidl", ".java", ".logtags"} {
+ if hasSrcExt(srcFiles.Strings(), ext) {
+ j.sourceExtensions = append(j.sourceExtensions, ext)
+ }
+ }
if hasSrcExt(srcFiles.Strings(), ".proto") {
flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
}
@@ -1051,6 +1091,8 @@
flags.classpath = append(flags.classpath, deps.kotlinStdlib...)
flags.classpath = append(flags.classpath, deps.kotlinAnnotations...)
+ flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...)
+
flags.kotlincClasspath = append(flags.kotlincClasspath, flags.bootClasspath...)
flags.kotlincClasspath = append(flags.kotlincClasspath, flags.classpath...)
@@ -1079,6 +1121,8 @@
// Jar kotlin classes into the final jar after javac
if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
+ } else {
+ flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...)
}
}
@@ -1803,6 +1847,7 @@
} else if sdkDep.useFiles {
// sdkDep.jar is actually equivalent to turbine header.jar.
deps.classpath = append(deps.classpath, sdkDep.jars...)
+ deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...)
deps.aidlPreprocess = sdkDep.aidl
} else {
deps.aidlPreprocess = sdkDep.aidl
@@ -1827,7 +1872,9 @@
if dep, ok := module.(SdkLibraryDependency); ok {
switch tag {
case libTag:
- deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))...)
+ depHeaderJars := dep.SdkHeaderJars(ctx, j.SdkVersion(ctx))
+ deps.classpath = append(deps.classpath, depHeaderJars...)
+ deps.dexClasspath = append(deps.dexClasspath, depHeaderJars...)
case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
}
@@ -1846,6 +1893,7 @@
deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...)
case libTag, instrumentationForTag:
deps.classpath = append(deps.classpath, dep.HeaderJars...)
+ deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
@@ -1913,6 +1961,7 @@
case libTag:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
+ deps.dexClasspath = append(deps.classpath, dep.Srcs()...)
case staticLibTag:
checkProducesJars(ctx, dep)
deps.classpath = append(deps.classpath, dep.Srcs()...)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index a36bd6a..c3a5d5f 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "io"
"path/filepath"
"reflect"
"strings"
@@ -139,7 +140,7 @@
BootclasspathFragmentsDepsProperties
}
-type SourceOnlyBootclasspathProperties struct {
+type HiddenApiPackageProperties struct {
Hidden_api struct {
// Contains prefixes of a package hierarchy that is provided solely by this
// bootclasspath_fragment.
@@ -148,6 +149,14 @@
// hidden API flags. See split_packages property for more details.
Package_prefixes []string
+ // A list of individual packages that are provided solely by this
+ // bootclasspath_fragment but which cannot be listed in package_prefixes
+ // because there are sub-packages which are provided by other modules.
+ //
+ // This should only be used for legacy packages. New packages should be
+ // covered by a package prefix.
+ Single_packages []string
+
// The list of split packages provided by this bootclasspath_fragment.
//
// A split package is one that contains classes which are provided by multiple
@@ -207,6 +216,11 @@
}
}
+type SourceOnlyBootclasspathProperties struct {
+ HiddenApiPackageProperties
+ Coverage HiddenApiPackageProperties
+}
+
type BootclasspathFragmentModule struct {
android.ModuleBase
android.ApexModuleBase
@@ -270,6 +284,12 @@
ctx.PropertyErrorf("coverage", "error trying to append coverage specific properties: %s", err)
return
}
+
+ err = proptools.AppendProperties(&m.sourceOnlyProperties.HiddenApiPackageProperties, &m.sourceOnlyProperties.Coverage, nil)
+ if err != nil {
+ ctx.PropertyErrorf("coverage", "error trying to append hidden api coverage specific properties: %s", err)
+ return
+ }
}
// Initialize the contents property from the image_name.
@@ -588,6 +608,19 @@
// Provide the apex content info.
b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
}
+ } else {
+ // Versioned fragments are not needed by make.
+ b.HideFromMake()
+ }
+
+ // In order for information about bootclasspath_fragment modules to be added to module-info.json
+ // it is necessary to output an entry to Make. As bootclasspath_fragment modules are part of an
+ // APEX there can be multiple variants, including the default/platform variant and only one can
+ // be output to Make but it does not really matter which variant is output. The default/platform
+ // variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
+ // the last variant (ctx.FinalModule()).
+ if ctx.Module() != ctx.FinalModule() {
+ b.HideFromMake()
}
}
@@ -717,7 +750,8 @@
// TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of
// their own.
if output.SignaturePatternsPath == nil {
- output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, []string{"*"}, nil)
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
+ ctx, output.AllFlagsPath, []string{"*"}, nil, nil)
}
// Initialize a HiddenAPIInfo structure.
@@ -792,11 +826,13 @@
// signature patterns.
splitPackages := b.sourceOnlyProperties.Hidden_api.Split_packages
packagePrefixes := b.sourceOnlyProperties.Hidden_api.Package_prefixes
- if splitPackages != nil || packagePrefixes != nil {
+ singlePackages := b.sourceOnlyProperties.Hidden_api.Single_packages
+ if splitPackages != nil || packagePrefixes != nil || singlePackages != nil {
if splitPackages == nil {
splitPackages = []string{"*"}
}
- output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath, splitPackages, packagePrefixes)
+ output.SignaturePatternsPath = buildRuleSignaturePatternsFile(
+ ctx, output.AllFlagsPath, splitPackages, packagePrefixes, singlePackages)
}
return output
@@ -849,7 +885,22 @@
}
func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
- var entriesList []android.AndroidMkEntries
+ // Use the generated classpath proto as the output.
+ outputFile := b.outputFilepath
+ // Create a fake entry that will cause this to be added to the module-info.json file.
+ entriesList := []android.AndroidMkEntries{{
+ Class: "FAKE",
+ OutputFile: android.OptionalPathForPath(outputFile),
+ Include: "$(BUILD_PHONY_PACKAGE)",
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string) {
+ // Allow the bootclasspath_fragment to be built by simply passing its name on the command
+ // line.
+ fmt.Fprintln(w, ".PHONY:", b.Name())
+ fmt.Fprintln(w, b.Name()+":", outputFile.String())
+ },
+ },
+ }}
for _, install := range b.bootImageDeviceInstalls {
entriesList = append(entriesList, install.ToMakeEntries())
}
@@ -931,13 +982,13 @@
All_flags_path android.OptionalPath `supported_build_releases:"S"`
// The path to the generated signature-patterns.csv file.
- Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"`
+ Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-stub-flags.csv file.
- Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
// The path to the generated filtered-flags.csv file.
- Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"`
+ Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
}
func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
diff --git a/java/builder.go b/java/builder.go
index e64a61f..c48e3fa 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -247,16 +247,33 @@
}
type javaBuilderFlags struct {
- javacFlags string
- bootClasspath classpath
- classpath classpath
+ javacFlags string
+
+ // bootClasspath is the list of jars that form the boot classpath (generally the java.* and
+ // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses
+ // systemModules and java9Classpath instead.
+ bootClasspath classpath
+
+ // classpath is the list of jars that form the classpath for javac and kotlinc rules. It
+ // contains header jars for all static and non-static dependencies.
+ classpath classpath
+
+ // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains
+ // header jars for all non-static dependencies. Static dependencies have already been
+ // combined into the program jar.
+ dexClasspath classpath
+
+ // java9Classpath is the list of jars that will be added to the classpath when targeting
+ // 1.9 or higher. It generally contains the android.* classes, while the java.* classes
+ // are provided by systemModules.
java9Classpath classpath
- processorPath classpath
- processors []string
- systemModules *systemModules
- aidlFlags string
- aidlDeps android.Paths
- javaVersion javaVersion
+
+ processorPath classpath
+ processors []string
+ systemModules *systemModules
+ aidlFlags string
+ aidlDeps android.Paths
+ javaVersion javaVersion
errorProneExtraJavacFlags string
errorProneProcessorPath classpath
diff --git a/java/config/config.go b/java/config/config.go
index ea2f934..05dfde6 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -68,6 +68,12 @@
pctx.StaticVariable("JavacHeapSize", "2048M")
pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
+
+ // ErrorProne can use significantly more memory than javac alone, give it a higher heap
+ // size (b/221480398).
+ pctx.StaticVariable("ErrorProneHeapSize", "4096M")
+ pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
+
pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
@@ -99,7 +105,12 @@
if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
return override
}
- return "11"
+ switch ctx.Config().Getenv("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+ case "true":
+ return "17"
+ default:
+ return "11"
+ }
})
pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 6cb61f3..a83f87f 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -47,4 +47,9 @@
pctx.StaticVariable("KotlincSuppressJDK9Warnings", strings.Join([]string{
"-J--add-opens=java.base/java.util=ALL-UNNAMED", // https://youtrack.jetbrains.com/issue/KT-43704
}, " "))
+
+ pctx.StaticVariable("KotlincGlobalFlags", strings.Join([]string{
+ // b/222162908: prevent kotlinc from reading /tmp/build.txt
+ "-Didea.plugins.compatible.build=999.SNAPSHOT",
+ }, " "))
}
diff --git a/java/dex.go b/java/dex.go
index 474694a..84665e7 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -132,12 +132,15 @@
`--no-data-resources ` +
`-printmapping ${outDict} ` +
`-printusage ${outUsage} ` +
+ `--deps-file ${out}.d ` +
`$r8Flags && ` +
`touch "${outDict}" "${outUsage}" && ` +
`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
`rm -rf ${outUsageDir} && ` +
`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
CommandDeps: []string{
"${config.R8Cmd}",
"${config.Zip2ZipCmd}",
@@ -205,10 +208,10 @@
func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
- d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
+ d8Flags = append(d8Flags, flags.dexClasspath.FormRepeatedClassPath("--lib ")...)
d8Deps = append(d8Deps, flags.bootClasspath...)
- d8Deps = append(d8Deps, flags.classpath...)
+ d8Deps = append(d8Deps, flags.dexClasspath...)
return d8Flags, d8Deps
}
@@ -231,11 +234,11 @@
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
- r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
+ r8Flags = append(r8Flags, flags.dexClasspath.FormJavaClassPath("-libraryjars"))
r8Deps = append(r8Deps, proguardRaiseDeps...)
r8Deps = append(r8Deps, flags.bootClasspath...)
- r8Deps = append(r8Deps, flags.classpath...)
+ r8Deps = append(r8Deps, flags.dexClasspath...)
flagFiles := android.Paths{
android.PathForSource(ctx, "build/make/core/proguard.flags"),
diff --git a/java/dex_test.go b/java/dex_test.go
new file mode 100644
index 0000000..fbdccb6
--- /dev/null
+++ b/java/dex_test.go
@@ -0,0 +1,103 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestR8(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ android_app {
+ name: "app",
+ srcs: ["foo.java"],
+ libs: ["lib"],
+ static_libs: ["static_lib"],
+ platform_apis: true,
+ }
+
+ java_library {
+ name: "lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+ `)
+
+ app := result.ModuleForTests("app", "android_common")
+ lib := result.ModuleForTests("lib", "android_common")
+ staticLib := result.ModuleForTests("static_lib", "android_common")
+
+ appJavac := app.Rule("javac")
+ appR8 := app.Rule("r8")
+ libHeader := lib.Output("turbine-combined/lib.jar").Output
+ staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+
+ android.AssertStringDoesContain(t, "expected lib header jar in app javac classpath",
+ appJavac.Args["classpath"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected static_lib header jar in app javac classpath",
+ appJavac.Args["classpath"], staticLibHeader.String())
+
+ android.AssertStringDoesContain(t, "expected lib header jar in app r8 classpath",
+ appR8.Args["r8Flags"], libHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in app javac classpath",
+ appR8.Args["r8Flags"], staticLibHeader.String())
+}
+
+func TestD8(t *testing.T) {
+ result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ libs: ["lib"],
+ static_libs: ["static_lib"],
+ installable: true,
+ }
+
+ java_library {
+ name: "lib",
+ srcs: ["foo.java"],
+ }
+
+ java_library {
+ name: "static_lib",
+ srcs: ["foo.java"],
+ }
+ `)
+
+ foo := result.ModuleForTests("foo", "android_common")
+ lib := result.ModuleForTests("lib", "android_common")
+ staticLib := result.ModuleForTests("static_lib", "android_common")
+
+ fooJavac := foo.Rule("javac")
+ fooD8 := foo.Rule("d8")
+ libHeader := lib.Output("turbine-combined/lib.jar").Output
+ staticLibHeader := staticLib.Output("turbine-combined/static_lib.jar").Output
+
+ android.AssertStringDoesContain(t, "expected lib header jar in foo javac classpath",
+ fooJavac.Args["classpath"], libHeader.String())
+ android.AssertStringDoesContain(t, "expected static_lib header jar in foo javac classpath",
+ fooJavac.Args["classpath"], staticLibHeader.String())
+
+ android.AssertStringDoesContain(t, "expected lib header jar in foo d8 classpath",
+ fooD8.Args["d8Flags"], libHeader.String())
+ android.AssertStringDoesNotContain(t, "expected no static_lib header jar in foo javac classpath",
+ fooD8.Args["d8Flags"], staticLibHeader.String())
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index cad9c33..3d91aec 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -276,11 +276,17 @@
// Rules which should be used in make to install the outputs.
profileInstalls android.RuleBuilderInstalls
+ // Path to the license metadata file for the module that built the profile.
+ profileLicenseMetadataFile android.OptionalPath
+
// Path to the image profile file on host (or empty, if profile is not generated).
profilePathOnHost android.Path
// Target-dependent fields.
variants []*bootImageVariant
+
+ // Path of the preloaded classes file.
+ preloadedClassesFile string
}
// Target-dependent description of a boot image.
@@ -320,6 +326,9 @@
// Rules which should be used in make to install the outputs on device.
deviceInstalls android.RuleBuilderInstalls
+
+ // Path to the license metadata file for the module that built the image.
+ licenseMetadataFile android.OptionalPath
}
// Get target-specific boot image variant for the given boot image config and target.
@@ -680,6 +689,13 @@
cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
}
+ // We always expect a preloaded classes file to be available. However, if we cannot find it, it's
+ // OK to not pass the flag to dex2oat.
+ preloadedClassesPath := android.ExistentPathForSource(ctx, image.preloadedClassesFile)
+ if preloadedClassesPath.Valid() {
+ cmd.FlagWithInput("--preloaded-classes=", preloadedClassesPath.Path())
+ }
+
cmd.
FlagForEachInput("--dex-file=", image.dexPaths.Paths()).
FlagForEachArg("--dex-location=", image.dexLocations).
@@ -759,6 +775,7 @@
image.vdexInstalls = vdexInstalls
image.unstrippedInstalls = unstrippedInstalls
image.deviceInstalls = deviceInstalls
+ image.licenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
}
const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -807,6 +824,7 @@
if image == defaultBootImageConfig(ctx) {
rule.Install(profile, "/system/etc/boot-image.prof")
image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
}
rule.Build("bootJarsProfile", "profile boot jars")
@@ -844,6 +862,7 @@
rule.Install(profile, "/system/etc/boot-image.bprof")
rule.Build("bootFrameworkProfile", "profile boot framework jars")
image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+ image.profileLicenseMetadataFile = android.OptionalPathForPath(ctx.LicenseMetadataFile())
return profile
}
@@ -909,6 +928,9 @@
image := d.defaultBootImage
if image != nil {
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
+ if image.profileLicenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
+ }
global := dexpreopt.GetGlobalConfig(ctx)
dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
@@ -934,6 +956,9 @@
ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
+ if variant.licenseMetadataFile.Valid() {
+ ctx.Strict("DEXPREOPT_IMAGE_LICENSE_METADATA_"+sfx, variant.licenseMetadataFile.String())
+ }
}
imageLocationsOnHost, imageLocationsOnDevice := current.getAnyAndroidVariant().imageLocations()
ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_ON_HOST"+current.name, strings.Join(imageLocationsOnHost, ":"))
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index df8d8c8..4d0bd09 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -59,21 +59,23 @@
name: artBootImageName,
stem: "boot",
installDirOnHost: "apex/art_boot_images/javalib",
- installDirOnDevice: "apex/com.android.art/javalib",
+ installDirOnDevice: "system/framework",
profileInstallPathInApex: "etc/boot-image.prof",
modules: artModules,
+ preloadedClassesFile: "art/build/boot/preloaded-classes",
}
// Framework config for the boot image extension.
// It includes framework libraries and depends on the ART config.
frameworkSubdir := "system/framework"
frameworkCfg := bootImageConfig{
- extends: &artCfg,
- name: frameworkBootImageName,
- stem: "boot",
- installDirOnHost: frameworkSubdir,
- installDirOnDevice: frameworkSubdir,
- modules: frameworkModules,
+ extends: &artCfg,
+ name: frameworkBootImageName,
+ stem: "boot",
+ installDirOnHost: frameworkSubdir,
+ installDirOnDevice: frameworkSubdir,
+ modules: frameworkModules,
+ preloadedClassesFile: "frameworks/base/config/preloaded-classes",
}
return map[string]*bootImageConfig{
diff --git a/java/droiddoc.go b/java/droiddoc.go
index c84a15c..023d619 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -330,7 +330,7 @@
// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
if len(aidlSrcs) > 0 {
- srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+ srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, nil, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, srcJarFiles...)
}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index f9dcfd6..2921c3e 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -335,10 +335,10 @@
cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
FlagWithArg("--hide ", "SuperfluousPrefix").
FlagWithArg("--hide ", "AnnotationExtraction").
- // (b/217545629)
- FlagWithArg("--hide ", "ChangedThrows").
- // (b/217552813)
- FlagWithArg("--hide ", "ChangedAbstract")
+ // b/222738070
+ FlagWithArg("--hide ", "BannedThrow").
+ // b/223382732
+ FlagWithArg("--hide ", "ChangedDefault")
}
}
@@ -476,7 +476,10 @@
Flag("--quiet").
Flag("--format=v2").
FlagWithArg("--repeat-errors-max ", "10").
- FlagWithArg("--hide ", "UnresolvedImport")
+ FlagWithArg("--hide ", "UnresolvedImport").
+ FlagWithArg("--hide ", "InvalidNullability").
+ // b/223382732
+ FlagWithArg("--hide ", "ChangedDefault")
return cmd
}
diff --git a/java/gen.go b/java/gen.go
index 445a2d8..1572bf0 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -44,7 +44,7 @@
})
)
-func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlFlags string, deps android.Paths) android.Paths {
+func genAidl(ctx android.ModuleContext, aidlFiles android.Paths, aidlGlobalFlags string, aidlIndividualFlags map[string]string, deps android.Paths) android.Paths {
// Shard aidl files into groups of 50 to avoid having to recompile all of them if one changes and to avoid
// hitting command line length limits.
shards := android.ShardPaths(aidlFiles, 50)
@@ -61,15 +61,17 @@
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
- rule.Command().Text("FLAGS=' " + aidlFlags + "'")
+ rule.Command().Text("FLAGS=' " + aidlGlobalFlags + "'")
for _, aidlFile := range shard {
+ localFlag := aidlIndividualFlags[aidlFile.String()]
depFile := srcJarFile.InSameDir(ctx, aidlFile.String()+".d")
javaFile := outDir.Join(ctx, pathtools.ReplaceExtension(aidlFile.String(), "java"))
rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "aidl")).
FlagWithDepFile("-d", depFile).
Flag("$FLAGS").
+ Flag(localFlag).
Input(aidlFile).
Output(javaFile).
Implicits(deps)
@@ -159,7 +161,14 @@
// Process all aidl files together to support sharding them into one or more rules that produce srcjars.
if len(aidlSrcs) > 0 {
- srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, flags.aidlDeps)
+ individualFlags := make(map[string]string)
+ for _, aidlSrc := range aidlSrcs {
+ flags := j.individualAidlFlags(ctx, aidlSrc)
+ if flags != "" {
+ individualFlags[aidlSrc.String()] = flags
+ }
+ }
+ srcJarFiles := genAidl(ctx, aidlSrcs, flags.aidlFlags+aidlIncludeFlags, individualFlags, flags.aidlDeps)
outSrcFiles = append(outSrcFiles, srcJarFiles...)
}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 0cc960d..95ded34 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -943,7 +943,9 @@
// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
// patterns that will select a subset of the monolithic flags.
-func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path, splitPackages []string, packagePrefixes []string) android.Path {
+func buildRuleSignaturePatternsFile(
+ ctx android.ModuleContext, flagsPath android.Path,
+ splitPackages []string, packagePrefixes []string, singlePackages []string) android.Path {
patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv")
// Create a rule to validate the output from the following rule.
rule := android.NewRuleBuilder(pctx, ctx)
@@ -959,6 +961,7 @@
FlagWithInput("--flags ", flagsPath).
FlagForEachArg("--split-package ", quotedSplitPackages).
FlagForEachArg("--package-prefix ", packagePrefixes).
+ FlagForEachArg("--single-package ", singlePackages).
FlagWithOutput("--output ", patternsFile)
rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns")
diff --git a/java/java.go b/java/java.go
index e55f045..ddef34d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -421,9 +421,25 @@
}
type deps struct {
- classpath classpath
- java9Classpath classpath
- bootClasspath classpath
+ // bootClasspath is the list of jars that form the boot classpath (generally the java.* and
+ // android.* classes) for tools that still use it. javac targeting 1.9 or higher uses
+ // systemModules and java9Classpath instead.
+ bootClasspath classpath
+
+ // classpath is the list of jars that form the classpath for javac and kotlinc rules. It
+ // contains header jars for all static and non-static dependencies.
+ classpath classpath
+
+ // dexClasspath is the list of jars that form the classpath for d8 and r8 rules. It contains
+ // header jars for all non-static dependencies. Static dependencies have already been
+ // combined into the program jar.
+ dexClasspath classpath
+
+ // java9Classpath is the list of jars that will be added to the classpath when targeting
+ // 1.9 or higher. It generally contains the android.* classes, while the java.* classes
+ // are provided by systemModules.
+ java9Classpath classpath
+
processorPath classpath
errorProneProcessorPath classpath
processorClasses []string
@@ -1458,7 +1474,10 @@
if ctx.OtherModuleHasProvider(module, JavaInfoProvider) {
dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo)
switch tag {
- case libTag, staticLibTag:
+ case libTag:
+ flags.classpath = append(flags.classpath, dep.HeaderJars...)
+ flags.dexClasspath = append(flags.dexClasspath, dep.HeaderJars...)
+ case staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars...)
case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars...)
@@ -2011,10 +2030,26 @@
}
func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) *javaLibraryAttributes {
- //TODO(b/209577426): Support multiple arch variants
- srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+ var srcs bazel.LabelListAttribute
+ archVariantProps := m.GetArchVariantProperties(ctx, &CommonProperties{})
+ for axis, configToProps := range archVariantProps {
+ for config, _props := range configToProps {
+ if archProps, ok := _props.(*CommonProperties); ok {
+ archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs)
+ srcs.SetSelectValue(axis, config, archSrcs)
+ }
+ }
+ }
+
+ javaSrcPartition := "java"
+ protoSrcPartition := "proto"
+ srcPartitions := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
+ javaSrcPartition: bazel.LabelPartition{Extensions: []string{".java"}, Keep_remainder: true},
+ protoSrcPartition: android.ProtoSrcLabelPartition,
+ })
+
attrs := &javaLibraryAttributes{
- Srcs: srcs,
+ Srcs: srcPartitions[javaSrcPartition],
}
if m.properties.Javacflags != nil {
@@ -2022,6 +2057,11 @@
}
var deps bazel.LabelList
+ sdkVersion := m.SdkVersion(ctx)
+ if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
+ // TODO(b/220869005) remove forced dependency on current public android.jar
+ deps.Add(&bazel.Label{Label: "//prebuilts/sdk:public_current_android_sdk_java_import"})
+ }
if m.properties.Libs != nil {
deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
}
@@ -2029,6 +2069,12 @@
//TODO(b/217236083) handle static libs similarly to Soong
deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
}
+
+ protoDeps := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition])
+ if protoDeps != nil {
+ deps.Add(protoDeps)
+ }
+
attrs.Deps = bazel.MakeLabelListAttribute(deps)
return attrs
diff --git a/java/java_test.go b/java/java_test.go
index 21c76b6..f095c5e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1333,6 +1333,42 @@
}
}
+func TestAidlEnforcePermissions(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ aidl: { enforce_permissions: true },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "-Wmissing-permission-annotation -Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
+func TestAidlEnforcePermissionsException(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl", "aidl/foo/IFoo2.aidl"],
+ aidl: { enforce_permissions: true, enforce_permissions_exceptions: ["aidl/foo/IFoo2.aidl"] },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "$$FLAGS -Wmissing-permission-annotation -Werror aidl/foo/IFoo.aidl"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+ expectedAidlFlag = "$$FLAGS aidl/foo/IFoo2.aidl"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
func TestDataNativeBinaries(t *testing.T) {
ctx, _ := testJava(t, `
java_test_host {
diff --git a/java/kotlin.go b/java/kotlin.go
index e4f1bc1..3e5cec0 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -34,7 +34,8 @@
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
+ `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` +
+ `${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} ` +
`$kotlincFlags -jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile ` +
`-kotlin-home $emptyDir && ` +
`${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` +
@@ -125,7 +126,8 @@
`${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` +
` --srcs "$out.rsp" --srcs "$srcJarDir/list"` +
` $commonSrcFilesArg --out "$kotlinBuildFile" && ` +
- `${config.KotlincCmd} ${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` +
+ `${config.KotlincCmd} ${config.KotlincGlobalFlags} ` +
+ `${config.KaptSuppressJDK9Warnings} ${config.KotlincSuppressJDK9Warnings} ` +
`${config.JavacHeapFlags} $kotlincFlags -Xplugin=${config.KotlinKaptJar} ` +
`-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` +
`-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index e3396c1..8e22491 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -81,7 +81,6 @@
"ds-car-docs", // for AAOS API documentation only
"DynamicSystemInstallationService",
"EmergencyInfo-lib",
- "ethernet-service",
"EthernetServiceTests",
"ExternalStorageProvider",
"face-V1-0-javalib",
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c67e2bd..44650a6 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "path"
"strconv"
"strings"
@@ -37,6 +38,11 @@
// list of api version directories
Api_dirs []string
+ // Directory containing finalized api txt files for extension versions.
+ // Extension versions higher than the base sdk extension version will
+ // be assumed to be finalized later than all Api_dirs.
+ Extensions_dir *string
+
// The next API directory can optionally point to a directory where
// files incompatibility-tracking files are stored for the current
// "in progress" API. Each module present in one of the api_dirs will have
@@ -60,36 +66,45 @@
// no need to implement
}
-func parseJarPath(path string) (module string, apiver string, scope string) {
- elements := strings.Split(path, "/")
+// parsePrebuiltPath parses the relevant variables out of a variety of paths, e.g.
+// <version>/<scope>/<module>.jar
+// <version>/<scope>/api/<module>.txt
+// extensions/<version>/<scope>/<module>.jar
+// extensions/<version>/<scope>/api/<module>.txt
+func parsePrebuiltPath(ctx android.LoadHookContext, p string) (module string, version string, scope string) {
+ elements := strings.Split(p, "/")
- apiver = elements[0]
- scope = elements[1]
-
- module = strings.TrimSuffix(elements[2], ".jar")
- return
-}
-
-func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) {
- elements := strings.Split(path, "/")
- apiver = elements[0]
-
- scope = elements[1]
- if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
- ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
+ scopeIdx := len(elements) - 2
+ if elements[scopeIdx] == "api" {
+ scopeIdx--
+ }
+ scope = elements[scopeIdx]
+ if scope != "core" && scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
+ ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, p)
return
}
+ version = elements[scopeIdx-1]
- // elements[2] is string literal "api". skipping.
- module = strings.TrimSuffix(elements[3], ".txt")
+ module = strings.TrimSuffix(path.Base(p), path.Ext(p))
return
}
-func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string {
- return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
+// parseFinalizedPrebuiltPath is like parsePrebuiltPath, but verifies the version is numeric (a finalized version).
+func parseFinalizedPrebuiltPath(ctx android.LoadHookContext, p string) (module string, version int, scope string) {
+ module, v, scope := parsePrebuiltPath(ctx, p)
+ version, err := strconv.Atoi(v)
+ if err != nil {
+ ctx.ModuleErrorf("Found finalized API files in non-numeric dir '%v'", v)
+ return
+ }
+ return
}
-func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) {
+func prebuiltApiModuleName(mctx android.LoadHookContext, module, scope, version string) string {
+ return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
+}
+
+func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
props := struct {
Name *string
Jars []string
@@ -97,7 +112,7 @@
Installable *bool
Compile_dex *bool
}{}
- props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
+ props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version))
props.Jars = append(props.Jars, path)
props.Sdk_version = proptools.StringPtr(sdkVersion)
props.Installable = proptools.BoolPtr(false)
@@ -132,111 +147,125 @@
mctx.CreateModule(genrule.GenRuleFactory, &props)
}
-func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
+// globApiDirs collects all the files in all api_dirs and all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <api-dir>/<scope>/<glob> for all api-dir and scope.
+func globApiDirs(mctx android.LoadHookContext, p *prebuiltApis, api_dir_glob string) []string {
var files []string
for _, apiver := range p.properties.Api_dirs {
- files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...)
+ files = append(files, globScopeDir(mctx, apiver, api_dir_glob)...)
}
return files
}
-func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string {
+// globExtensionDirs collects all the files under the extension dir (for all versions and scopes) that match the given glob
+// <extension-dir>/<version>/<scope>/<glob> for all version and scope.
+func globExtensionDirs(mctx android.LoadHookContext, p *prebuiltApis, extension_dir_glob string) []string {
+ // <extensions-dir>/<num>/<extension-dir-glob>
+ return globScopeDir(mctx, *p.properties.Extensions_dir+"/*", extension_dir_glob)
+}
+
+// globScopeDir collects all the files in the given subdir across all scopes that match the given glob, e.g. '*.jar' or 'api/*.txt'.
+// <subdir>/<scope>/<glob> for all scope.
+func globScopeDir(mctx android.LoadHookContext, subdir string, subdir_glob string) []string {
var files []string
dir := mctx.ModuleDir() + "/" + subdir
for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
- glob := fmt.Sprintf("%s/%s/%s", dir, scope, name)
+ glob := fmt.Sprintf("%s/%s/%s", dir, scope, subdir_glob)
vfiles, err := mctx.GlobWithDeps(glob, nil)
if err != nil {
- mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err)
+ mctx.ModuleErrorf("failed to glob %s files under %q: %s", subdir_glob, dir+"/"+scope, err)
}
files = append(files, vfiles...)
}
+ for i, f := range files {
+ files[i] = strings.TrimPrefix(f, mctx.ModuleDir()+"/")
+ }
return files
}
func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) {
- mydir := mctx.ModuleDir() + "/"
// <apiver>/<scope>/<module>.jar
- files := getPrebuiltFiles(mctx, p, "*.jar")
+ files := globApiDirs(mctx, p, "*.jar")
sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current")
compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false)
for _, f := range files {
// create a Import module for each jar file
- localPath := strings.TrimPrefix(f, mydir)
- module, apiver, scope := parseJarPath(localPath)
- createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
+ module, version, scope := parsePrebuiltPath(mctx, f)
+ createImport(mctx, module, scope, version, f, sdkVersion, compileDex)
if module == "core-for-system-modules" {
- createSystemModules(mctx, apiver, scope)
+ createSystemModules(mctx, version, scope)
}
}
}
-func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) {
+func createSystemModules(mctx android.LoadHookContext, version, scope string) {
props := struct {
Name *string
Libs []string
}{}
- props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver))
- props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver))
+ props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, version))
+ props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, version))
mctx.CreateModule(systemModulesImportFactory, &props)
}
func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
- mydir := mctx.ModuleDir() + "/"
// <apiver>/<scope>/api/<module>.txt
- files := getPrebuiltFiles(mctx, p, "api/*.txt")
-
- if len(files) == 0 {
- mctx.ModuleErrorf("no api file found under %q", mydir)
- }
-
- // construct a map to find out the latest api file path
- // for each (<module>, <scope>) pair.
- type latestApiInfo struct {
- module string
- scope string
- version int
- path string
+ apiLevelFiles := globApiDirs(mctx, p, "api/*.txt")
+ if len(apiLevelFiles) == 0 {
+ mctx.ModuleErrorf("no api file found under %q", mctx.ModuleDir())
}
// Create modules for all (<module>, <scope, <version>) triplets,
- // and a "latest" module variant for each (<module>, <scope>) pair
apiModuleName := func(module, scope, version string) string {
return module + ".api." + scope + "." + version
}
- m := make(map[string]latestApiInfo)
- for _, f := range files {
- localPath := strings.TrimPrefix(f, mydir)
- module, apiver, scope := parseApiFilePath(mctx, localPath)
- createApiModule(mctx, apiModuleName(module, scope, apiver), localPath)
+ for _, f := range apiLevelFiles {
+ module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+ createApiModule(mctx, apiModuleName(module, scope, strconv.Itoa(version)), f)
+ }
- version, err := strconv.Atoi(apiver)
- if err != nil {
- mctx.ModuleErrorf("Found finalized API files in non-numeric dir %v", apiver)
- return
- }
+ // Figure out the latest version of each module/scope
+ type latestApiInfo struct {
+ module, scope, path string
+ version int
+ }
- // Track latest version of each module/scope, except for incompatibilities
- if !strings.HasSuffix(module, "incompatibilities") {
+ getLatest := func(files []string) map[string]latestApiInfo {
+ m := make(map[string]latestApiInfo)
+ for _, f := range files {
+ module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
+ if strings.HasSuffix(module, "incompatibilities") {
+ continue
+ }
key := module + "." + scope
- info, ok := m[key]
- if !ok {
- m[key] = latestApiInfo{module, scope, version, localPath}
- } else if version > info.version {
- info.version = version
- info.path = localPath
- m[key] = info
+ info, exists := m[key]
+ if !exists || version > info.version {
+ m[key] = latestApiInfo{module, scope, f, version}
+ }
+ }
+ return m
+ }
+
+ latest := getLatest(apiLevelFiles)
+ if p.properties.Extensions_dir != nil {
+ extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
+ for k, v := range getLatest(extensionApiFiles) {
+ if v.version > mctx.Config().PlatformBaseSdkExtensionVersion() {
+ if _, exists := latest[k]; !exists {
+ mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
+ }
+ latest[k] = v
}
}
}
// Sort the keys in order to make build.ninja stable
- for _, k := range android.SortedStringKeys(m) {
- info := m[k]
+ for _, k := range android.SortedStringKeys(latest) {
+ info := latest[k]
name := apiModuleName(info.module, info.scope, "latest")
createApiModule(mctx, name, info.path)
}
@@ -244,21 +273,20 @@
// Create incompatibilities tracking files for all modules, if we have a "next" api.
incompatibilities := make(map[string]bool)
if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
- files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
+ files := globScopeDir(mctx, nextApiDir, "api/*incompatibilities.txt")
for _, f := range files {
- localPath := strings.TrimPrefix(f, mydir)
- filename, _, scope := parseApiFilePath(mctx, localPath)
+ filename, _, scope := parsePrebuiltPath(mctx, f)
referencedModule := strings.TrimSuffix(filename, "-incompatibilities")
- createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+ createApiModule(mctx, apiModuleName(referencedModule+"-incompatibilities", scope, "latest"), f)
incompatibilities[referencedModule+"."+scope] = true
}
}
// Create empty incompatibilities files for remaining modules
- for _, k := range android.SortedStringKeys(m) {
+ for _, k := range android.SortedStringKeys(latest) {
if _, ok := incompatibilities[k]; !ok {
- createEmptyFile(mctx, apiModuleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+ createEmptyFile(mctx, apiModuleName(latest[k].module+"-incompatibilities", latest[k].scope, "latest"))
}
}
}
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
index 79f4225..75422ad 100644
--- a/java/prebuilt_apis_test.go
+++ b/java/prebuilt_apis_test.go
@@ -20,9 +20,14 @@
"testing"
"android/soong/android"
+
"github.com/google/blueprint"
)
+func intPtr(v int) *int {
+ return &v
+}
+
func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForJavaTest,
@@ -54,3 +59,34 @@
sort.Strings(expected)
android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules)
}
+
+func TestPrebuiltApis_WithExtensions(t *testing.T) {
+ runTestWithBaseExtensionLevel := func(v int) (foo_input string, bar_input string) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.Platform_base_sdk_extension_version = intPtr(v)
+ }),
+ FixtureWithPrebuiltApisAndExtensions(map[string][]string{
+ "31": {"foo"},
+ "32": {"foo", "bar"},
+ "current": {"foo", "bar"},
+ }, map[string][]string{
+ "1": {"foo"},
+ "2": {"foo", "bar"},
+ }),
+ ).RunTest(t)
+ foo_input = result.ModuleForTests("foo.api.public.latest", "").Rule("generator").Implicits[0].String()
+ bar_input = result.ModuleForTests("bar.api.public.latest", "").Rule("generator").Implicits[0].String()
+ return
+ }
+ // Here, the base extension level is 1, so extension level 2 is the latest
+ foo_input, bar_input := runTestWithBaseExtensionLevel(1)
+ android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest = extension level 2", "prebuilts/sdk/extensions/2/public/api/bar.txt", bar_input)
+
+ // Here, the base extension level is 2, so 2 is not later than 32.
+ foo_input, bar_input = runTestWithBaseExtensionLevel(2)
+ android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/foo.txt", foo_input)
+ android.AssertStringEquals(t, "Expected latest = api level 32", "prebuilts/sdk/32/public/api/bar.txt", bar_input)
+}
diff --git a/java/proto.go b/java/proto.go
index ab913d8..5ba486f 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -19,6 +19,13 @@
"strconv"
"android/soong/android"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint/proptools"
+)
+
+const (
+ protoTypeDefault = "lite"
)
func genProto(ctx android.ModuleContext, protoFiles android.Paths, flags android.ProtoFlags) android.Paths {
@@ -73,13 +80,15 @@
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
+ const unspecifiedProtobufPluginType = ""
if String(p.Proto.Plugin) == "" {
switch String(p.Proto.Type) {
+ case "stream": // does not require additional dependencies
case "micro":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
case "nano":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
- case "lite", "":
+ case "lite", unspecifiedProtobufPluginType:
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
if ctx.Host() || ctx.BazelConversionMode() {
@@ -132,3 +141,52 @@
return flags
}
+
+type protoAttributes struct {
+ Deps bazel.LabelListAttribute
+}
+
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label {
+ protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
+ if !ok {
+ return nil
+ }
+
+ typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault)
+ var rule_class string
+ suffix := "_java_proto"
+ switch typ {
+ case "nano":
+ suffix += "_nano"
+ rule_class = "java_nano_proto_library"
+ case "micro":
+ suffix += "_micro"
+ rule_class = "java_micro_proto_library"
+ case "lite":
+ suffix += "_lite"
+ rule_class = "java_lite_proto_library"
+ case "stream":
+ suffix += "_stream"
+ rule_class = "java_stream_proto_library"
+ case "full":
+ rule_class = "java_proto_library"
+ default:
+ ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ)
+ }
+
+ protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"}
+ var protoAttrs protoAttributes
+ protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+
+ name := m.Name() + suffix
+
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: rule_class,
+ Bzl_load_location: "//build/bazel/rules/java:proto.bzl",
+ },
+ android.CommonAttributes{Name: name},
+ &protoAttrs)
+
+ return &bazel.Label{Label: ":" + name}
+}
diff --git a/java/proto_test.go b/java/proto_test.go
new file mode 100644
index 0000000..d1cb714
--- /dev/null
+++ b/java/proto_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+const protoModules = `
+java_library_static {
+ name: "libprotobuf-java-lite",
+}
+`
+
+func TestProtoStream(t *testing.T) {
+ bp := `
+ java_library {
+ name: "java-stream-protos",
+ proto: {
+ type: "stream",
+ },
+ srcs: [
+ "a.proto",
+ "b.proto",
+ ],
+ }
+ `
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ ).RunTestWithBp(t, protoModules+bp)
+
+ proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+
+ if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
+ t.Errorf("expected '--javastream_out' in %q", cmd)
+ }
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 820d9ff..c37ed1a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -21,7 +21,6 @@
"reflect"
"regexp"
"sort"
- "strconv"
"strings"
"sync"
@@ -1512,15 +1511,15 @@
}
droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
disabledWarnings := []string{
- "MissingPermission",
"BroadcastBehavior",
- "HiddenSuperclass",
"DeprecationMismatch",
- "UnavailableSymbol",
- "SdkConstant",
+ "HiddenSuperclass",
"HiddenTypeParameter",
+ "MissingPermission",
+ "SdkConstant",
"Todo",
"Typo",
+ "UnavailableSymbol",
}
droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
@@ -2551,8 +2550,14 @@
ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
return ""
}
- intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt())
- return formattedOptionalAttribute(attrName, &intStr)
+ if apiLevel.IsCurrent() {
+ // passing "current" would always mean a future release, never the current (or the current in
+ // progress) which means some conditions would never be triggered.
+ ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+ `"current" is not an allowed value for this attribute`)
+ return ""
+ }
+ return formattedOptionalAttribute(attrName, value)
}
// formats an attribute for the xml permissions file if the value is not null
@@ -2808,7 +2813,7 @@
StubsSrcJar android.Path
CurrentApiFile android.Path
RemovedApiFile android.Path
- AnnotationsZip android.Path `supported_build_releases:"T+"`
+ AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"`
SdkVersion string
}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index e0e5b56..3500c84 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -182,7 +182,7 @@
"30": {"foo", "fooUpdatable", "fooUpdatableErr"},
}),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"}
+ variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"}
}),
).RunTestWithBp(t,
`
@@ -193,7 +193,7 @@
on_bootclasspath_since: "U",
on_bootclasspath_before: "V",
min_device_sdk: "W",
- max_device_sdk: "current",
+ max_device_sdk: "X",
min_sdk_version: "S",
}
java_sdk_library {
@@ -202,12 +202,13 @@
api_packages: ["foo"],
}
`)
+
// test that updatability attributes are passed on correctly
fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`)
- android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
+ android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
// double check that updatability attributes are not written if they don't exist in the bp file
// the permissions file for the foo library defined above
@@ -230,7 +231,7 @@
`on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
`on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
`min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
- `max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`,
+ `max_device_sdk: "current" is not an allowed value for this attribute`,
})).RunTestWithBp(t,
`
java_sdk_library {
@@ -240,7 +241,7 @@
on_bootclasspath_since: "aaa",
on_bootclasspath_before: "bbc",
min_device_sdk: "ccc",
- max_device_sdk: "ddd",
+ max_device_sdk: "current",
}
`)
}
diff --git a/java/testing.go b/java/testing.go
index 6c49bc8..82aa29b 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -146,6 +146,10 @@
// This defines a file in the mock file system in a predefined location (prebuilts/sdk/Android.bp)
// and so only one instance of this can be used in each fixture.
func FixtureWithPrebuiltApis(release2Modules map[string][]string) android.FixturePreparer {
+ return FixtureWithPrebuiltApisAndExtensions(release2Modules, nil)
+}
+
+func FixtureWithPrebuiltApisAndExtensions(apiLevel2Modules map[string][]string, extensionLevel2Modules map[string][]string) android.FixturePreparer {
mockFS := android.MockFS{}
path := "prebuilts/sdk/Android.bp"
@@ -153,14 +157,20 @@
prebuilt_apis {
name: "sdk",
api_dirs: ["%s"],
+ extensions_dir: "extensions",
imports_sdk_version: "none",
imports_compile_dex: true,
}
- `, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
+ `, strings.Join(android.SortedStringKeys(apiLevel2Modules), `", "`))
- for release, modules := range release2Modules {
+ for release, modules := range apiLevel2Modules {
mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
}
+ if extensionLevel2Modules != nil {
+ for release, modules := range extensionLevel2Modules {
+ mockFS.Merge(prebuiltExtensionApiFiles([]string{release}, modules))
+ }
+ }
return android.GroupFixturePreparers(
android.FixtureAddTextFile(path, bp),
android.FixtureMergeMockFs(mockFS),
@@ -198,6 +208,19 @@
return fs
}
+func prebuiltExtensionApiFiles(extensionLevels []string, modules []string) map[string][]byte {
+ fs := make(map[string][]byte)
+ for _, level := range extensionLevels {
+ for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer} {
+ for _, lib := range modules {
+ fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/extensions/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil
+ }
+ }
+ }
+ return fs
+}
+
// FixtureConfigureBootJars configures the boot jars in both the dexpreopt.GlobalConfig and
// Config.productVariables structs. As a side effect that enables dexpreopt.
func FixtureConfigureBootJars(bootJars ...string) android.FixturePreparer {
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index dbc112e..003b275 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -158,7 +158,6 @@
entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
- entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
},
},
}}
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 07f7ca1..dc16d1d 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -595,6 +595,7 @@
for i, arg := range cx.args {
argsCopy[i] = arg.transform(transformer)
}
+ cx.args = argsCopy
if replacement := transformer(cx); replacement != nil {
return replacement
} else {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index b8fe162..c881751 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -50,15 +50,12 @@
soongNsPrefix = "SOONG_CONFIG_"
// And here are the functions and variables:
- cfnGetCfg = baseName + ".cfg"
- cfnMain = baseName + ".product_configuration"
- cfnBoardMain = baseName + ".board_configuration"
- cfnPrintVars = baseName + ".printvars"
- cfnWarning = baseName + ".warning"
- cfnLocalAppend = baseName + ".local_append"
- cfnLocalSetDefault = baseName + ".local_set_default"
- cfnInherit = baseName + ".inherit"
- cfnSetListDefault = baseName + ".setdefault"
+ cfnGetCfg = baseName + ".cfg"
+ cfnMain = baseName + ".product_configuration"
+ cfnBoardMain = baseName + ".board_configuration"
+ cfnPrintVars = baseName + ".printvars"
+ cfnInherit = baseName + ".inherit"
+ cfnSetListDefault = baseName + ".setdefault"
)
const (
@@ -69,54 +66,55 @@
var knownFunctions = map[string]interface {
parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
}{
- "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
- "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
- "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
- soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
- soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
- "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
- "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
- "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
- "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
- "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
- "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
- "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
- "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
- "error": &makeControlFuncParser{name: baseName + ".mkerror"},
- "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
- "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
- "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
- "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
- "firstword": &firstOrLastwordCallParser{isLastWord: false},
- "foreach": &foreachCallPaser{},
- "if": &ifCallParser{},
- "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
- "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
- "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
- "is-product-in-list": &isProductInListCallParser{},
- "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
- "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
- "lastword": &firstOrLastwordCallParser{isLastWord: true},
- "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
- "math_max": &mathMaxOrMinCallParser{function: "max"},
- "math_min": &mathMaxOrMinCallParser{function: "min"},
- "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
- "math_gt": &mathComparisonCallParser{op: ">"},
- "math_lt": &mathComparisonCallParser{op: "<"},
- "my-dir": &myDirCallParser{},
- "patsubst": &substCallParser{fname: "patsubst"},
- "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
- "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
- "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
+ "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString},
+ "add-product-dex-preopt-module-config": &simpleCallParser{name: baseName + ".add_product_dex_preopt_module_config", returnType: starlarkTypeString, addHandle: true},
+ "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+ "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+ soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+ "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+ "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
+ "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList},
+ "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList},
+ "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
+ "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList},
+ "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+ "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid},
+ "error": &makeControlFuncParser{name: baseName + ".mkerror"},
+ "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt},
+ "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
+ "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList},
+ "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList},
+ "firstword": &firstOrLastwordCallParser{isLastWord: false},
+ "foreach": &foreachCallPaser{},
+ "if": &ifCallParser{},
+ "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
+ "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+ "is-product-in-list": &isProductInListCallParser{},
+ "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
+ "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
+ "lastword": &firstOrLastwordCallParser{isLastWord: true},
+ "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString},
+ "math_max": &mathMaxOrMinCallParser{function: "max"},
+ "math_min": &mathMaxOrMinCallParser{function: "min"},
+ "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
+ "math_gt": &mathComparisonCallParser{op: ">"},
+ "math_lt": &mathComparisonCallParser{op: "<"},
+ "my-dir": &myDirCallParser{},
+ "patsubst": &substCallParser{fname: "patsubst"},
+ "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList},
+ "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid},
+ "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid},
// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
"shell": &shellCallParser{},
- "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+ "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString},
"subst": &substCallParser{fname: "subst"},
"warning": &makeControlFuncParser{name: baseName + ".mkwarning"},
"word": &wordCallParser{},
- "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
+ "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList},
}
// These are functions that we don't implement conversions for, but
@@ -409,6 +407,8 @@
dependentModules map[string]*moduleInfo
soongNamespaces map[string]map[string]bool
includeTops []string
+ typeHints map[string]starlarkType
+ atTopOfMakefile bool
}
func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
@@ -430,7 +430,6 @@
{"TARGET_COPY_OUT_RECOVERY", "recovery"},
{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
// TODO(asmundak): to process internal config files, we need the following variables:
- // BOARD_CONFIG_VENDOR_PATH
// TARGET_VENDOR
// target_base_product
//
@@ -453,6 +452,8 @@
dependentModules: make(map[string]*moduleInfo),
soongNamespaces: make(map[string]map[string]bool),
includeTops: []string{},
+ typeHints: make(map[string]starlarkType),
+ atTopOfMakefile: true,
}
ctx.pushVarAssignments()
for _, item := range predefined {
@@ -465,17 +466,17 @@
return ctx
}
-func (ctx *parseContext) lastAssignment(name string) *assignmentNode {
+func (ctx *parseContext) lastAssignment(v variable) *assignmentNode {
for va := ctx.varAssignments; va != nil; va = va.outer {
- if v, ok := va.vars[name]; ok {
+ if v, ok := va.vars[v.name()]; ok {
return v
}
}
return nil
}
-func (ctx *parseContext) setLastAssignment(name string, asgn *assignmentNode) {
- ctx.varAssignments.vars[name] = asgn
+func (ctx *parseContext) setLastAssignment(v variable, asgn *assignmentNode) {
+ ctx.varAssignments.vars[v.name()] = asgn
}
func (ctx *parseContext) pushVarAssignments() {
@@ -532,7 +533,7 @@
if lhs == nil {
return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
}
- _, isTraced := ctx.tracedVariables[name]
+ _, isTraced := ctx.tracedVariables[lhs.name()]
asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
if lhs.valueType() == starlarkTypeUnknown {
// Try to divine variable type from the RHS
@@ -565,17 +566,19 @@
}
}
- asgn.previous = ctx.lastAssignment(name)
- ctx.setLastAssignment(name, asgn)
+ if asgn.lhs.valueType() == starlarkTypeString &&
+ asgn.value.typ() != starlarkTypeUnknown &&
+ asgn.value.typ() != starlarkTypeString {
+ asgn.value = &toStringExpr{expr: asgn.value}
+ }
+
+ asgn.previous = ctx.lastAssignment(lhs)
+ ctx.setLastAssignment(lhs, asgn)
switch a.Type {
case "=", ":=":
asgn.flavor = asgnSet
case "+=":
- if asgn.previous == nil && !asgn.lhs.isPreset() {
- asgn.flavor = asgnMaybeAppend
- } else {
- asgn.flavor = asgnAppend
- }
+ asgn.flavor = asgnAppend
case "?=":
asgn.flavor = asgnMaybeSet
default:
@@ -807,20 +810,16 @@
if len(matchingPaths) > maxMatchingFiles {
return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
}
- if len(matchingPaths) == 1 {
- res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
- return []starlarkNode{processModule(res)}
- } else {
- needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
- res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
- for _, p := range matchingPaths {
- // A product configuration files discovered dynamically may attempt to inherit
- // from another one which does not exist in this source tree. Prevent load errors
- // by always loading the dynamic files as optional.
- res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
- }
- return []starlarkNode{processModule(res)}
+
+ needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
+ res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+ for _, p := range matchingPaths {
+ // A product configuration files discovered dynamically may attempt to inherit
+ // from another one which does not exist in this source tree. Prevent load errors
+ // by always loading the dynamic files as optional.
+ res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
}
+ return []starlarkNode{processModule(res)}
}
func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
@@ -1092,7 +1091,7 @@
// Given an if statement's directive and the left/right starlarkExprs,
// check if the starlarkExprs are one of a few hardcoded special cases
-// that can be converted to a simpler equalify expression than simply comparing
+// that can be converted to a simpler equality expression than simply comparing
// the two.
func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
right starlarkExpr) (starlarkExpr, bool) {
@@ -1121,8 +1120,8 @@
}
switch call.name {
- case baseName + ".filter", baseName + ".filter-out":
- return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
+ case baseName + ".filter":
+ return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
case baseName + ".expand_wildcard":
return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
case baseName + ".findstring":
@@ -1134,68 +1133,39 @@
}
func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
- filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+ filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
// We handle:
// * ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
// * ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
- // * ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
- // TODO(Asmundak): check the last case works for filter-out, too.
+ if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
+ return nil, false
+ }
xPattern := filterFuncCall.args[0]
xText := filterFuncCall.args[1]
var xInList *stringLiteralExpr
var expr starlarkExpr
var ok bool
- switch x := xValue.(type) {
- case *stringLiteralExpr:
- if x.literal != "" {
- return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
- }
- // Either pattern or text should be const, and the
- // non-const one should be varRefExpr
- if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
- expr = xText
- } else if xInList, ok = xText.(*stringLiteralExpr); ok {
- expr = xPattern
- } else {
- expr = &callExpr{
- object: nil,
- name: filterFuncCall.name,
- args: filterFuncCall.args,
- returnType: starlarkTypeBool,
- }
- if negate {
- expr = ¬Expr{expr: expr}
- }
- return expr
- }
- case *variableRefExpr:
- if v, ok := xPattern.(*variableRefExpr); ok {
- if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
- // ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
- // it's the opposite to what is done when comparing to empty.
- expr = xPattern
- negate = !negate
- }
- }
+ if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
+ expr = xText
+ } else if xInList, ok = xText.(*stringLiteralExpr); ok {
+ expr = xPattern
+ } else {
+ return nil, false
}
- if expr != nil && xInList != nil {
- slExpr := newStringListExpr(strings.Fields(xInList.literal))
- // Generate simpler code for the common cases:
- if expr.typ() == starlarkTypeList {
- if len(slExpr.items) == 1 {
- // Checking that a string belongs to list
- return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
- } else {
- // TODO(asmundak):
- panic("TBD")
- }
- } else if len(slExpr.items) == 1 {
- return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
+ slExpr := newStringListExpr(strings.Fields(xInList.literal))
+ // Generate simpler code for the common cases:
+ if expr.typ() == starlarkTypeList {
+ if len(slExpr.items) == 1 {
+ // Checking that a string belongs to list
+ return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
} else {
- return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
+ return nil, false
}
+ } else if len(slExpr.items) == 1 {
+ return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
+ } else {
+ return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
}
- return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
}
func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
@@ -1301,12 +1271,12 @@
args: []starlarkExpr{
&stringLiteralExpr{literal: substParts[0]},
&stringLiteralExpr{literal: substParts[1]},
- NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil),
+ NewVariableRefExpr(v, ctx.lastAssignment(v) != nil),
},
}
}
if v := ctx.addVariable(refDump); v != nil {
- return NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil)
+ return NewVariableRefExpr(v, ctx.lastAssignment(v) != nil)
}
return ctx.newBadExpr(node, "unknown variable %s", refDump)
}
@@ -1340,6 +1310,7 @@
name string
returnType starlarkType
addGlobals bool
+ addHandle bool
}
func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
@@ -1347,6 +1318,9 @@
if p.addGlobals {
expr.args = append(expr.args, &globalsExpr{})
}
+ if p.addHandle {
+ expr.args = append(expr.args, &identifierExpr{name: "handle"})
+ }
for _, arg := range args.Split(",") {
arg.TrimLeftSpaces()
arg.TrimRightSpaces()
@@ -1402,7 +1376,7 @@
if !args.Empty() {
return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
}
- return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+ return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)}
}
type isProductInListCallParser struct{}
@@ -1716,7 +1690,8 @@
// Clear the includeTops after each non-comment statement
// so that include annotations placed on certain statements don't apply
// globally for the rest of the makefile was well.
- if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
+ if _, wasComment := node.(*mkparser.Comment); !wasComment {
+ ctx.atTopOfMakefile = false
ctx.includeTops = []string{}
}
@@ -1726,6 +1701,12 @@
return result
}
+// The types allowed in a type_hint
+var typeHintMap = map[string]starlarkType{
+ "string": starlarkTypeString,
+ "list": starlarkTypeList,
+}
+
// Processes annotation. An annotation is a comment that starts with #RBC# and provides
// a conversion hint -- say, where to look for the dynamically calculated inherit/include
// paths. Returns true if the comment was a successfully-handled annotation.
@@ -1750,6 +1731,35 @@
}
ctx.includeTops = append(ctx.includeTops, p)
return nil, true
+ } else if p, ok := maybeTrim(annotation, "type_hint"); ok {
+ // Type hints must come at the beginning the file, to avoid confusion
+ // if a type hint was specified later and thus only takes effect for half
+ // of the file.
+ if !ctx.atTopOfMakefile {
+ return ctx.newBadNode(cnode, "type_hint annotations must come before the first Makefile statement"), true
+ }
+
+ parts := strings.Fields(p)
+ if len(parts) <= 1 {
+ return ctx.newBadNode(cnode, "Invalid type_hint annotation: %s. Must be a variable type followed by a list of variables of that type", p), true
+ }
+
+ var varType starlarkType
+ if varType, ok = typeHintMap[parts[0]]; !ok {
+ varType = starlarkTypeUnknown
+ }
+ if varType == starlarkTypeUnknown {
+ return ctx.newBadNode(cnode, "Invalid type_hint annotation. Only list/string types are accepted, found %s", parts[0]), true
+ }
+
+ for _, name := range parts[1:] {
+ // Don't allow duplicate type hints
+ if _, ok := ctx.typeHints[name]; ok {
+ return ctx.newBadNode(cnode, "Duplicate type hint for variable %s", name), true
+ }
+ ctx.typeHints[name] = varType
+ }
+ return nil, true
}
return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
}
@@ -1900,9 +1910,7 @@
fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
- fmt.Fprintf(&buf, "globals, cfg, globals_base = %s(init, input_variables_init)\n", cfnBoardMain)
- fmt.Fprintf(&buf, "# TODO: Some product config variables need to be printed, but most are readonly so we can't just print cfg here.\n")
- fmt.Fprintf(&buf, "%s((globals, cfg, globals_base))\n", cfnPrintVars)
+ fmt.Fprintf(&buf, "%s(%s(init, input_variables_init))\n", cfnPrintVars, cfnBoardMain)
return buf.String()
}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 2083121..31739fa 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -65,6 +65,10 @@
PRODUCT_NAME := Pixel 3
PRODUCT_MODEL :=
local_var = foo
+local-var-with-dashes := bar
+$(warning local-var-with-dashes: $(local-var-with-dashes))
+GLOBAL-VAR-WITH-DASHES := baz
+$(warning GLOBAL-VAR-WITH-DASHES: $(GLOBAL-VAR-WITH-DASHES))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -73,6 +77,10 @@
cfg["PRODUCT_NAME"] = "Pixel 3"
cfg["PRODUCT_MODEL"] = ""
_local_var = "foo"
+ _local_var_with_dashes = "bar"
+ rblf.mkwarning("pixel3.mk", "local-var-with-dashes: %s" % _local_var_with_dashes)
+ g["GLOBAL-VAR-WITH-DASHES"] = "baz"
+ rblf.mkwarning("pixel3.mk", "GLOBAL-VAR-WITH-DASHES: %s" % g["GLOBAL-VAR-WITH-DASHES"])
`,
},
{
@@ -246,6 +254,8 @@
in: `
$(warning this is the warning)
$(warning)
+$(warning # this warning starts with a pound)
+$(warning this warning has a # in the middle)
$(info this is the info)
$(error this is the error)
PRODUCT_NAME:=$(shell echo *)
@@ -256,6 +266,8 @@
cfg = rblf.cfg(handle)
rblf.mkwarning("product.mk", "this is the warning")
rblf.mkwarning("product.mk", "")
+ rblf.mkwarning("product.mk", "# this warning starts with a pound")
+ rblf.mkwarning("product.mk", "this warning has a # in the middle")
rblf.mkinfo("product.mk", "this is the info")
rblf.mkerror("product.mk", "this is the error")
cfg["PRODUCT_NAME"] = rblf.shell("echo *")
@@ -389,6 +401,10 @@
endif
ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
endif
+ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
ifneq (,$(filter true, $(v1)$(v2)))
endif
ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
@@ -407,8 +423,12 @@
pass
if "plaf" in g.get("PLATFORM_LIST", []):
pass
+ if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")):
+ pass
if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
pass
+ if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
+ pass
if rblf.filter("true", "%s%s" % (_v1, _v2)):
pass
if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
@@ -731,6 +751,7 @@
$(call require-artifacts-in-path, foo, bar)
$(call require-artifacts-in-path-relaxed, foo, bar)
$(call dist-for-goals, goal, from:to)
+$(call add-product-dex-preopt-module-config,MyModule,disable)
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -741,6 +762,7 @@
rblf.require_artifacts_in_path("foo", "bar")
rblf.require_artifacts_in_path_relaxed("foo", "bar")
rblf.mkdist_for_goals(g, "goal", "from:to")
+ rblf.add_product_dex_preopt_module_config(handle, "MyModule", "disable")
`,
},
{
@@ -883,6 +905,43 @@
`,
},
{
+ desc: "assigment setdefaults",
+ mkname: "product.mk",
+ in: `
+# All of these should have a setdefault because they're self-referential and not defined before
+PRODUCT_LIST1 = a $(PRODUCT_LIST1)
+PRODUCT_LIST2 ?= a $(PRODUCT_LIST2)
+PRODUCT_LIST3 += a
+
+# Now doing them again should not have a setdefault because they've already been set
+PRODUCT_LIST1 = a $(PRODUCT_LIST1)
+PRODUCT_LIST2 ?= a $(PRODUCT_LIST2)
+PRODUCT_LIST3 += a
+`,
+ expected: `# All of these should have a setdefault because they're self-referential and not defined before
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.setdefault(handle, "PRODUCT_LIST1")
+ cfg["PRODUCT_LIST1"] = (["a"] +
+ cfg.get("PRODUCT_LIST1", []))
+ if cfg.get("PRODUCT_LIST2") == None:
+ rblf.setdefault(handle, "PRODUCT_LIST2")
+ cfg["PRODUCT_LIST2"] = (["a"] +
+ cfg.get("PRODUCT_LIST2", []))
+ rblf.setdefault(handle, "PRODUCT_LIST3")
+ cfg["PRODUCT_LIST3"] += ["a"]
+ # Now doing them again should not have a setdefault because they've already been set
+ cfg["PRODUCT_LIST1"] = (["a"] +
+ cfg["PRODUCT_LIST1"])
+ if cfg.get("PRODUCT_LIST2") == None:
+ cfg["PRODUCT_LIST2"] = (["a"] +
+ cfg["PRODUCT_LIST2"])
+ cfg["PRODUCT_LIST3"] += ["a"]
+`,
+ },
+ {
desc: "soong namespace assignments",
mkname: "product.mk",
in: `
@@ -975,6 +1034,7 @@
def init(g, handle):
cfg = rblf.cfg(handle)
if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []):
+ rblf.setdefault(handle, "PRODUCT_PACKAGES")
cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split()
`,
},
@@ -1071,7 +1131,13 @@
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
- rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get("%s/cfg.mk" % g["MY_PATH"])
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+ rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
@@ -1091,8 +1157,20 @@
def init(g, handle):
cfg = rblf.cfg(handle)
g["MY_PATH"] = "foo"
- rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
- rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get("%s/cfg.mk" % g["MY_PATH"])
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+ rblf.inherit(handle, _varmod, _varmod_init)
+ _entry = {
+ "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+ }.get("%s/cfg.mk" % g["MY_PATH"])
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+ rblf.inherit(handle, _varmod, _varmod_init)
`,
},
{
@@ -1116,10 +1194,22 @@
def init(g, handle):
cfg = rblf.cfg(handle)
- rblf.inherit(handle, "foo/font", _font_init)
+ _entry = {
+ "foo/font.mk": ("foo/font", _font_init),
+ }.get("%s/font.mk" % g.get("MY_VAR", ""))
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+ rblf.inherit(handle, _varmod, _varmod_init)
# There's some space and even this comment between the include_top and the inherit-product
- rblf.inherit(handle, "foo/font", _font_init)
- rblf.mkwarning("product.mk:11", "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
+ _entry = {
+ "foo/font.mk": ("foo/font", _font_init),
+ }.get("%s/font.mk" % g.get("MY_VAR", ""))
+ (_varmod, _varmod_init) = _entry if _entry else (None, None)
+ if not _varmod_init:
+ rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+ rblf.inherit(handle, _varmod, _varmod_init)
+ rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
_entry = {
"foo/font.mk": ("foo/font", _font_init),
"bar/font.mk": ("bar/font", _font1_init),
@@ -1179,7 +1269,7 @@
TEST_VAR_LIST += bar
TEST_VAR_2 := $(if $(TEST_VAR),bar)
TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
-TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
+TEST_VAR_4 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1190,7 +1280,7 @@
g["TEST_VAR_LIST"] += ["bar"]
g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
- g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
+ g["TEST_VAR_4"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
`,
},
{
@@ -1220,6 +1310,9 @@
BOOT_KERNEL_MODULES_LIST += bar.ko
BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
+FOREACH_WITH_IF := $(foreach module,\
+ $(BOOT_KERNEL_MODULES_LIST),\
+ $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!)))
`,
expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -1230,6 +1323,7 @@
g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"]
g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"]
g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]]
+ g["FOREACH_WITH_IF"] = [("" if rblf.filter(module, "foo.ko") else rblf.mkerror("product.mk", "module \"%s\" has an error!" % module)) for module in g["BOOT_KERNEL_MODULES_LIST"]]
`,
},
{
@@ -1316,6 +1410,70 @@
pass
`,
},
+ {
+ desc: "Type hints",
+ mkname: "product.mk",
+ in: `
+# Test type hints
+#RBC# type_hint list MY_VAR MY_VAR_2
+# Unsupported type
+#RBC# type_hint bool MY_VAR_3
+# Invalid syntax
+#RBC# type_hint list
+# Duplicated variable
+#RBC# type_hint list MY_VAR_2
+#RBC# type_hint list my-local-var-with-dashes
+#RBC# type_hint string MY_STRING_VAR
+
+MY_VAR := foo
+MY_VAR_UNHINTED := foo
+
+# Vars set after other statements still get the hint
+MY_VAR_2 := foo
+
+# You can't specify a type hint after the first statement
+#RBC# type_hint list MY_VAR_4
+MY_VAR_4 := foo
+
+my-local-var-with-dashes := foo
+
+MY_STRING_VAR := $(wildcard foo/bar.mk)
+`,
+ expected: `# Test type hints
+# Unsupported type
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+ rblf.mk2rbc_error("product.mk:5", "Invalid type_hint annotation. Only list/string types are accepted, found bool")
+ # Invalid syntax
+ rblf.mk2rbc_error("product.mk:7", "Invalid type_hint annotation: list. Must be a variable type followed by a list of variables of that type")
+ # Duplicated variable
+ rblf.mk2rbc_error("product.mk:9", "Duplicate type hint for variable MY_VAR_2")
+ g["MY_VAR"] = ["foo"]
+ g["MY_VAR_UNHINTED"] = "foo"
+ # Vars set after other statements still get the hint
+ g["MY_VAR_2"] = ["foo"]
+ # You can't specify a type hint after the first statement
+ rblf.mk2rbc_error("product.mk:20", "type_hint annotations must come before the first Makefile statement")
+ g["MY_VAR_4"] = "foo"
+ _my_local_var_with_dashes = ["foo"]
+ g["MY_STRING_VAR"] = " ".join(rblf.expand_wildcard("foo/bar.mk"))
+`,
+ },
+ {
+ desc: "Set LOCAL_PATH to my-dir",
+ mkname: "product.mk",
+ in: `
+LOCAL_PATH := $(call my-dir)
+`,
+ expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+ cfg = rblf.cfg(handle)
+
+`,
+ },
}
var known_variables = []struct {
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index 61aaf91..9d5af91 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -101,7 +101,7 @@
func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
if i.needsWarning {
gctx.newLine()
- gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Including a path with a non-constant prefix, please convert this to a simple literal to generate cleaner starlark.")
+ gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
}
gctx.newLine()
gctx.writef("_entry = {")
@@ -184,10 +184,9 @@
const (
// Assignment flavors
- asgnSet assignmentFlavor = iota // := or =
- asgnMaybeSet assignmentFlavor = iota // ?= and variable may be unset
- asgnAppend assignmentFlavor = iota // += and variable has been set before
- asgnMaybeAppend assignmentFlavor = iota // += and variable may be unset
+ asgnSet assignmentFlavor = iota // := or =
+ asgnMaybeSet assignmentFlavor = iota // ?=
+ asgnAppend assignmentFlavor = iota // +=
)
type assignmentNode struct {
@@ -215,6 +214,20 @@
}
}
+func (asgn *assignmentNode) isSelfReferential() bool {
+ if asgn.flavor == asgnAppend {
+ return true
+ }
+ isSelfReferential := false
+ asgn.value.transform(func(expr starlarkExpr) starlarkExpr {
+ if ref, ok := expr.(*variableRefExpr); ok && ref.ref.name() == asgn.lhs.name() {
+ isSelfReferential = true
+ }
+ return nil
+ })
+ return isSelfReferential
+}
+
type exprNode struct {
expr starlarkExpr
}
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index f7adca5..be1b174 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -88,25 +88,36 @@
}
value.emit(gctx)
}
-
- switch asgn.flavor {
- case asgnSet:
- emitAssignment()
- case asgnAppend:
- emitAppend()
- case asgnMaybeAppend:
- // If we are not sure variable has been assigned before, emit setdefault
+ emitSetDefault := func() {
if pcv.typ == starlarkTypeList {
gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
} else {
gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
}
gctx.newLine()
+ }
+
+ // If we are not sure variable has been assigned before, emit setdefault
+ needsSetDefault := asgn.previous == nil && !pcv.isPreset() && asgn.isSelfReferential()
+
+ switch asgn.flavor {
+ case asgnSet:
+ if needsSetDefault {
+ emitSetDefault()
+ }
+ emitAssignment()
+ case asgnAppend:
+ if needsSetDefault {
+ emitSetDefault()
+ }
emitAppend()
case asgnMaybeSet:
gctx.writef("if cfg.get(%q) == None:", pcv.nam)
gctx.indentLevel++
gctx.newLine()
+ if needsSetDefault {
+ emitSetDefault()
+ }
emitAssignment()
gctx.indentLevel--
}
@@ -121,7 +132,7 @@
}
func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
- gctx.writef("g.get(%q) != None", pcv.name())
+ gctx.writef("cfg.get(%q) != None", pcv.name())
}
type otherGlobalVariable struct {
@@ -146,20 +157,30 @@
value.emit(gctx)
}
+ // If we are not sure variable has been assigned before, emit setdefault
+ needsSetDefault := asgn.previous == nil && !scv.isPreset() && asgn.isSelfReferential()
+
switch asgn.flavor {
case asgnSet:
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAssignment()
case asgnAppend:
- emitAppend()
- case asgnMaybeAppend:
- // If we are not sure variable has been assigned before, emit setdefault
- gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
- gctx.newLine()
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAppend()
case asgnMaybeSet:
gctx.writef("if g.get(%q) == None:", scv.nam)
gctx.indentLevel++
gctx.newLine()
+ if needsSetDefault {
+ gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+ gctx.newLine()
+ }
emitAssignment()
gctx.indentLevel--
}
@@ -191,7 +212,7 @@
func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
switch asgn.flavor {
- case asgnSet:
+ case asgnSet, asgnMaybeSet:
gctx.writef("%s = ", lv)
asgn.value.emitListVarCopy(gctx)
case asgnAppend:
@@ -203,14 +224,6 @@
value = &toStringExpr{expr: value}
}
value.emit(gctx)
- case asgnMaybeAppend:
- gctx.writef("%s(%q, ", cfnLocalAppend, lv)
- asgn.value.emit(gctx)
- gctx.write(")")
- case asgnMaybeSet:
- gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
- asgn.value.emit(gctx)
- gctx.write(")")
}
}
@@ -278,31 +291,41 @@
// addVariable returns a variable with a given name. A variable is
// added if it does not exist yet.
func (ctx *parseContext) addVariable(name string) variable {
+ // Get the hintType before potentially changing the variable name
+ var hintType starlarkType
+ var ok bool
+ if hintType, ok = ctx.typeHints[name]; !ok {
+ hintType = starlarkTypeUnknown
+ }
+ // Heuristics: if variable's name is all lowercase, consider it local
+ // string variable.
+ isLocalVariable := name == strings.ToLower(name)
+ // Local variables can't have special characters in them, because they
+ // will be used as starlark identifiers
+ if isLocalVariable {
+ name = strings.ReplaceAll(strings.TrimSpace(name), "-", "_")
+ }
v, found := ctx.variables[name]
if !found {
- _, preset := presetVariables[name]
if vi, found := KnownVariables[name]; found {
+ _, preset := presetVariables[name]
switch vi.class {
case VarClassConfig:
v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
case VarClassSoong:
v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
}
- } else if name == strings.ToLower(name) {
- // Heuristics: if variable's name is all lowercase, consider it local
- // string variable.
- v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
+ } else if isLocalVariable {
+ v = &localVariable{baseVariable{nam: name, typ: hintType}}
} else {
- vt := starlarkTypeUnknown
- if strings.HasPrefix(name, "LOCAL_") {
- // Heuristics: local variables that contribute to corresponding config variables
- if cfgVarName, found := localProductConfigVariables[name]; found {
- vi, found2 := KnownVariables[cfgVarName]
- if !found2 {
- panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
- }
- vt = vi.valueType
+ vt := hintType
+ // Heuristics: local variables that contribute to corresponding config variables
+ if cfgVarName, found := localProductConfigVariables[name]; found && vt == starlarkTypeUnknown {
+ vi, found2 := KnownVariables[cfgVarName]
+ if !found2 {
+ panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
}
+ vt = vi.valueType
}
if strings.HasSuffix(name, "_LIST") && vt == starlarkTypeUnknown {
// Heuristics: Variables with "_LIST" suffix are lists
diff --git a/python/python.go b/python/python.go
index 734ac57..b100cc3 100644
--- a/python/python.go
+++ b/python/python.go
@@ -423,6 +423,9 @@
if ctx.Target().Os.Bionic() {
launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
}
+ if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
+ }
switch p.properties.Actual_version {
case pyVersion2:
@@ -432,6 +435,7 @@
if p.bootstrapper.autorun() {
launcherModule = "py2-launcher-autorun"
}
+
launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
case pyVersion3:
@@ -441,6 +445,9 @@
if p.bootstrapper.autorun() {
launcherModule = "py3-launcher-autorun"
}
+ if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
+ launcherModule += "-static"
+ }
if ctx.Device() {
launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
diff --git a/rust/OWNERS b/rust/OWNERS
index d07ef7e..ddaebc5 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -1,5 +1,5 @@
# Additional owner/reviewers for rust rules, including parent directory owners.
-per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
+per-file * = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
# Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
+per-file allowed_list.go = chiw@google.com, chriswailes@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 4e58632..2361e03 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -106,6 +106,9 @@
}
entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+ if test.Properties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+ }
})
cc.AndroidMkWriteTestData(test.data, ret)
diff --git a/rust/bindgen.go b/rust/bindgen.go
index f4c337d..c2b0512 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -30,7 +30,7 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r437112b"
+ bindgenClangVersion = "clang-r445002"
_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/compiler.go b/rust/compiler.go
index c5d40f4..19499fa 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -121,6 +121,12 @@
// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
Whole_static_libs []string `android:"arch_variant"`
+ // list of Rust system library dependencies.
+ //
+ // This is usually only needed when `no_stdlibs` is true, in which case it can be used to depend on system crates
+ // like `core` and `alloc`.
+ Stdlibs []string `android:"arch_variant"`
+
// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -360,6 +366,7 @@
deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
+ deps.Stdlibs = append(deps.Stdlibs, compiler.Properties.Stdlibs...)
if !Bool(compiler.Properties.No_stdlibs) {
for _, stdlib := range config.Stdlibs {
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0962168..bc36b20 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -24,6 +24,7 @@
"packages/modules/DnsResolver",
"packages/modules/Uwb",
"packages/modules/Virtualization",
+ "platform_testing/tests/codecoverage/native/rust",
"prebuilts/rust",
"system/core/libstats/pull_rust",
"system/extras/profcollectd",
@@ -37,6 +38,7 @@
"system/tools/aidl",
"tools/security/fuzzing/example_rust_fuzzer",
"tools/security/fuzzing/orphans",
+ "tools/security/remote_provisioning/cert_validator",
"tools/vendor",
"vendor/",
}
diff --git a/rust/config/global.go b/rust/config/global.go
index c1ce13f..1cf773e 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.58.1"
+ RustDefaultVersion = "1.59.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
@@ -49,7 +49,7 @@
"-C overflow-checks=on",
"-C force-unwind-tables=yes",
// Use v0 mangling to distinguish from C++ symbols
- "-Z symbol-mangling-version=v0",
+ "-C symbol-mangling-version=v0",
}
deviceGlobalRustFlags = []string{
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index a769f12..9c9d572 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -121,14 +121,7 @@
}
func LibclangRuntimeLibrary(t Toolchain, library string) string {
- arch := t.LibclangRuntimeLibraryArch()
- if arch == "" {
- return ""
- }
- if !t.Bionic() {
- return "libclang_rt." + library + "-" + arch
- }
- return "libclang_rt." + library + "-" + arch + "-android"
+ return "libclang_rt." + library
}
func LibRustRuntimeLibrary(t Toolchain, library string) string {
diff --git a/rust/coverage.go b/rust/coverage.go
index 050b811..651ce6e 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -22,6 +22,7 @@
var CovLibraryName = "libprofile-clang-extras"
+// Add '%c' to default specifier after we resolve http://b/210012154
const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
type coverage struct {
@@ -59,6 +60,10 @@
flags.LinkFlags = append(flags.LinkFlags,
profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+ if cc.EnableContinuousCoverage(ctx) {
+ flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
+ }
}
return flags, deps
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 98be7c2..865665e 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -47,17 +47,17 @@
// Check that compiler flags are set appropriately .
fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
- !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
- t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+ t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
}
// Check that dependencies have 'fuzzer' variants produced for them as well.
libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
- !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+ !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
- t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+ t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
}
}
diff --git a/rust/image.go b/rust/image.go
index 5d57f15..dfc7f74 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -149,6 +149,10 @@
return mod.ModuleBase.InRecovery() || mod.ModuleBase.InstallInRecovery()
}
+func (mod *Module) InRamdisk() bool {
+ return mod.ModuleBase.InRamdisk() || mod.ModuleBase.InstallInRamdisk()
+}
+
func (mod *Module) InVendorRamdisk() bool {
return mod.ModuleBase.InVendorRamdisk() || mod.ModuleBase.InstallInVendorRamdisk()
}
diff --git a/rust/rust.go b/rust/rust.go
index 018d1dd..1c718a4 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -968,6 +968,7 @@
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+ deps.Stdlibs = android.LastUniqueStrings(deps.Stdlibs)
deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
return deps
@@ -1192,6 +1193,10 @@
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+ // Record baseLibName for snapshots.
+ mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
case cc.IsSharedDepTag(depTag):
// For the shared lib dependencies, we may link to the stub variant
@@ -1214,7 +1219,6 @@
// Record baseLibName for snapshots.
mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
- mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
exportDep = true
diff --git a/rust/sanitize.go b/rust/sanitize.go
index be9dc42..39aaf33 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -57,7 +57,7 @@
}
var fuzzerFlags = []string{
- "-C passes='sancov'",
+ "-C passes='sancov-module'",
"--cfg fuzzing",
"-C llvm-args=-sanitizer-coverage-level=3",
@@ -70,7 +70,7 @@
"-C link-dead-code",
// Sancov breaks with lto
- // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+ // TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov-module works with LTO
"-C lto=no",
}
diff --git a/rust/testing.go b/rust/testing.go
index 1b34dfe..cb98bed 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -88,13 +88,13 @@
export_include_dirs: ["libprotobuf-cpp-full-includes"],
}
cc_library {
- name: "libclang_rt.asan-aarch64-android",
+ name: "libclang_rt.asan",
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
}
cc_library {
- name: "libclang_rt.hwasan_static-aarch64-android",
+ name: "libclang_rt.hwasan_static",
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 03bd867..7be0042 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -561,7 +561,7 @@
static_libs: [
"libvendor",
"libvndk",
- "libclang_rt.builtins-aarch64-android",
+ "libclang_rt.builtins",
"note_memtag_heap_sync",
],
shared_libs: [
@@ -589,7 +589,7 @@
static_libs: [
"libvendor",
"libvndk",
- "libclang_rt.builtins-arm-android",
+ "libclang_rt.builtins",
],
shared_libs: [
"libvendor_available",
@@ -731,19 +731,7 @@
}
vendor_snapshot_static {
- name: "libclang_rt.builtins-aarch64-android",
- version: "30",
- target_arch: "arm64",
- vendor: true,
- arch: {
- arm64: {
- src: "libclang_rt.builtins-aarch64-android.a",
- },
- },
- }
-
- vendor_snapshot_static {
- name: "libclang_rt.builtins-arm-android",
+ name: "libclang_rt.builtins",
version: "30",
target_arch: "arm64",
vendor: true,
@@ -751,6 +739,9 @@
arm: {
src: "libclang_rt.builtins-arm-android.a",
},
+ arm64: {
+ src: "libclang_rt.builtins-aarch64-android.a",
+ },
},
}
@@ -967,7 +958,7 @@
}
libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
- if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins-aarch64-android.vendor"}; !reflect.DeepEqual(g, w) {
+ if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
}
@@ -1024,7 +1015,7 @@
}
memtagStaticLibs := ctx.ModuleForTests("memtag_binary", "android_vendor.30_arm64_armv8-a").Module().(*Module).Properties.AndroidMkStaticLibs
- if g, w := memtagStaticLibs, []string{"libclang_rt.builtins-aarch64-android.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
+ if g, w := memtagStaticLibs, []string{"libclang_rt.builtins.vendor", "note_memtag_heap_sync.vendor"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted memtag_binary AndroidMkStaticLibs %q, got %q", w, g)
}
}
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index 4783037..b57963b 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -19,54 +19,9 @@
exit 1
fi
-TOP=$(pwd)
-
-source build/envsetup.sh
-PLATFORM_SDK_VERSION=$(get_build_var PLATFORM_SDK_VERSION)
-PLATFORM_VERSION_ALL_CODENAMES=$(get_build_var PLATFORM_VERSION_ALL_CODENAMES)
-
-# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
-# turn this into ["O","P"].
-PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
-PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
-
-# Get the list of missing <uses-library> modules and convert it to a JSON array
-# (quote module names, add comma separator and wrap in brackets).
-MISSING_USES_LIBRARIES="$(get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
-MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
-
-SOONG_OUT=${OUT_DIR}/soong
-SOONG_NDK_OUT=${OUT_DIR}/soong/ndk
-rm -rf ${SOONG_OUT}
-mkdir -p ${SOONG_OUT}
-
-# We only really need to set some of these variables, but soong won't merge this
-# with the defaults, so we need to write out all the defaults with our values
-# added.
-cat > ${SOONG_OUT}/soong.variables << EOF
-{
- "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
- "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
-
- "DeviceName": "generic_arm64",
- "HostArch": "x86_64",
- "Malloc_not_svelte": false,
- "Safestack": false,
-
- "Ndk_abis": true,
-
- "VendorVars": {
- "art_module": {
- "source_build": "true"
- }
- },
-
- "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
-}
-EOF
-m --soong-only --skip-config ${SOONG_OUT}/ndk.timestamp
+TARGET_PRODUCT=ndk build/soong/soong_ui.bash --make-mode --soong-only ${OUT_DIR}/soong/ndk.timestamp
if [ -n "${DIST_DIR}" ]; then
mkdir -p ${DIST_DIR} || true
- tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${SOONG_OUT} ndk
+ tar cjf ${DIST_DIR}/ndk_platform.tar.bz2 -C ${OUT_DIR}/soong ndk
fi
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 7ffda62..8a47c5d 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -69,10 +69,37 @@
},
}
+python_library_host {
+ name: "signature_trie",
+ srcs: ["signature_trie.py"],
+}
+
+python_test_host {
+ name: "signature_trie_test",
+ main: "signature_trie_test.py",
+ srcs: ["signature_trie_test.py"],
+ libs: ["signature_trie"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ test_options: {
+ unit_test: true,
+ },
+}
+
python_binary_host {
name: "verify_overlaps",
main: "verify_overlaps.py",
srcs: ["verify_overlaps.py"],
+ libs: [
+ "signature_trie",
+ ],
version: {
py2: {
enabled: false,
@@ -91,6 +118,9 @@
"verify_overlaps.py",
"verify_overlaps_test.py",
],
+ libs: [
+ "signature_trie",
+ ],
version: {
py2: {
enabled: false,
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
index e75ee95..5a82be7 100755
--- a/scripts/hiddenapi/signature_patterns.py
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -25,92 +25,138 @@
import sys
-def dict_reader(csvfile):
+def dict_reader(csv_file):
return csv.DictReader(
- csvfile, delimiter=',', quotechar='|', fieldnames=['signature'])
+ csv_file, delimiter=',', quotechar='|', fieldnames=['signature'])
-def dotPackageToSlashPackage(pkg):
+def dot_package_to_slash_package(pkg):
return pkg.replace('.', '/')
-def slashPackageToDotPackage(pkg):
+def dot_packages_to_slash_packages(pkgs):
+ return [dot_package_to_slash_package(p) for p in pkgs]
+
+
+def slash_package_to_dot_package(pkg):
return pkg.replace('/', '.')
-def isSplitPackage(splitPackages, pkg):
- return splitPackages and (pkg in splitPackages or '*' in splitPackages)
+def slash_packages_to_dot_packages(pkgs):
+ return [slash_package_to_dot_package(p) for p in pkgs]
-def matchedByPackagePrefixPattern(packagePrefixes, prefix):
- for packagePrefix in packagePrefixes:
+def is_split_package(split_packages, pkg):
+ return split_packages and (pkg in split_packages or '*' in split_packages)
+
+
+def matched_by_package_prefix_pattern(package_prefixes, prefix):
+ for packagePrefix in package_prefixes:
if prefix == packagePrefix:
return packagePrefix
- elif prefix.startswith(packagePrefix) and prefix[len(
- packagePrefix)] == '/':
+ if (prefix.startswith(packagePrefix) and
+ prefix[len(packagePrefix)] == '/'):
return packagePrefix
return False
-def validate_package_prefixes(splitPackages, packagePrefixes):
+def validate_package_is_not_matched_by_package_prefix(package_type, pkg,
+ package_prefixes):
+ package_prefix = matched_by_package_prefix_pattern(package_prefixes, pkg)
+ if package_prefix:
+ # A package prefix matches the package.
+ package_for_output = slash_package_to_dot_package(pkg)
+ package_prefix_for_output = slash_package_to_dot_package(package_prefix)
+ return [
+ f'{package_type} {package_for_output} is matched by '
+ f'package prefix {package_prefix_for_output}'
+ ]
+ return []
+
+
+def validate_package_prefixes(split_packages, single_packages,
+ package_prefixes):
# If there are no package prefixes then there is no possible conflict
# between them and the split packages.
- if len(packagePrefixes) == 0:
- return
+ if len(package_prefixes) == 0:
+ return []
# Check to make sure that the split packages and package prefixes do not
# overlap.
errors = []
- for splitPackage in splitPackages:
- if splitPackage == '*':
+ for split_package in split_packages:
+ if split_package == '*':
# A package prefix matches a split package.
- packagePrefixesForOutput = ', '.join(
- map(slashPackageToDotPackage, packagePrefixes))
+ package_prefixes_for_output = ', '.join(
+ slash_packages_to_dot_packages(package_prefixes))
errors.append(
- 'split package "*" conflicts with all package prefixes %s\n'
- ' add split_packages:[] to fix' % packagePrefixesForOutput)
+ "split package '*' conflicts with all package prefixes "
+ f'{package_prefixes_for_output}\n'
+ ' add split_packages:[] to fix')
else:
- packagePrefix = matchedByPackagePrefixPattern(
- packagePrefixes, splitPackage)
- if packagePrefix:
- # A package prefix matches a split package.
- splitPackageForOutput = slashPackageToDotPackage(splitPackage)
- packagePrefixForOutput = slashPackageToDotPackage(packagePrefix)
- errors.append(
- 'split package %s is matched by package prefix %s' %
- (splitPackageForOutput, packagePrefixForOutput))
+ errs = validate_package_is_not_matched_by_package_prefix(
+ 'split package', split_package, package_prefixes)
+ errors.extend(errs)
+
+ # Check to make sure that the single packages and package prefixes do not
+ # overlap.
+ for single_package in single_packages:
+ errs = validate_package_is_not_matched_by_package_prefix(
+ 'single package', single_package, package_prefixes)
+ errors.extend(errs)
return errors
-def validate_split_packages(splitPackages):
+def validate_split_packages(split_packages):
errors = []
- if '*' in splitPackages and len(splitPackages) > 1:
+ if '*' in split_packages and len(split_packages) > 1:
errors.append('split packages are invalid as they contain both the'
' wildcard (*) and specific packages, use the wildcard or'
' specific packages, not a mixture')
return errors
-def produce_patterns_from_file(file, splitPackages=None, packagePrefixes=None):
- with open(file, 'r') as f:
- return produce_patterns_from_stream(f, splitPackages, packagePrefixes)
+def validate_single_packages(split_packages, single_packages):
+ overlaps = []
+ for single_package in single_packages:
+ if single_package in split_packages:
+ overlaps.append(single_package)
+ if overlaps:
+ indented = ''.join([f'\n {o}' for o in overlaps])
+ return [
+ f'single_packages and split_packages overlap, please ensure the '
+ f'following packages are only present in one:{indented}'
+ ]
+ return []
+
+
+def produce_patterns_from_file(file,
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ with open(file, 'r', encoding='utf8') as f:
+ return produce_patterns_from_stream(f, split_packages, single_packages,
+ package_prefixes)
def produce_patterns_from_stream(stream,
- splitPackages=None,
- packagePrefixes=None):
- splitPackages = set(splitPackages or [])
- packagePrefixes = list(packagePrefixes or [])
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ split_packages = set(split_packages or [])
+ single_packages = set(single_packages or [])
+ package_prefixes = list(package_prefixes or [])
# Read in all the signatures into a list and remove any unnecessary class
# and member names.
patterns = set()
+ unmatched_packages = set()
for row in dict_reader(stream):
signature = row['signature']
text = signature.removeprefix('L')
# Remove the class specific member signature
pieces = text.split(';->')
- qualifiedClassName = pieces[0]
- pieces = qualifiedClassName.rsplit('/', maxsplit=1)
+ qualified_class_name = pieces[0]
+ pieces = qualified_class_name.rsplit('/', maxsplit=1)
pkg = pieces[0]
# If the package is split across multiple modules then it cannot be used
# to select the subset of the monolithic flags that this module
@@ -121,27 +167,54 @@
# If the package is not split then every class in the package must be
# provided by this module so there is no need to list the classes
# explicitly so just use the package name instead.
- if isSplitPackage(splitPackages, pkg):
+ if is_split_package(split_packages, pkg):
# Remove inner class names.
- pieces = qualifiedClassName.split('$', maxsplit=1)
+ pieces = qualified_class_name.split('$', maxsplit=1)
pattern = pieces[0]
- else:
+ patterns.add(pattern)
+ elif pkg in single_packages:
# Add a * to ensure that the pattern matches the classes in that
# package.
pattern = pkg + '/*'
- patterns.add(pattern)
+ patterns.add(pattern)
+ else:
+ unmatched_packages.add(pkg)
+
+ # Remove any unmatched packages that would be matched by a package prefix
+ # pattern.
+ unmatched_packages = [
+ p for p in unmatched_packages
+ if not matched_by_package_prefix_pattern(package_prefixes, p)
+ ]
+ errors = []
+ if unmatched_packages:
+ unmatched_packages.sort()
+ indented = ''.join([
+ f'\n {slash_package_to_dot_package(p)}'
+ for p in unmatched_packages
+ ])
+ errors.append('The following packages were unexpected, please add them '
+ 'to one of the hidden_api properties, split_packages, '
+ f'single_packages or package_prefixes:{indented}')
# Remove any patterns that would be matched by a package prefix pattern.
- patterns = list(
- filter(lambda p: not matchedByPackagePrefixPattern(packagePrefixes, p),
- patterns))
+ patterns = [
+ p for p in patterns
+ if not matched_by_package_prefix_pattern(package_prefixes, p)
+ ]
# Add the package prefix patterns to the list. Add a ** to ensure that each
# package prefix pattern will match the classes in that package and all
# sub-packages.
- patterns = patterns + list(map(lambda x: x + '/**', packagePrefixes))
+ patterns = patterns + [f'{p}/**' for p in package_prefixes]
# Sort the patterns.
patterns.sort()
- return patterns
+ return patterns, errors
+
+
+def print_and_exit(errors):
+ for error in errors:
+ print(error)
+ sys.exit(1)
def main(args):
@@ -155,35 +228,50 @@
args_parser.add_argument(
'--split-package',
action='append',
- help='A package that is split across multiple bootclasspath_fragment modules'
- )
+ help='A package that is split across multiple bootclasspath_fragment '
+ 'modules')
args_parser.add_argument(
'--package-prefix',
action='append',
help='A package prefix unique to this set of flags')
+ args_parser.add_argument(
+ '--single-package',
+ action='append',
+ help='A single package unique to this set of flags')
args_parser.add_argument('--output', help='Generated signature prefixes')
args = args_parser.parse_args(args)
- splitPackages = set(map(dotPackageToSlashPackage, args.split_package or []))
- errors = validate_split_packages(splitPackages)
+ split_packages = set(
+ dot_packages_to_slash_packages(args.split_package or []))
+ errors = validate_split_packages(split_packages)
+ if errors:
+ print_and_exit(errors)
- packagePrefixes = list(
- map(dotPackageToSlashPackage, args.package_prefix or []))
+ single_packages = list(
+ dot_packages_to_slash_packages(args.single_package or []))
- if not errors:
- errors = validate_package_prefixes(splitPackages, packagePrefixes)
+ errors = validate_single_packages(split_packages, single_packages)
+ if errors:
+ print_and_exit(errors)
+
+ package_prefixes = dot_packages_to_slash_packages(args.package_prefix or [])
+
+ errors = validate_package_prefixes(split_packages, single_packages,
+ package_prefixes)
+ if errors:
+ print_and_exit(errors)
+
+ patterns = []
+ # Read in all the patterns into a list.
+ patterns, errors = produce_patterns_from_file(args.flags, split_packages,
+ single_packages,
+ package_prefixes)
if errors:
- for error in errors:
- print(error)
- sys.exit(1)
-
- # Read in all the patterns into a list.
- patterns = produce_patterns_from_file(args.flags, splitPackages,
- packagePrefixes)
+ print_and_exit(errors)
# Write out all the patterns.
- with open(args.output, 'w') as outputFile:
+ with open(args.output, 'w', encoding='utf8') as outputFile:
for pattern in patterns:
outputFile.write(pattern)
outputFile.write('\n')
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
index b59dfd7..90b3d0b 100755
--- a/scripts/hiddenapi/signature_patterns_test.py
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -17,37 +17,52 @@
import io
import unittest
-from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+import signature_patterns
class TestGeneratedPatterns(unittest.TestCase):
- csvFlags = """
+ csv_flags = """
Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
"""
- def produce_patterns_from_string(self,
- csv,
- splitPackages=None,
- packagePrefixes=None):
- with io.StringIO(csv) as f:
- return produce_patterns_from_stream(f, splitPackages,
- packagePrefixes)
+ @staticmethod
+ def produce_patterns_from_string(csv_text,
+ split_packages=None,
+ single_packages=None,
+ package_prefixes=None):
+ with io.StringIO(csv_text) as f:
+ return signature_patterns.produce_patterns_from_stream(
+ f, split_packages, single_packages, package_prefixes)
+
+ def test_generate_unmatched(self):
+ _, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags)
+ self.assertEqual([
+ 'The following packages were unexpected, please add them to one of '
+ 'the hidden_api properties, split_packages, single_packages or '
+ 'package_prefixes:\n'
+ ' java.lang'
+ ], errors)
def test_generate_default(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags)
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, single_packages=['java/lang'])
+ self.assertEqual([], errors)
+
expected = [
'java/lang/*',
]
self.assertEqual(expected, patterns)
def test_generate_split_package(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, splitPackages={'java/lang'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, split_packages={'java/lang'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/Character',
'java/lang/Object',
@@ -56,8 +71,10 @@
self.assertEqual(expected, patterns)
def test_generate_split_package_wildcard(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, splitPackages={'*'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, split_packages={'*'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/Character',
'java/lang/Object',
@@ -66,23 +83,27 @@
self.assertEqual(expected, patterns)
def test_generate_package_prefix(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, packagePrefixes={'java/lang'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, package_prefixes={'java/lang'})
+ self.assertEqual([], errors)
+
expected = [
'java/lang/**',
]
self.assertEqual(expected, patterns)
def test_generate_package_prefix_top_package(self):
- patterns = self.produce_patterns_from_string(
- TestGeneratedPatterns.csvFlags, packagePrefixes={'java'})
+ patterns, errors = self.produce_patterns_from_string(
+ TestGeneratedPatterns.csv_flags, package_prefixes={'java'})
+ self.assertEqual([], errors)
+
expected = [
'java/**',
]
self.assertEqual(expected, patterns)
def test_split_package_wildcard_conflicts_with_other_split_packages(self):
- errors = validate_split_packages({'*', 'java'})
+ errors = signature_patterns.validate_split_packages({'*', 'java'})
expected = [
'split packages are invalid as they contain both the wildcard (*)'
' and specific packages, use the wildcard or specific packages,'
@@ -91,21 +112,39 @@
self.assertEqual(expected, errors)
def test_split_package_wildcard_conflicts_with_package_prefixes(self):
- errors = validate_package_prefixes({'*'}, packagePrefixes={'java'})
+ errors = signature_patterns.validate_package_prefixes(
+ {'*'}, [], package_prefixes={'java'})
expected = [
- 'split package "*" conflicts with all package prefixes java\n'
+ "split package '*' conflicts with all package prefixes java\n"
' add split_packages:[] to fix',
]
self.assertEqual(expected, errors)
- def test_split_package_conflict(self):
- errors = validate_package_prefixes({'java/split'},
- packagePrefixes={'java'})
+ def test_split_package_conflicts_with_package_prefixes(self):
+ errors = signature_patterns.validate_package_prefixes(
+ {'java/split'}, [], package_prefixes={'java'})
expected = [
'split package java.split is matched by package prefix java',
]
self.assertEqual(expected, errors)
+ def test_single_package_conflicts_with_package_prefixes(self):
+ errors = signature_patterns.validate_package_prefixes(
+ {}, ['java/single'], package_prefixes={'java'})
+ expected = [
+ 'single package java.single is matched by package prefix java',
+ ]
+ self.assertEqual(expected, errors)
+
+ def test_single_package_conflicts_with_split_packages(self):
+ errors = signature_patterns.validate_single_packages({'java/pkg'},
+ ['java/pkg'])
+ expected = [
+ 'single_packages and split_packages overlap, please ensure the '
+ 'following packages are only present in one:\n java/pkg'
+ ]
+ self.assertEqual(expected, errors)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/signature_trie.py b/scripts/hiddenapi/signature_trie.py
new file mode 100644
index 0000000..e813a97
--- /dev/null
+++ b/scripts/hiddenapi/signature_trie.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Verify that one set of hidden API flags is a subset of another."""
+import dataclasses
+import typing
+
+from itertools import chain
+
+
+@dataclasses.dataclass()
+class Node:
+ """A node in the signature trie."""
+
+ # The type of the node.
+ #
+ # Leaf nodes are of type "member".
+ # Interior nodes can be either "package", or "class".
+ type: str
+
+ # The selector of the node.
+ #
+ # That is a string that can be used to select the node, e.g. in a pattern
+ # that is passed to InteriorNode.get_matching_rows().
+ selector: str
+
+ def values(self, selector):
+ """Get the values from a set of selected nodes.
+
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+
+ :return: A list of iterables of all the values associated with
+ this node and its children.
+ """
+ raise NotImplementedError("Please Implement this method")
+
+ def append_values(self, values, selector):
+ """Append the values associated with this node and its children.
+
+ For each item (key, child) in nodes the child node's values are returned
+ if and only if the selector returns True when called on its key. A child
+ node's values are all the values associated with it and all its
+ descendant nodes.
+
+ :param selector: a function that can be applied to a key in the nodes
+ attribute to determine whether to return its values.
+ :param values: a list of a iterables of values.
+ """
+ raise NotImplementedError("Please Implement this method")
+
+ def child_nodes(self):
+ """Get an iterable of the child nodes of this node."""
+ raise NotImplementedError("Please Implement this method")
+
+
+# pylint: disable=line-too-long
+@dataclasses.dataclass()
+class InteriorNode(Node):
+ """An interior node in a trie.
+
+ Each interior node has a dict that maps from an element of a signature to
+ either another interior node or a leaf. Each interior node represents either
+ a package, class or nested class. Class members are represented by a Leaf.
+
+ Associating the set of flags [public-api] with the signature
+ "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
+ nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Object -> Node()
+ ^- member:String()Ljava/lang/String; -> Leaf([public-api])
+
+ Associating the set of flags [blocked,core-platform-api] with the signature
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
+ will cause the following nodes to be created:
+ Node()
+ ^- package:java -> Node()
+ ^- package:lang -> Node()
+ ^- class:Character -> Node()
+ ^- class:UnicodeScript -> Node()
+ ^- member:of(I)Ljava/lang/Character$UnicodeScript;
+ -> Leaf([blocked,core-platform-api])
+ """
+
+ # pylint: enable=line-too-long
+
+ # A dict from an element of the signature to the Node/Leaf containing the
+ # next element/value.
+ nodes: typing.Dict[str, Node] = dataclasses.field(default_factory=dict)
+
+ # pylint: disable=line-too-long
+ @staticmethod
+ def signature_to_elements(signature):
+ """Split a signature or a prefix into a number of elements:
+
+ 1. The packages (excluding the leading L preceding the first package).
+ 2. The class names, from outermost to innermost.
+ 3. The member signature.
+ e.g.
+ Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ will be broken down into these elements:
+ 1. package:java
+ 2. package:lang
+ 3. class:Character
+ 4. class:UnicodeScript
+ 5. member:of(I)Ljava/lang/Character$UnicodeScript;
+ """
+ # Remove the leading L.
+ # - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+ text = signature.removeprefix("L")
+ # Split the signature between qualified class name and the class member
+ # signature.
+ # 0 - java/lang/Character$UnicodeScript
+ # 1 - of(I)Ljava/lang/Character$UnicodeScript;
+ parts = text.split(";->")
+ # If there is no member then this will be an empty list.
+ member = parts[1:]
+ # Split the qualified class name into packages, and class name.
+ # 0 - java
+ # 1 - lang
+ # 2 - Character$UnicodeScript
+ elements = parts[0].split("/")
+ last_element = elements[-1]
+ wildcard = []
+ classes = []
+ if "*" in last_element:
+ if last_element not in ("*", "**"):
+ raise Exception(f"Invalid signature '{signature}': invalid "
+ f"wildcard '{last_element}'")
+ packages = elements[0:-1]
+ # Cannot specify a wildcard and target a specific member
+ if member:
+ raise Exception(f"Invalid signature '{signature}': contains "
+ f"wildcard '{last_element}' and "
+ f"member signature '{member[0]}'")
+ wildcard = [last_element]
+ elif last_element.islower():
+ raise Exception(f"Invalid signature '{signature}': last element "
+ f"'{last_element}' is lower case but should be an "
+ f"upper case class name or wildcard")
+ else:
+ packages = elements[0:-1]
+ # Split the class name into outer / inner classes
+ # 0 - Character
+ # 1 - UnicodeScript
+ classes = last_element.removesuffix(";").split("$")
+
+ # Assemble the parts into a single list, adding prefixes to identify
+ # the different parts. If a wildcard is provided then it looks something
+ # like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - *
+ #
+ # Otherwise, it looks something like this:
+ # 0 - package:java
+ # 1 - package:lang
+ # 2 - class:Character
+ # 3 - class:UnicodeScript
+ # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+ return list(
+ chain([("package", x) for x in packages],
+ [("class", x) for x in classes],
+ [("member", x) for x in member],
+ [("wildcard", x) for x in wildcard]))
+
+ # pylint: enable=line-too-long
+
+ @staticmethod
+ def split_element(element):
+ element_type, element_value = element
+ return element_type, element_value
+
+ @staticmethod
+ def element_type(element):
+ element_type, _ = InteriorNode.split_element(element)
+ return element_type
+
+ @staticmethod
+ def elements_to_selector(elements):
+ """Compute a selector for a set of elements.
+
+ A selector uniquely identifies a specific Node in the trie. It is
+ essentially a prefix of a signature (without the leading L).
+
+ e.g. a trie containing "Ljava/lang/Object;->String()Ljava/lang/String;"
+ would contain nodes with the following selectors:
+ * "java"
+ * "java/lang"
+ * "java/lang/Object"
+ * "java/lang/Object;->String()Ljava/lang/String;"
+ """
+ signature = ""
+ preceding_type = ""
+ for element in elements:
+ element_type, element_value = InteriorNode.split_element(element)
+ separator = ""
+ if element_type == "package":
+ separator = "/"
+ elif element_type == "class":
+ if preceding_type == "class":
+ separator = "$"
+ else:
+ separator = "/"
+ elif element_type == "wildcard":
+ separator = "/"
+ elif element_type == "member":
+ separator += ";->"
+
+ if signature:
+ signature += separator
+
+ signature += element_value
+
+ preceding_type = element_type
+
+ return signature
+
+ def add(self, signature, value, only_if_matches=False):
+ """Associate the value with the specific signature.
+
+ :param signature: the member signature
+ :param value: the value to associated with the signature
+ :param only_if_matches: True if the value is added only if the signature
+ matches at least one of the existing top level packages.
+ :return: n/a
+ """
+ # Split the signature into elements.
+ elements = self.signature_to_elements(signature)
+ # Find the Node associated with the deepest class.
+ node = self
+ for index, element in enumerate(elements[:-1]):
+ if element in node.nodes:
+ node = node.nodes[element]
+ elif only_if_matches and index == 0:
+ return
+ else:
+ selector = self.elements_to_selector(elements[0:index + 1])
+ next_node = InteriorNode(
+ type=InteriorNode.element_type(element), selector=selector)
+ node.nodes[element] = next_node
+ node = next_node
+ # Add a Leaf containing the value and associate it with the member
+ # signature within the class.
+ last_element = elements[-1]
+ last_element_type = self.element_type(last_element)
+ if last_element_type != "member":
+ raise Exception(
+ f"Invalid signature: {signature}, does not identify a "
+ "specific member")
+ if last_element in node.nodes:
+ raise Exception(f"Duplicate signature: {signature}")
+ leaf = Leaf(
+ type=last_element_type,
+ selector=signature,
+ value=value,
+ )
+ node.nodes[last_element] = leaf
+
+ def get_matching_rows(self, pattern):
+ """Get the values (plural) associated with the pattern.
+
+ e.g. If the pattern is a full signature then this will return a list
+ containing the value associated with that signature.
+
+ If the pattern is a class then this will return a list containing the
+ values associated with all members of that class.
+
+ If the pattern ends with "*" then the preceding part is treated as a
+ package and this will return a list containing the values associated
+ with all the members of all the classes in that package.
+
+ If the pattern ends with "**" then the preceding part is treated
+ as a package and this will return a list containing the values
+ associated with all the members of all the classes in that package and
+ all sub-packages.
+
+ :param pattern: the pattern which could be a complete signature or a
+ class, or package wildcard.
+ :return: an iterable containing all the values associated with the
+ pattern.
+ """
+ elements = self.signature_to_elements(pattern)
+ node = self
+
+ # Include all values from this node and all its children.
+ selector = lambda x: True
+
+ last_element = elements[-1]
+ last_element_type, last_element_value = self.split_element(last_element)
+ if last_element_type == "wildcard":
+ elements = elements[:-1]
+ if last_element_value == "*":
+ # Do not include values from sub-packages.
+ selector = lambda x: InteriorNode.element_type(x) != "package"
+
+ for element in elements:
+ if element in node.nodes:
+ node = node.nodes[element]
+ else:
+ return []
+ return chain.from_iterable(node.values(selector))
+
+ def values(self, selector):
+ values = []
+ self.append_values(values, selector)
+ return values
+
+ def append_values(self, values, selector):
+ for key, node in self.nodes.items():
+ if selector(key):
+ node.append_values(values, lambda x: True)
+
+ def child_nodes(self):
+ return self.nodes.values()
+
+
+@dataclasses.dataclass()
+class Leaf(Node):
+ """A leaf of the trie"""
+
+ # The value associated with this leaf.
+ value: typing.Any
+
+ def values(self, selector):
+ return [[self.value]]
+
+ def append_values(self, values, selector):
+ values.append([self.value])
+
+ def child_nodes(self):
+ return []
+
+
+def signature_trie():
+ return InteriorNode(type="root", selector="")
diff --git a/scripts/hiddenapi/signature_trie_test.py b/scripts/hiddenapi/signature_trie_test.py
new file mode 100755
index 0000000..1295691
--- /dev/null
+++ b/scripts/hiddenapi/signature_trie_test.py
@@ -0,0 +1,232 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for verify_overlaps_test.py."""
+import io
+import unittest
+
+from signature_trie import InteriorNode
+from signature_trie import signature_trie
+
+
+class TestSignatureToElements(unittest.TestCase):
+
+ @staticmethod
+ def signature_to_elements(signature):
+ return InteriorNode.signature_to_elements(signature)
+
+ @staticmethod
+ def elements_to_signature(elements):
+ return InteriorNode.elements_to_selector(elements)
+
+ def test_nested_inner_classes(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "ProcessBuilder"),
+ ("class", "Redirect"),
+ ("class", "1"),
+ ("member", "<init>()V"),
+ ]
+ signature = "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_basic_member(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "Object"),
+ ("member", "hashCode()I"),
+ ]
+ signature = "Ljava/lang/Object;->hashCode()I"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_double_dollar_class(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "CharSequence"),
+ ("class", ""),
+ ("class", "ExternalSyntheticLambda0"),
+ ("member", "<init>(Ljava/lang/CharSequence;)V"),
+ ]
+ signature = "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;" \
+ "-><init>(Ljava/lang/CharSequence;)V"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_no_member(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("class", "CharSequence"),
+ ("class", ""),
+ ("class", "ExternalSyntheticLambda0"),
+ ]
+ signature = "Ljava/lang/CharSequence$$ExternalSyntheticLambda0"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_wildcard(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("wildcard", "*"),
+ ]
+ signature = "java/lang/*"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_recursive_wildcard(self):
+ elements = [
+ ("package", "java"),
+ ("package", "lang"),
+ ("wildcard", "**"),
+ ]
+ signature = "java/lang/**"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_no_packages_wildcard(self):
+ elements = [
+ ("wildcard", "*"),
+ ]
+ signature = "*"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_no_packages_recursive_wildcard(self):
+ elements = [
+ ("wildcard", "**"),
+ ]
+ signature = "**"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, self.elements_to_signature(elements))
+
+ def test_invalid_no_class_or_wildcard(self):
+ signature = "java/lang"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(signature)
+ self.assertIn(
+ "last element 'lang' is lower case but should be an "
+ "upper case class name or wildcard", str(context.exception))
+
+ def test_non_standard_class_name(self):
+ elements = [
+ ("package", "javax"),
+ ("package", "crypto"),
+ ("class", "extObjectInputStream"),
+ ]
+ signature = "Ljavax/crypto/extObjectInputStream"
+ self.assertEqual(elements, self.signature_to_elements(signature))
+ self.assertEqual(signature, "L" + self.elements_to_signature(elements))
+
+ def test_invalid_pattern_wildcard(self):
+ pattern = "Ljava/lang/Class*"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(pattern)
+ self.assertIn("invalid wildcard 'Class*'", str(context.exception))
+
+ def test_invalid_pattern_wildcard_and_member(self):
+ pattern = "Ljava/lang/*;->hashCode()I"
+ with self.assertRaises(Exception) as context:
+ self.signature_to_elements(pattern)
+ self.assertIn(
+ "contains wildcard '*' and member signature 'hashCode()I'",
+ str(context.exception))
+
+
+class TestGetMatchingRows(unittest.TestCase):
+ extractInput = """
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+Ljava/lang/Character;->serialVersionUID:J
+Ljava/lang/Object;->hashCode()I
+Ljava/lang/Object;->toString()Ljava/lang/String;
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V
+Ljava/util/zip/ZipFile;-><clinit>()V
+"""
+
+ def read_trie(self):
+ trie = signature_trie()
+ with io.StringIO(self.extractInput.strip()) as f:
+ for line in iter(f.readline, ""):
+ line = line.rstrip()
+ trie.add(line, line)
+ return trie
+
+ def check_patterns(self, pattern, expected):
+ trie = self.read_trie()
+ self.check_node_patterns(trie, pattern, expected)
+
+ def check_node_patterns(self, node, pattern, expected):
+ actual = list(node.get_matching_rows(pattern))
+ actual.sort()
+ self.assertEqual(expected, actual)
+
+ def test_member_pattern(self):
+ self.check_patterns("java/util/zip/ZipFile;-><clinit>()V",
+ ["Ljava/util/zip/ZipFile;-><clinit>()V"])
+
+ def test_class_pattern(self):
+ self.check_patterns("java/lang/Object", [
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ ])
+
+ # pylint: disable=line-too-long
+ def test_nested_class_pattern(self):
+ self.check_patterns("java/lang/Character", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ ])
+
+ def test_wildcard(self):
+ self.check_patterns("java/lang/*", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ ])
+
+ def test_recursive_wildcard(self):
+ self.check_patterns("java/**", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ "Ljava/util/zip/ZipFile;-><clinit>()V",
+ ])
+
+ def test_node_wildcard(self):
+ trie = self.read_trie()
+ node = list(trie.child_nodes())[0]
+ self.check_node_patterns(node, "**", [
+ "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;",
+ "Ljava/lang/Character;->serialVersionUID:J",
+ "Ljava/lang/Object;->hashCode()I",
+ "Ljava/lang/Object;->toString()Ljava/lang/String;",
+ "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V",
+ "Ljava/util/zip/ZipFile;-><clinit>()V",
+ ])
+
+ # pylint: enable=line-too-long
+
+
+if __name__ == "__main__":
+ unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index 4cd7e63..e5214df 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -13,239 +13,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Verify that one set of hidden API flags is a subset of another.
-"""
+"""Verify that one set of hidden API flags is a subset of another."""
import argparse
import csv
import sys
from itertools import chain
-#pylint: disable=line-too-long
-class InteriorNode:
- """An interior node in a trie.
-
- Each interior node has a dict that maps from an element of a signature to
- either another interior node or a leaf. Each interior node represents either
- a package, class or nested class. Class members are represented by a Leaf.
-
- Associating the set of flags [public-api] with the signature
- "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
- nodes to be created:
- Node()
- ^- package:java -> Node()
- ^- package:lang -> Node()
- ^- class:Object -> Node()
- ^- member:String()Ljava/lang/String; -> Leaf([public-api])
-
- Associating the set of flags [blocked,core-platform-api] with the signature
- "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
- will cause the following nodes to be created:
- Node()
- ^- package:java -> Node()
- ^- package:lang -> Node()
- ^- class:Character -> Node()
- ^- class:UnicodeScript -> Node()
- ^- member:of(I)Ljava/lang/Character$UnicodeScript;
- -> Leaf([blocked,core-platform-api])
-
- Attributes:
- nodes: a dict from an element of the signature to the Node/Leaf
- containing the next element/value.
- """
- #pylint: enable=line-too-long
-
- def __init__(self):
- self.nodes = {}
-
- #pylint: disable=line-too-long
- def signatureToElements(self, signature):
- """Split a signature or a prefix into a number of elements:
- 1. The packages (excluding the leading L preceding the first package).
- 2. The class names, from outermost to innermost.
- 3. The member signature.
- e.g.
- Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
- will be broken down into these elements:
- 1. package:java
- 2. package:lang
- 3. class:Character
- 4. class:UnicodeScript
- 5. member:of(I)Ljava/lang/Character$UnicodeScript;
- """
- # Remove the leading L.
- # - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
- text = signature.removeprefix("L")
- # Split the signature between qualified class name and the class member
- # signature.
- # 0 - java/lang/Character$UnicodeScript
- # 1 - of(I)Ljava/lang/Character$UnicodeScript;
- parts = text.split(";->")
- member = parts[1:]
- # Split the qualified class name into packages, and class name.
- # 0 - java
- # 1 - lang
- # 2 - Character$UnicodeScript
- elements = parts[0].split("/")
- packages = elements[0:-1]
- className = elements[-1]
- if className in ("*" , "**"): #pylint: disable=no-else-return
- # Cannot specify a wildcard and target a specific member
- if len(member) != 0:
- raise Exception(
- "Invalid signature %s: contains wildcard %s and member " \
- "signature %s"
- % (signature, className, member[0]))
- wildcard = [className]
- # Assemble the parts into a single list, adding prefixes to identify
- # the different parts.
- # 0 - package:java
- # 1 - package:lang
- # 2 - *
- return list(
- chain(["package:" + x for x in packages], wildcard))
- else:
- # Split the class name into outer / inner classes
- # 0 - Character
- # 1 - UnicodeScript
- classes = className.split("$")
- # Assemble the parts into a single list, adding prefixes to identify
- # the different parts.
- # 0 - package:java
- # 1 - package:lang
- # 2 - class:Character
- # 3 - class:UnicodeScript
- # 4 - member:of(I)Ljava/lang/Character$UnicodeScript;
- return list(
- chain(
- ["package:" + x for x in packages],
- ["class:" + x for x in classes],
- ["member:" + x for x in member]))
- #pylint: enable=line-too-long
-
- def add(self, signature, value):
- """Associate the value with the specific signature.
-
- :param signature: the member signature
- :param value: the value to associated with the signature
- :return: n/a
- """
- # Split the signature into elements.
- elements = self.signatureToElements(signature)
- # Find the Node associated with the deepest class.
- node = self
- for element in elements[:-1]:
- if element in node.nodes:
- node = node.nodes[element]
- else:
- next_node = InteriorNode()
- node.nodes[element] = next_node
- node = next_node
- # Add a Leaf containing the value and associate it with the member
- # signature within the class.
- lastElement = elements[-1]
- if not lastElement.startswith("member:"):
- raise Exception(
- "Invalid signature: %s, does not identify a specific member" %
- signature)
- if lastElement in node.nodes:
- raise Exception("Duplicate signature: %s" % signature)
- node.nodes[lastElement] = Leaf(value)
-
- def getMatchingRows(self, pattern):
- """Get the values (plural) associated with the pattern.
-
- e.g. If the pattern is a full signature then this will return a list
- containing the value associated with that signature.
-
- If the pattern is a class then this will return a list containing the
- values associated with all members of that class.
-
- If the pattern is a package then this will return a list containing the
- values associated with all the members of all the classes in that
- package and sub-packages.
-
- If the pattern ends with "*" then the preceding part is treated as a
- package and this will return a list containing the values associated
- with all the members of all the classes in that package.
-
- If the pattern ends with "**" then the preceding part is treated
- as a package and this will return a list containing the values
- associated with all the members of all the classes in that package and
- all sub-packages.
-
- :param pattern: the pattern which could be a complete signature or a
- class, or package wildcard.
- :return: an iterable containing all the values associated with the
- pattern.
- """
- elements = self.signatureToElements(pattern)
- node = self
- # Include all values from this node and all its children.
- selector = lambda x: True
- lastElement = elements[-1]
- if lastElement in ("*", "**"):
- elements = elements[:-1]
- if lastElement == "*":
- # Do not include values from sub-packages.
- selector = lambda x: not x.startswith("package:")
- for element in elements:
- if element in node.nodes:
- node = node.nodes[element]
- else:
- return []
- return chain.from_iterable(node.values(selector))
-
- def values(self, selector):
- """:param selector: a function that can be applied to a key in the nodes
- attribute to determine whether to return its values.
-
- :return: A list of iterables of all the values associated with
- this node and its children.
- """
- values = []
- self.appendValues(values, selector)
- return values
-
- def appendValues(self, values, selector):
- """Append the values associated with this node and its children to the
- list.
-
- For each item (key, child) in nodes the child node's values are returned
- if and only if the selector returns True when called on its key. A child
- node's values are all the values associated with it and all its
- descendant nodes.
-
- :param selector: a function that can be applied to a key in the nodes
- attribute to determine whether to return its values.
- :param values: a list of a iterables of values.
- """
- for key, node in self.nodes.items():
- if selector(key):
- node.appendValues(values, lambda x: True)
-
-
-class Leaf:
- """A leaf of the trie
-
- Attributes:
- value: the value associated with this leaf.
- """
-
- def __init__(self, value):
- self.value = value
-
- def values(self, selector): #pylint: disable=unused-argument
- """:return: A list of a list of the value associated with this node.
- """
- return [[self.value]]
-
- def appendValues(self, values, selector): #pylint: disable=unused-argument
- """Appends a list of the value associated with this node to the list.
-
- :param values: a list of a iterables of values.
- """
- values.append([self.value])
+from signature_trie import signature_trie
def dict_reader(csvfile):
@@ -259,7 +34,7 @@
def read_flag_trie_from_stream(stream):
- trie = InteriorNode()
+ trie = signature_trie()
reader = dict_reader(stream)
for row in reader:
signature = row["signature"]
@@ -269,8 +44,7 @@
def extract_subset_from_monolithic_flags_as_dict_from_file(
monolithicTrie, patternsFile):
- """Extract a subset of flags from the dict containing all the monolithic
- flags.
+ """Extract a subset of flags from the dict of monolithic flags.
:param monolithicFlagsDict: the dict containing all the monolithic flags.
:param patternsFile: a file containing a list of signature patterns that
@@ -284,8 +58,7 @@
def extract_subset_from_monolithic_flags_as_dict_from_stream(
monolithicTrie, stream):
- """Extract a subset of flags from the trie containing all the monolithic
- flags.
+ """Extract a subset of flags from the trie of monolithic flags.
:param monolithicTrie: the trie containing all the monolithic flags.
:param stream: a stream containing a list of signature patterns that define
@@ -295,7 +68,7 @@
dict_signature_to_row = {}
for pattern in stream:
pattern = pattern.rstrip()
- rows = monolithicTrie.getMatchingRows(pattern)
+ rows = monolithicTrie.get_matching_rows(pattern)
for row in rows:
signature = row["signature"]
dict_signature_to_row[signature] = row
@@ -303,8 +76,10 @@
def read_signature_csv_from_stream_as_dict(stream):
- """Read the csv contents from the stream into a dict. The first column is
- assumed to be the signature and used as the key.
+ """Read the csv contents from the stream into a dict.
+
+ The first column is assumed to be the signature and used as the
+ key.
The whole row is stored as the value.
:param stream: the csv contents to read
@@ -319,8 +94,10 @@
def read_signature_csv_from_file_as_dict(csvFile):
- """Read the csvFile into a dict. The first column is assumed to be the
- signature and used as the key.
+ """Read the csvFile into a dict.
+
+ The first column is assumed to be the signature and used as the
+ key.
The whole row is stored as the value.
:param csvFile: the csv file to read
@@ -363,8 +140,7 @@
def main(argv):
args_parser = argparse.ArgumentParser(
description="Verify that sets of hidden API flags are each a subset of "
- "the monolithic flag file."
- )
+ "the monolithic flag file.")
args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
args_parser.add_argument(
"modularFlags",
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 22a1cdf..8cf2959 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -17,54 +17,9 @@
import io
import unittest
-from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
+from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
-class TestSignatureToElements(unittest.TestCase):
-
- def signatureToElements(self, signature):
- return InteriorNode().signatureToElements(signature)
-
- def test_signatureToElements_1(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:ProcessBuilder',
- 'class:Redirect',
- 'class:1',
- 'member:<init>()V',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements(
- 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V'))
-
- def test_signatureToElements_2(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:Object',
- 'member:hashCode()I',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements('Ljava/lang/Object;->hashCode()I'))
-
- def test_signatureToElements_3(self):
- expected = [
- 'package:java',
- 'package:lang',
- 'class:CharSequence',
- 'class:',
- 'class:ExternalSyntheticLambda0',
- 'member:<init>(Ljava/lang/CharSequence;)V',
- ]
- self.assertEqual(
- expected,
- self.signatureToElements(
- 'Ljava/lang/CharSequence$$ExternalSyntheticLambda0;'
- '-><init>(Ljava/lang/CharSequence;)V'))
-
#pylint: disable=line-too-long
class TestDetectOverlaps(unittest.TestCase):
@@ -239,18 +194,6 @@
}
self.assertEqual(expected, subset)
- def test_extract_subset_invalid_pattern_wildcard_and_member(self):
- monolithic = self.read_flag_trie_from_string(
- TestDetectOverlaps.extractInput)
-
- patterns = 'Ljava/lang/*;->hashCode()I'
-
- with self.assertRaises(Exception) as context:
- self.extract_subset_from_monolithic_flags_as_dict_from_string(
- monolithic, patterns)
- self.assertTrue('contains wildcard * and member signature hashCode()I'
- in str(context.exception))
-
def test_read_trie_duplicate(self):
with self.assertRaises(Exception) as context:
self.read_flag_trie_from_string("""
@@ -369,6 +312,8 @@
mismatches = compare_signature_flags(monolithic, modular)
expected = []
self.assertEqual(expected, mismatches)
+
+
#pylint: enable=line-too-long
if __name__ == '__main__':
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
index 617d425..cefaa42 100644
--- a/scripts/rustfmt.toml
+++ b/scripts/rustfmt.toml
@@ -1,5 +1,5 @@
# Android Format Style
-edition = "2018"
+edition = "2021"
use_small_heuristics = "Max"
newline_style = "Unix"
diff --git a/sdk/build_release.go b/sdk/build_release.go
index 2bcdc6f..4c2277e 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -85,7 +85,7 @@
// Add the build releases from oldest to newest.
buildReleaseS = initBuildRelease("S")
- buildReleaseT = initBuildRelease("T")
+ buildReleaseT = initBuildRelease("Tiramisu")
)
// initBuildRelease creates a new build release with the specified name.
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index 6608be4..6f1ef9e 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -60,7 +60,7 @@
t.Run("closed range", func(t *testing.T) {
set, err := parseBuildReleaseSet("S-F1")
android.AssertDeepEquals(t, "errors", nil, err)
- android.AssertStringEquals(t, "set", "[S,T,F1]", set.String())
+ android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
})
invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
t.Run("invalid release", func(t *testing.T) {
@@ -79,7 +79,7 @@
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
t.Run("invalid release in closed range end", func(t *testing.T) {
- set, err := parseBuildReleaseSet("T-A")
+ set, err := parseBuildReleaseSet("Tiramisu-A")
android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set)
android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
})
@@ -128,13 +128,13 @@
type mapped struct {
Default string
- T_only string `supported_build_releases:"T"`
+ T_only string `supported_build_releases:"Tiramisu"`
}
type testBuildReleasePruner struct {
Default string
- S_and_T_only string `supported_build_releases:"S-T"`
- T_later string `supported_build_releases:"T+"`
+ S_and_T_only string `supported_build_releases:"S-Tiramisu"`
+ T_later string `supported_build_releases:"Tiramisu+"`
Nested nested
Mapped map[string]*mapped
}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 2ab784d..d1beaba 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -149,6 +149,9 @@
// Only available for host sh_test modules.
Data_device_libs []string `android:"path,arch_variant"`
+ // Install the test into a folder named for the module in all test suites.
+ Per_testcase_directory *bool
+
// Test options.
Test_options TestOptions
}
@@ -458,9 +461,13 @@
dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
}
+ if s.testProperties.Data_bins != nil {
+ entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
+ }
if Bool(s.testProperties.Test_options.Unit_test) {
entries.SetBool("LOCAL_IS_UNIT_TEST", true)
}
+ entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
},
},
}}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 7fe1d85..89b8126 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -37,6 +37,8 @@
//
// deprecated
func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
result := prepareForShTest.RunTestWithBp(t, bp)
return result.TestContext, result.Config
diff --git a/starlark_fmt/Android.bp b/starlark_fmt/Android.bp
new file mode 100644
index 0000000..8d80ccd
--- /dev/null
+++ b/starlark_fmt/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-starlark-format",
+ pkgPath: "android/soong/starlark_fmt",
+ srcs: [
+ "format.go",
+ ],
+ testSrcs: [
+ "format_test.go",
+ ],
+}
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
new file mode 100644
index 0000000..23eee59
--- /dev/null
+++ b/starlark_fmt/format.go
@@ -0,0 +1,96 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+const (
+ indent = 4
+)
+
+// Indention returns an indent string of the specified level.
+func Indention(level int) string {
+ if level < 0 {
+ panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
+ }
+ return strings.Repeat(" ", level*indent)
+}
+
+// PrintBool returns a Starlark compatible bool string.
+func PrintBool(item bool) string {
+ return strings.Title(fmt.Sprintf("%t", item))
+}
+
+// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
+func PrintStringList(items []string, indentLevel int) string {
+ return PrintList(items, indentLevel, `"%s"`)
+}
+
+// PrintList returns a Starlark-compatible string of list formmated as requested.
+func PrintList(items []string, indentLevel int, formatString string) string {
+ if len(items) == 0 {
+ return "[]"
+ } else if len(items) == 1 {
+ return fmt.Sprintf("["+formatString+"]", items[0])
+ }
+ list := make([]string, 0, len(items)+2)
+ list = append(list, "[")
+ innerIndent := Indention(indentLevel + 1)
+ for _, item := range items {
+ list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
+ }
+ list = append(list, Indention(indentLevel)+"]")
+ return strings.Join(list, "\n")
+}
+
+// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and list of string values.
+func PrintStringListDict(dict map[string][]string, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintStringList(v, indentLevel+1)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintBoolDict(dict map[string]bool, indentLevel int) string {
+ formattedValueDict := make(map[string]string, len(dict))
+ for k, v := range dict {
+ formattedValueDict[k] = PrintBool(v)
+ }
+ return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintDict(dict map[string]string, indentLevel int) string {
+ if len(dict) == 0 {
+ return "{}"
+ }
+ items := make([]string, 0, len(dict))
+ for k, v := range dict {
+ items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
+ }
+ sort.Strings(items)
+ return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+}
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
new file mode 100644
index 0000000..90f78ef
--- /dev/null
+++ b/starlark_fmt/format_test.go
@@ -0,0 +1,169 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package starlark_fmt
+
+import (
+ "testing"
+)
+
+func TestPrintEmptyStringList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementStringList(t *testing.T) {
+ in := []string{"a"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `["a"]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementStringList(t *testing.T) {
+ in := []string{"a", "b"}
+ indentLevel := 0
+ out := PrintStringList(in, indentLevel)
+ expectedOut := `[
+ "a",
+ "b",
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintEmptyList(t *testing.T) {
+ in := []string{}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := "[]"
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintSingleElementList(t *testing.T) {
+ in := []string{"1"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[1]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintMultiElementList(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 0
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[
+ 1,
+ 2,
+]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestListWithNonZeroIndent(t *testing.T) {
+ in := []string{"1", "2"}
+ indentLevel := 1
+ out := PrintList(in, indentLevel, "%s")
+ expectedOut := `[
+ 1,
+ 2,
+ ]`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDictEmpty(t *testing.T) {
+ in := map[string][]string{}
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestStringListDict(t *testing.T) {
+ in := map[string][]string{
+ "key1": []string{},
+ "key2": []string{"a"},
+ "key3": []string{"1", "2"},
+ }
+ indentLevel := 0
+ out := PrintStringListDict(in, indentLevel)
+ expectedOut := `{
+ "key1": [],
+ "key2": ["a"],
+ "key3": [
+ "1",
+ "2",
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDict(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ "key3": `[
+ 1,
+ 2,
+ ]`,
+ }
+ indentLevel := 0
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ "key3": [
+ 1,
+ 2,
+ ],
+}`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
+
+func TestPrintDictWithIndent(t *testing.T) {
+ in := map[string]string{
+ "key1": `""`,
+ "key2": `"a"`,
+ }
+ indentLevel := 1
+ out := PrintDict(in, indentLevel)
+ expectedOut := `{
+ "key1": "",
+ "key2": "a",
+ }`
+ if out != expectedOut {
+ t.Errorf("Expected %q, got %q", expectedOut, out)
+ }
+}
diff --git a/tests/lib.sh b/tests/lib.sh
index e6074f8..55f9ab4 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -86,6 +86,7 @@
symlink_directory prebuilts/go
symlink_directory prebuilts/build-tools
+ symlink_directory prebuilts/clang/host
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
diff --git a/ui/build/build.go b/ui/build/build.go
index 2e44aaa..d261f89 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -264,6 +264,7 @@
if config.StartRBE() {
startRBE(ctx, config)
+ defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
}
if what&RunProductConfig != 0 {
diff --git a/ui/build/config.go b/ui/build/config.go
index 1dd948c..dd5bd0c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -35,10 +35,10 @@
)
const (
- envConfigDir = "vendor/google/tools/soong_config"
- jsonSuffix = "json"
+ envConfigDir = "vendor/google/tools/soong_config"
+ jsonSuffix = "json"
- configFetcher = "vendor/google/tools/soong/expconfigfetcher"
+ configFetcher = "vendor/google/tools/soong/expconfigfetcher"
envConfigFetchTimeout = 10 * time.Second
)
@@ -62,6 +62,7 @@
jsonModuleGraph bool
bp2build bool
queryview bool
+ reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
soongDocs bool
skipConfig bool
skipKati bool
@@ -155,7 +156,7 @@
}
configExists := false
- outConfigFilePath := filepath.Join(config.OutDir(), envConfigName + jsonSuffix)
+ outConfigFilePath := filepath.Join(config.OutDir(), envConfigName+jsonSuffix)
if _, err := os.Stat(outConfigFilePath); err == nil {
configExists = true
}
@@ -186,7 +187,7 @@
}
if err := fetchEnvConfig(ctx, config, bc); err != nil {
- fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v", err)
+ fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v\n", err)
}
configDirs := []string{
@@ -367,10 +368,14 @@
java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag())
java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag())
java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag())
+ java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag())
javaHome := func() string {
if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok {
return override
}
+ if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") {
+ return java17Home
+ }
if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" {
ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.")
}
@@ -711,6 +716,8 @@
c.skipConfig = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
+ } else if arg == "--mk-metrics" {
+ c.reportMkMetrics = true
} else if len(arg) > 0 && arg[0] == '-' {
parseArgNum := func(def int) int {
if len(arg) > 2 {
@@ -1184,7 +1191,12 @@
}
func (c *configImpl) rbeAuth() (string, string) {
- credFlags := []string{"use_application_default_credentials", "use_gce_credentials", "credential_file"}
+ credFlags := []string{
+ "use_application_default_credentials",
+ "use_gce_credentials",
+ "credential_file",
+ "use_google_prod_creds",
+ }
for _, cf := range credFlags {
for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} {
if v, ok := c.environ.Get(f); ok {
@@ -1381,6 +1393,11 @@
return filepath.Join(c.LogsDir(), "bazel_metrics")
}
+// MkFileMetrics returns the file path for make-related metrics.
+func (c *configImpl) MkMetrics() string {
+ return filepath.Join(c.LogsDir(), "mk_metrics.pb")
+}
+
func (c *configImpl) SetEmptyNinjaFile(v bool) {
c.emptyNinjaFile = v
}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 68efe21..262de3d 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -138,6 +138,17 @@
ctx.Fatalf("Could not export module list: %v", err)
}
+ // Gate collecting/reporting mk metrics on builds that specifically request
+ // it, as identifying the total number of mk files adds 4-5ms onto null
+ // builds.
+ if config.reportMkMetrics {
+ androidMksTotal := f.FindNamedAt(".", "Android.mk")
+
+ ctx.Metrics.SetToplevelMakefiles(len(androidMks))
+ ctx.Metrics.SetTotalMakefiles(len(androidMksTotal))
+ ctx.Metrics.DumpMkMetrics(config.MkMetrics())
+ }
+
// Stop searching a subdirectory recursively after finding a CleanSpec.mk.
cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 81c500d..831a80f 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -91,6 +91,7 @@
"pstree": Allowed,
"rsync": Allowed,
"sh": Allowed,
+ "stubby": Allowed,
"tr": Allowed,
"unzip": Allowed,
"zip": Allowed,
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 117a2a5..1b993e1 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -507,6 +507,9 @@
if shouldCollectBuildSoongMetrics(config) && ctx.Metrics != nil {
ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
}
+ if config.JsonModuleGraph() {
+ distGzipFile(ctx, config, config.ModuleGraphFile(), "soong")
+ }
}
func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3ba3907..05db1d7 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -21,9 +21,10 @@
pkgPath: "android/soong/ui/metrics",
deps: [
"golang-protobuf-proto",
+ "soong-ui-bp2build_metrics_proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
- "soong-ui-bp2build_metrics_proto",
+ "soong-ui-mk_metrics_proto",
"soong-ui-tracer",
"soong-shared",
],
@@ -71,3 +72,15 @@
"bp2build_metrics_proto/bp2build_metrics.pb.go",
],
}
+
+bootstrap_go_package {
+ name: "soong-ui-mk_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "mk_metrics_proto/mk_metrics.pb.go",
+ ],
+}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 80f8c1a..6f1ed60 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -41,6 +41,7 @@
"google.golang.org/protobuf/proto"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ mk_metrics_proto "android/soong/ui/metrics/mk_metrics_proto"
)
const (
@@ -62,14 +63,22 @@
Total = "total"
)
-// Metrics is a struct that stores collected metrics during the course
-// of a build which later is dumped to a MetricsBase protobuf file.
-// See ui/metrics/metrics_proto/metrics.proto for further details
-// on what information is collected.
+// Metrics is a struct that stores collected metrics during the course of a
+// build. It is later dumped to protobuf files. See underlying metrics protos
+// for further details on what information is collected.
type Metrics struct {
- // The protobuf message that is later written to the file.
+ // Protobuf containing various top-level build metrics. These include:
+ // 1. Build identifiers (ex: branch ID, requested product, hostname,
+ // originating command)
+ // 2. Per-subprocess top-level metrics (ex: ninja process IO and runtime).
+ // Note that, since these metrics are reported by soong_ui, there is little
+ // insight that can be provided into performance breakdowns of individual
+ // subprocesses.
metrics soong_metrics_proto.MetricsBase
+ // Protobuf containing metrics pertaining to number of makefiles in a build.
+ mkMetrics mk_metrics_proto.MkMetrics
+
// A list of pending build events.
EventTracer *EventTracer
}
@@ -78,11 +87,24 @@
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
+ mkMetrics: mk_metrics_proto.MkMetrics{},
EventTracer: &EventTracer{},
}
return m
}
+func (m *Metrics) SetTotalMakefiles(total int) {
+ m.mkMetrics.TotalMakefiles = uint32(total)
+}
+
+func (m *Metrics) SetToplevelMakefiles(total int) {
+ m.mkMetrics.ToplevelMakefiles = uint32(total)
+}
+
+func (m *Metrics) DumpMkMetrics(outPath string) {
+ shared.Save(&m.mkMetrics, outPath)
+}
+
// SetTimeMetrics stores performance information from an executed block of
// code.
func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.pb.go b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
new file mode 100644
index 0000000..32e136a
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
@@ -0,0 +1,177 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: mk_metrics.proto
+
+package mk_metrics_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Contains metrics pertaining to makefiles.
+type MkMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Total number of mk files present in the workspace.
+ TotalMakefiles uint32 `protobuf:"varint,1,opt,name=totalMakefiles,proto3" json:"totalMakefiles,omitempty"`
+ // Number of top-level mk files present in the workspace.
+ // A mk file is "top level" if there are no mk files in its parent
+ // direrctories.
+ // This value is equivalent to the number of entries in Android.mk.list.
+ ToplevelMakefiles uint32 `protobuf:"varint,2,opt,name=toplevelMakefiles,proto3" json:"toplevelMakefiles,omitempty"`
+}
+
+func (x *MkMetrics) Reset() {
+ *x = MkMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_mk_metrics_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *MkMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MkMetrics) ProtoMessage() {}
+
+func (x *MkMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_mk_metrics_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use MkMetrics.ProtoReflect.Descriptor instead.
+func (*MkMetrics) Descriptor() ([]byte, []int) {
+ return file_mk_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MkMetrics) GetTotalMakefiles() uint32 {
+ if x != nil {
+ return x.TotalMakefiles
+ }
+ return 0
+}
+
+func (x *MkMetrics) GetToplevelMakefiles() uint32 {
+ if x != nil {
+ return x.ToplevelMakefiles
+ }
+ return 0
+}
+
+var File_mk_metrics_proto protoreflect.FileDescriptor
+
+var file_mk_metrics_proto_rawDesc = []byte{
+ 0x0a, 0x10, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x12, 0x16, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+ 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x61, 0x0a, 0x09, 0x4d, 0x6b,
+ 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+ 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12,
+ 0x2c, 0x0a, 0x11, 0x74, 0x6f, 0x70, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66,
+ 0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x74, 0x6f, 0x70, 0x6c,
+ 0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a,
+ 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
+ 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x33,
+}
+
+var (
+ file_mk_metrics_proto_rawDescOnce sync.Once
+ file_mk_metrics_proto_rawDescData = file_mk_metrics_proto_rawDesc
+)
+
+func file_mk_metrics_proto_rawDescGZIP() []byte {
+ file_mk_metrics_proto_rawDescOnce.Do(func() {
+ file_mk_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_mk_metrics_proto_rawDescData)
+ })
+ return file_mk_metrics_proto_rawDescData
+}
+
+var file_mk_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_mk_metrics_proto_goTypes = []interface{}{
+ (*MkMetrics)(nil), // 0: soong_build_mk_metrics.MkMetrics
+}
+var file_mk_metrics_proto_depIdxs = []int32{
+ 0, // [0:0] is the sub-list for method output_type
+ 0, // [0:0] is the sub-list for method input_type
+ 0, // [0:0] is the sub-list for extension type_name
+ 0, // [0:0] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_mk_metrics_proto_init() }
+func file_mk_metrics_proto_init() {
+ if File_mk_metrics_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_mk_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*MkMetrics); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_mk_metrics_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_mk_metrics_proto_goTypes,
+ DependencyIndexes: file_mk_metrics_proto_depIdxs,
+ MessageInfos: file_mk_metrics_proto_msgTypes,
+ }.Build()
+ File_mk_metrics_proto = out.File
+ file_mk_metrics_proto_rawDesc = nil
+ file_mk_metrics_proto_goTypes = nil
+ file_mk_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.proto b/ui/metrics/mk_metrics_proto/mk_metrics.proto
new file mode 100644
index 0000000..df7bca3
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.proto
@@ -0,0 +1,30 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package soong_build_mk_metrics;
+option go_package = "android/soong/ui/metrics/mk_metrics_proto";
+
+// Contains metrics pertaining to makefiles.
+message MkMetrics {
+ // Total number of mk files present in the workspace.
+ uint32 totalMakefiles = 1;
+
+ // Number of top-level mk files present in the workspace.
+ // A mk file is "top level" if there are no mk files in its parent
+ // direrctories.
+ // This value is equivalent to the number of entries in Android.mk.list.
+ uint32 toplevelMakefiles = 2;
+}
diff --git a/ui/metrics/mk_metrics_proto/regen.sh b/ui/metrics/mk_metrics_proto/regen.sh
new file mode 100755
index 0000000..64018d4
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/regen.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# Copyright 2022 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generates the golang source file of the mk_metrics.proto protobuf file.
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+ die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. mk_metrics.proto; then
+ die "build failed. ${error_msg}"
+fi