diff options
-rw-r--r-- | android/allowlists/allowlists.go | 7 | ||||
-rw-r--r-- | apex/apex.go | 26 | ||||
-rw-r--r-- | apex/deapexer.go | 2 | ||||
-rw-r--r-- | apex/prebuilt.go | 9 | ||||
-rw-r--r-- | bazel/properties.go | 8 | ||||
-rw-r--r-- | bp2build/cc_binary_conversion_test.go | 79 | ||||
-rw-r--r-- | bp2build/cc_library_conversion_test.go | 193 | ||||
-rw-r--r-- | bp2build/cc_library_headers_conversion_test.go | 22 | ||||
-rw-r--r-- | bp2build/cc_library_shared_conversion_test.go | 82 | ||||
-rw-r--r-- | bp2build/cc_library_static_conversion_test.go | 82 | ||||
-rw-r--r-- | bp2build/configurability.go | 30 | ||||
-rw-r--r-- | cc/bp2build.go | 72 | ||||
-rw-r--r-- | cc/cc.go | 6 | ||||
-rw-r--r-- | cc/library.go | 6 | ||||
-rw-r--r-- | cmd/soong_ui/main.go | 4 | ||||
-rw-r--r-- | java/lint.go | 3 | ||||
-rw-r--r-- | java/testing.go | 2 | ||||
-rw-r--r-- | mk2rbc/mk2rbc.go | 67 | ||||
-rw-r--r-- | mk2rbc/mk2rbc_test.go | 52 | ||||
-rw-r--r-- | mk2rbc/types.go | 21 | ||||
-rwxr-xr-x | scripts/unpack-prebuilt-apex.sh | 17 | ||||
-rwxr-xr-x | tests/apex_comparison_tests.sh | 8 | ||||
-rwxr-xr-x | tests/bp2build_bazel_test.sh | 2 | ||||
-rw-r--r-- | ui/build/soong.go | 9 |
24 files changed, 367 insertions, 442 deletions
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go index fce8d7b28..7134c7b46 100644 --- a/android/allowlists/allowlists.go +++ b/android/allowlists/allowlists.go @@ -731,7 +731,7 @@ var ( "platform_tools_properties", "build_tools_source_properties", // TODO(b/203369847): multiple genrules in the same package creating the same file // aar support - "prebuilt_car-ui-androidx-core-common", // TODO(b/224773339), genrule dependency creates an .aar, not a .jar + "prebuilt_car-ui-androidx-core-common", // TODO(b/224773339), genrule dependency creates an .aar, not a .jar // ERROR: The dependencies for the following 1 jar(s) are not complete. // 1.bazel-out/android_target-fastbuild/bin/prebuilts/tools/common/m2/_aar/robolectric-monitor-1.0.2-alpha1/classes_and_libs_merged.jar "prebuilt_robolectric-monitor-1.0.2-alpha1", @@ -1359,5 +1359,8 @@ var ( // Staging-mode allowlist. Modules in this list are only built // by Bazel with --bazel-mode-staging. This list should contain modules // which will soon be added to the prod allowlist. - StagingMixedBuildsEnabledList = []string{"com.android.tzdata"} + StagingMixedBuildsEnabledList = []string{ + "com.android.adbd", + "com.android.tzdata", + } ) diff --git a/apex/apex.go b/apex/apex.go index 25de1ee17..01e4f1222 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -3087,31 +3087,7 @@ func makeApexAvailableBaseline() map[string][]string { // Module separator // m["com.android.btservices"] = []string{ - "bluetooth-protos-lite", - "internal_include_headers", - "libaudio-a2dp-hw-utils", - "libaudio-hearing-aid-hw-utils", - "libbluetooth", - "libbluetooth-types", - "libbluetooth-types-header", - "libbluetooth_gd", - "libbluetooth_headers", - "libbluetooth_jni", - "libbt-audio-hal-interface", - "libbt-bta", - "libbt-common", - "libbt-hci", - "libbt-platform-protos-lite", - "libbt-protos-lite", - "libbt-sbc-decoder", - "libbt-sbc-encoder", - "libbt-stack", - "libbt-utils", - "libbtcore", - "libbtdevice", - "libbte", - "libbtif", - "libchrome", + // empty } // // Module separator diff --git a/apex/deapexer.go b/apex/deapexer.go index 8c9030a85..fed9cd1f0 100644 --- a/apex/deapexer.go +++ b/apex/deapexer.go @@ -140,6 +140,8 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) { Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")). BuiltTool("deapexer"). BuiltTool("debugfs"). + BuiltTool("blkid"). + BuiltTool("fsck.erofs"). Input(p.inputApex). Text(deapexerOutput.String()) for _, p := range exportedPaths { diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 6fdd50a5e..14020fc31 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -915,6 +915,15 @@ func (a *ApexSet) hasSanitizedSource(sanitizer string) bool { return false } +func (a *ApexSet) OutputFiles(tag string) (android.Paths, error) { + switch tag { + case "": + return android.Paths{a.outputApex}, nil + default: + return nil, fmt.Errorf("unsupported module reference tag %q", tag) + } +} + // prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex. func apexSetFactory() android.Module { module := &ApexSet{} diff --git a/bazel/properties.go b/bazel/properties.go index ee9609aa3..692198402 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -1207,6 +1207,11 @@ type StringListAttribute struct { // The configured attribute label list Values. Optional // a map of independent configurability axes ConfigurableValues configurableStringLists + + // If a property has struct tag "variant_prepend", this value should + // be set to True, so that when bp2build generates BUILD.bazel, variant + // properties(select ...) come before general properties. + Prepend bool } // IsEmpty returns true if the attribute has no values under any configuration. @@ -1273,6 +1278,9 @@ func (sla StringListAttribute) HasConfigurableValues() bool { // Append appends all values, including os and arch specific ones, from another // StringListAttribute to this StringListAttribute func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute { + if sla.Prepend != other.Prepend { + panic(fmt.Errorf("StringListAttribute could not be appended because it has different prepend value")) + } sla.Value = append(sla.Value, other.Value...) if sla.ConfigurableValues == nil { sla.ConfigurableValues = make(configurableStringLists) diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go index 68dc383ff..8aa2c3ecb 100644 --- a/bp2build/cc_binary_conversion_test.go +++ b/bp2build/cc_binary_conversion_test.go @@ -789,82 +789,3 @@ func TestCcBinaryWithSyspropSrcsSomeConfigs(t *testing.T) { }, }) } - -func TestCcBinaryWithIntegerOverflowProperty(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with integer overflow property specified", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - integer_overflow: true, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `["ubsan_integer_overflow"]`, - }}, - }, - }) -} - -func TestCcBinaryWithMiscUndefinedProperty(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary with miscellaneous properties specified", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - }}, - }, - }) -} - -func TestCcBinaryWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{ - description: "cc_binary has correct feature select when UBSan props are specified in arch specific blocks", - blueprint: ` -{rule_name} { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -}`, - targets: []testBazelTarget{ - {"cc_binary", "foo", AttrNameToString{ - "local_includes": `["."]`, - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - }}, - }, - }) -} diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index 5feb02e21..ee6e5b807 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -3603,6 +3603,89 @@ cc_library_static { }) } +func TestCcLibraryWithAfdoEnabled(t *testing.T) { + bp := ` +cc_library { + name: "foo", + afdo: true, + include_build_directory: false, +}` + + // TODO(b/260714900): Add test case for arch-specific afdo profile + testCases := []struct { + description string + filesystem map[string]string + expectedBazelTargets []string + }{ + { + description: "cc_library with afdo enabled and existing profile", + filesystem: map[string]string{ + "vendor/google_data/pgo_profile/sampling/BUILD": "", + "vendor/google_data/pgo_profile/sampling/foo.afdo": "", + }, + expectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "fdo_profile": `"//vendor/google_data/pgo_profile/sampling:foo"`, + }), + }, + }, + { + description: "cc_library with afdo enabled and existing profile in AOSP", + filesystem: map[string]string{ + "toolchain/pgo-profiles/sampling/BUILD": "", + "toolchain/pgo-profiles/sampling/foo.afdo": "", + }, + expectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ + "fdo_profile": `"//toolchain/pgo-profiles/sampling:foo"`, + }), + }, + }, + { + description: "cc_library with afdo enabled but profile filename doesn't match with module name", + filesystem: map[string]string{ + "toolchain/pgo-profiles/sampling/BUILD": "", + "toolchain/pgo-profiles/sampling/bar.afdo": "", + }, + expectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), + }, + }, + { + description: "cc_library with afdo enabled but profile doesn't exist", + expectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), + }, + }, + { + description: "cc_library with afdo enabled and existing profile but BUILD file doesn't exist", + filesystem: map[string]string{ + "vendor/google_data/pgo_profile/sampling/foo.afdo": "", + }, + expectedBazelTargets: []string{ + MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}), + MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{}), + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.description, func(t *testing.T) { + runCcLibraryTestCase(t, Bp2buildTestCase{ + ExpectedBazelTargets: testCase.expectedBazelTargets, + ModuleTypeUnderTest: "cc_library", + ModuleTypeUnderTestFactory: cc.LibraryFactory, + Description: testCase.description, + Blueprint: binaryReplacer.Replace(bp), + Filesystem: testCase.filesystem, + }) + }) + } +} + func TestCcLibraryHeaderAbiChecker(t *testing.T) { runCcLibraryTestCase(t, Bp2buildTestCase{ Description: "cc_library with header abi checker", @@ -3691,113 +3774,3 @@ cc_library { }, ) } - -func TestCcLibraryWithIntegerOverflowProperty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when integer_overflow property is provided", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithMiscUndefinedProperty(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct features when misc_undefined property is provided", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibraryTestCase(t, Bp2buildTestCase{ - Description: "cc_library has correct feature select when UBSan props are specified in arch specific blocks", - ModuleTypeUnderTest: "cc_library", - ModuleTypeUnderTestFactory: cc.LibraryFactory, - Blueprint: ` -cc_library { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go index 7d9db6fe2..686c9d569 100644 --- a/bp2build/cc_library_headers_conversion_test.go +++ b/bp2build/cc_library_headers_conversion_test.go @@ -107,15 +107,15 @@ cc_library_headers { }`, ExpectedBazelTargets: []string{ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "export_includes": `[ - "dir-1", - "dir-2", - ] + select({ + "export_includes": `select({ "//build/bazel/platforms/arch:arm64": ["arch_arm64_exported_include_dir"], "//build/bazel/platforms/arch:x86": ["arch_x86_exported_include_dir"], "//build/bazel/platforms/arch:x86_64": ["arch_x86_64_exported_include_dir"], "//conditions:default": [], - })`, + }) + [ + "dir-1", + "dir-2", + ]`, "sdk_version": `"current"`, "min_sdk_version": `"29"`, }), @@ -340,16 +340,16 @@ func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) { }`, ExpectedBazelTargets: []string{ MakeBazelTarget("cc_library_headers", "foo_headers", AttrNameToString{ - "export_system_includes": `["shared_include_dir"] + select({ - "//build/bazel/platforms/arch:arm": ["arm_include_dir"], - "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"], - "//conditions:default": [], - }) + select({ + "export_system_includes": `select({ "//build/bazel/platforms/os:android": ["android_include_dir"], "//build/bazel/platforms/os:darwin": ["darwin_include_dir"], "//build/bazel/platforms/os:linux": ["linux_include_dir"], "//conditions:default": [], - })`, + }) + select({ + "//build/bazel/platforms/arch:arm": ["arm_include_dir"], + "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"], + "//conditions:default": [], + }) + ["shared_include_dir"]`, }), }, }) diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go index 9b01b6f42..7e1d1117d 100644 --- a/bp2build/cc_library_shared_conversion_test.go +++ b/bp2build/cc_library_shared_conversion_test.go @@ -886,85 +886,3 @@ func TestCcLibrarySharedHeaderAbiChecker(t *testing.T) { }, }) } - -func TestCcLibrarySharedWithIntegerOverflowProperty(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when integer_overflow property is provided", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithMiscUndefinedProperty(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct features when misc_undefined property is provided", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibrarySharedWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibrarySharedTestCase(t, Bp2buildTestCase{ - Description: "cc_library_shared has correct feature select when UBSan props are specified in arch specific blocks", - Blueprint: ` -cc_library_shared { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go index cac7f9b5e..b47d1f1f1 100644 --- a/bp2build/cc_library_static_conversion_test.go +++ b/bp2build/cc_library_static_conversion_test.go @@ -1723,85 +1723,3 @@ cc_library_static { }, }) } - -func TestCcLibraryStaticWithIntegerOverflowProperty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when integer_overflow property is provided", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - integer_overflow: true, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `["ubsan_integer_overflow"]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithMiscUndefinedProperty(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct features when misc_undefined property is provided", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ]`, - "local_includes": `["."]`, - }), - }, - }) -} - -func TestCcLibraryStaticWithUBSanPropertiesArchSpecific(t *testing.T) { - runCcLibraryStaticTestCase(t, Bp2buildTestCase{ - Description: "cc_library_static has correct feature select when UBSan props are specified in arch specific blocks", - Blueprint: ` -cc_library_static { - name: "foo", - sanitize: { - misc_undefined: ["undefined", "nullability"], - }, - target: { - android: { - sanitize: { - misc_undefined: ["alignment"], - }, - }, - linux_glibc: { - sanitize: { - integer_overflow: true, - }, - }, - }, -} -`, - ExpectedBazelTargets: []string{ - MakeBazelTarget("cc_library_static", "foo", AttrNameToString{ - "features": `[ - "ubsan_undefined", - "ubsan_nullability", - ] + select({ - "//build/bazel/platforms/os:android": ["ubsan_alignment"], - "//build/bazel/platforms/os:linux": ["ubsan_integer_overflow"], - "//conditions:default": [], - })`, - "local_includes": `["."]`, - }), - }, - }) -} diff --git a/bp2build/configurability.go b/bp2build/configurability.go index 9398d127c..112755b53 100644 --- a/bp2build/configurability.go +++ b/bp2build/configurability.go @@ -37,10 +37,11 @@ func getStringValue(str bazel.StringAttribute) (reflect.Value, []selects) { return value, []selects{ret} } -func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) { +func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects, bool) { value := reflect.ValueOf(list.Value) + prepend := reflect.ValueOf(list.Prepend).Bool() if !list.HasConfigurableValues() { - return value, []selects{} + return value, []selects{}, prepend } var ret []selects @@ -56,7 +57,7 @@ func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selec } } - return value, ret + return value, ret, prepend } func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) { @@ -156,6 +157,7 @@ var ( func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) { var value reflect.Value var configurableAttrs []selects + var prepend bool var defaultSelectValue *string var emitZeroValues bool // If true, print the default attribute value, even if the attribute is zero. @@ -168,7 +170,7 @@ func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) { value, configurableAttrs = getStringValue(list) defaultSelectValue = &bazelNone case bazel.StringListAttribute: - value, configurableAttrs = getStringListValues(list) + value, configurableAttrs, prepend = getStringListValues(list) defaultSelectValue = &emptyBazelList case bazel.LabelListAttribute: value, configurableAttrs = getLabelListValues(list) @@ -203,22 +205,28 @@ func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) { ret += s } - // Convenience function to append selects components to an attribute value. - appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) { + // Convenience function to prepend/append selects components to an attribute value. + concatenateSelects := func(selectsData selects, defaultValue *string, s string, prepend bool) (string, error) { selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent, emitZeroValues) if err != nil { return "", err } - if s != "" && selectMap != "" { - s += " + " + var left, right string + if prepend { + left, right = selectMap, s + } else { + left, right = s, selectMap + } + if left != "" && right != "" { + left += " + " } - s += selectMap + left += right - return s, nil + return left, nil } for _, configurableAttr := range configurableAttrs { - ret, err = appendSelects(configurableAttr, defaultSelectValue, ret) + ret, err = concatenateSelects(configurableAttr, defaultSelectValue, ret, prepend) if err != nil { return "", err } diff --git a/cc/bp2build.go b/cc/bp2build.go index d41aa0057..16ab79108 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -341,7 +341,7 @@ type baseAttributes struct { compilerAttributes linkerAttributes - // A combination of compilerAttributes.features and linkerAttributes.features, as well as sanitizer features + // A combination of compilerAttributes.features and linkerAttributes.features features bazel.StringListAttribute protoDependency *bazel.LabelAttribute aidlDependency *bazel.LabelAttribute @@ -393,6 +393,8 @@ type compilerAttributes struct { features bazel.StringListAttribute suffix bazel.StringAttribute + + fdoProfile bazel.LabelAttribute } type filterOutFn func(string) bool @@ -777,11 +779,18 @@ func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) (&compilerAttrs).srcs.Add(&convertedLSrcs.srcName) (&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName) + if module.afdo != nil && module.afdo.Properties.Afdo { + fdoProfileDep := bp2buildFdoProfile(ctx, module) + if fdoProfileDep != nil { + (&compilerAttrs).fdoProfile.SetValue(*fdoProfileDep) + } + } + if !compilerAttrs.syspropSrcs.IsEmpty() { (&linkerAttrs).wholeArchiveDeps.Add(bp2buildCcSysprop(ctx, module.Name(), module.Properties.Min_sdk_version, compilerAttrs.syspropSrcs)) } - features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module)) + features := compilerAttrs.features.Clone().Append(linkerAttrs.features) features.DeduplicateAxesFromBase() return baseAttributes{ @@ -793,6 +802,41 @@ func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) } } +type fdoProfileAttributes struct { + Absolute_path_profile string +} + +func bp2buildFdoProfile( + ctx android.Bp2buildMutatorContext, + m *Module, +) *bazel.Label { + for _, project := range globalAfdoProfileProjects { + // Ensure handcrafted BUILD file exists in the project + BUILDPath := android.ExistentPathForSource(ctx, project, "BUILD") + if BUILDPath.Valid() { + // We handcraft a BUILD file with fdo_profile targets that use the existing profiles in the project + // This implementation is assuming that every afdo profile in globalAfdoProfileProjects already has + // an associated fdo_profile target declared in the same package. + // TODO(b/260714900): Handle arch-specific afdo profiles (e.g. `<module-name>-arm<64>.afdo`) + path := android.ExistentPathForSource(ctx, project, m.Name()+".afdo") + if path.Valid() { + // FIXME: Some profiles only exist internally and are not released to AOSP. + // When generated BUILD files are checked in, we'll run into merge conflict. + // The cc_library_shared target in AOSP won't have reference to an fdo_profile target because + // the profile doesn't exist. Internally, the same cc_library_shared target will + // have reference to the fdo_profile. + // For more context, see b/258682955#comment2 + fdoProfileLabel := "//" + strings.TrimSuffix(project, "/") + ":" + m.Name() + return &bazel.Label{ + Label: fdoProfileLabel, + } + } + } + } + + return nil +} + func bp2buildCcAidlLibrary( ctx android.Bp2buildMutatorContext, m *Module, @@ -1257,6 +1301,13 @@ func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, modul } else { exported = BazelIncludes{} } + + // cc library Export_include_dirs and Export_system_include_dirs are marked + // "variant_prepend" in struct tag, set their prepend property to true to make + // sure bp2build generates correct result. + exported.Includes.Prepend = true + exported.SystemIncludes.Prepend = true + bp2BuildPropParseHelper(ctx, module, &FlagExporterProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) { if flagExporterProperties, ok := props.(*FlagExporterProperties); ok { if len(flagExporterProperties.Export_include_dirs) > 0 { @@ -1364,20 +1415,3 @@ func bp2buildBinaryLinkerProps(ctx android.BazelConversionPathContext, m *Module return attrs } - -func bp2buildSanitizerFeatures(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute { - sanitizerFeatures := bazel.StringListAttribute{} - bp2BuildPropParseHelper(ctx, m, &SanitizeProperties{}, func(axis bazel.ConfigurationAxis, config string, props interface{}) { - var features []string - if sanitizerProps, ok := props.(*SanitizeProperties); ok { - if sanitizerProps.Sanitize.Integer_overflow != nil && *sanitizerProps.Sanitize.Integer_overflow { - features = append(features, "ubsan_integer_overflow") - } - for _, sanitizer := range sanitizerProps.Sanitize.Misc_undefined { - features = append(features, "ubsan_"+sanitizer) - } - sanitizerFeatures.SetSelectValue(axis, config, features) - } - }) - return sanitizerFeatures -} @@ -1859,11 +1859,7 @@ func (c *Module) QueueBazelCall(ctx android.BaseModuleContext) { } func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool { - // TODO(b/261058727): Remove this (enable mised builds for modules with UBSan) - ubsanEnabled := c.sanitize != nil && - ((c.sanitize.Properties.Sanitize.Integer_overflow != nil && *c.sanitize.Properties.Sanitize.Integer_overflow) || - c.sanitize.Properties.Sanitize.Misc_undefined != nil) - return c.bazelHandler != nil && !ubsanEnabled + return c.bazelHandler != nil } func (c *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) { diff --git a/cc/library.go b/cc/library.go index 705902310..ed0ed01bc 100644 --- a/cc/library.go +++ b/cc/library.go @@ -417,6 +417,8 @@ func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) { Strip: stripAttrsFromLinkerAttrs(&linkerAttrs), Features: baseAttributes.features, bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, m), + + Fdo_profile: compilerAttrs.fdoProfile, } if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 { @@ -2930,6 +2932,8 @@ func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Mo Suffix: compilerAttrs.suffix, bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, module), + + Fdo_profile: compilerAttrs.fdoProfile, } if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 { hasStubs := true @@ -3020,6 +3024,8 @@ type bazelCcLibrarySharedAttributes struct { Suffix bazel.StringAttribute bazelCcHeaderAbiCheckerAttributes + + Fdo_profile bazel.LabelAttribute } type bazelCcStubSuiteAttributes struct { diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index 8adc86f4a..928ae172d 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -117,6 +117,7 @@ func inList(s string, list []string) bool { // Command is the type of soong_ui execution. Only one type of // execution is specified. The args are specific to the command. func main() { + //TODO(juu): Add logic to soong_ui to delete a hardcoded list of metrics files shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary()) buildStarted := time.Now() @@ -184,6 +185,7 @@ func main() { rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb") soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics") bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb") + soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb") build.PrintOutDirWarning(buildCtx, config) @@ -211,6 +213,7 @@ func main() { files := []string{ buildErrorFile, // build error strings rbeMetricsFile, // high level metrics related to remote build execution. + soongBuildMetricsFile, // high level metrics related to soong build(except bp2build). bp2buildMetricsFile, // high level metrics related to bp2build. soongMetricsFile, // high level metrics related to this build system. config.BazelMetricsDir(), // directory that contains a set of bazel metrics. @@ -219,7 +222,6 @@ func main() { if !config.SkipMetricsUpload() { defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...) } - defer met.Dump(soongMetricsFile) defer build.CheckProdCreds(buildCtx, config) } diff --git a/java/lint.go b/java/lint.go index 7a6e5d9c1..07b962912 100644 --- a/java/lint.go +++ b/java/lint.go @@ -366,6 +366,9 @@ func (l *linter) lint(ctx android.ModuleContext) { } } + l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx, + "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar")) + rule := android.NewRuleBuilder(pctx, ctx). Sbox(android.PathForModuleOut(ctx, "lint"), android.PathForModuleOut(ctx, "lint.sbox.textproto")). diff --git a/java/testing.go b/java/testing.go index ccbb638bf..e6f76e10d 100644 --- a/java/testing.go +++ b/java/testing.go @@ -56,6 +56,8 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( "build/make/target/product/security": nil, // Required to generate Java used-by API coverage "build/soong/scripts/gen_java_usedby_apex.sh": nil, + // Needed for the global lint checks provided from frameworks/base + "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar": nil, }.AddToFixture(), ) diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go index aa48e6377..c3b192d44 100644 --- a/mk2rbc/mk2rbc.go +++ b/mk2rbc/mk2rbc.go @@ -77,6 +77,7 @@ var knownFunctions = map[string]interface { "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}, + "and": &andOrParser{isAnd: true}, "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList}, "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString}, "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true}, @@ -105,6 +106,7 @@ var knownFunctions = map[string]interface { "math_gt": &mathComparisonCallParser{op: ">"}, "math_lt": &mathComparisonCallParser{op: "<"}, "my-dir": &myDirCallParser{}, + "or": &andOrParser{isAnd: false}, "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, addHandle: true}, @@ -114,6 +116,8 @@ var knownFunctions = map[string]interface { "sort": &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList}, "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString}, "subst": &substCallParser{fname: "subst"}, + "to-lower": &lowerUpperParser{isUpper: false}, + "to-upper": &lowerUpperParser{isUpper: true}, "warning": &makeControlFuncParser{name: baseName + ".mkwarning"}, "word": &wordCallParser{}, "words": &wordsCallParser{}, @@ -1430,6 +1434,51 @@ func (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkp return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)} } +type andOrParser struct { + isAnd bool +} + +func (p *andOrParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { + if args.Empty() { + return ctx.newBadExpr(node, "and/or function must have at least 1 argument") + } + op := "or" + if p.isAnd { + op = "and" + } + + argsParsed := make([]starlarkExpr, 0) + + for _, arg := range args.Split(",") { + arg.TrimLeftSpaces() + arg.TrimRightSpaces() + x := ctx.parseMakeString(node, arg) + if xBad, ok := x.(*badExpr); ok { + return xBad + } + argsParsed = append(argsParsed, x) + } + typ := starlarkTypeUnknown + for _, arg := range argsParsed { + if typ != arg.typ() && arg.typ() != starlarkTypeUnknown && typ != starlarkTypeUnknown { + return ctx.newBadExpr(node, "Expected all arguments to $(or) or $(and) to have the same type, found %q and %q", typ.String(), arg.typ().String()) + } + if arg.typ() != starlarkTypeUnknown { + typ = arg.typ() + } + } + result := argsParsed[0] + for _, arg := range argsParsed[1:] { + result = &binaryOpExpr{ + left: result, + right: arg, + op: op, + returnType: typ, + } + } + return result +} + type isProductInListCallParser struct{} func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { @@ -1848,6 +1897,24 @@ func (p *evalNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkpa return []starlarkNode{ctx.newBadNode(node, "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")} } +type lowerUpperParser struct { + isUpper bool +} + +func (p *lowerUpperParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr { + fn := "lower" + if p.isUpper { + fn = "upper" + } + arg := ctx.parseMakeString(node, args) + + return &callExpr{ + object: arg, + name: fn, + returnType: starlarkTypeString, + } +} + func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr { if mk.Const() { return &stringLiteralExpr{mk.Dump()} diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go index 31555d3f5..65a3be7f7 100644 --- a/mk2rbc/mk2rbc_test.go +++ b/mk2rbc/mk2rbc_test.go @@ -1629,6 +1629,58 @@ def init(g, handle): g["MY_VAR_5"] = rblf.mk2rbc_error("product.mk:6", "reference is too complex: $(MY_VAR_2) bar") `, }, + { + desc: "Conditional functions", + mkname: "product.mk", + in: ` +B := foo +X := $(or $(A)) +X := $(or $(A),$(B)) +X := $(or $(A),$(B),$(C)) +X := $(and $(A)) +X := $(and $(A),$(B)) +X := $(and $(A),$(B),$(C)) +X := $(or $(A),$(B)) Y + +D := $(wildcard *.mk) +X := $(or $(B),$(D)) +`, + expected: `load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + g["B"] = "foo" + g["X"] = g.get("A", "") + g["X"] = g.get("A", "") or g["B"] + g["X"] = g.get("A", "") or g["B"] or g.get("C", "") + g["X"] = g.get("A", "") + g["X"] = g.get("A", "") and g["B"] + g["X"] = g.get("A", "") and g["B"] and g.get("C", "") + g["X"] = "%s Y" % g.get("A", "") or g["B"] + g["D"] = rblf.expand_wildcard("*.mk") + g["X"] = rblf.mk2rbc_error("product.mk:12", "Expected all arguments to $(or) or $(and) to have the same type, found \"string\" and \"list\"") +`, + }, + { + + desc: "is-lower/is-upper", + mkname: "product.mk", + in: ` +X := $(call to-lower,aBc) +X := $(call to-upper,aBc) +X := $(call to-lower,$(VAR)) +X := $(call to-upper,$(VAR)) +`, + expected: `load("//build/make/core:product_config.rbc", "rblf") + +def init(g, handle): + cfg = rblf.cfg(handle) + g["X"] = ("aBc").lower() + g["X"] = ("aBc").upper() + g["X"] = (g.get("VAR", "")).lower() + g["X"] = (g.get("VAR", "")).upper() +`, + }, } var known_variables = []struct { diff --git a/mk2rbc/types.go b/mk2rbc/types.go index 46c6aa9b3..ac3250722 100644 --- a/mk2rbc/types.go +++ b/mk2rbc/types.go @@ -14,6 +14,8 @@ package mk2rbc +import "fmt" + // Starlark expression types we use type starlarkType int @@ -31,6 +33,25 @@ const ( starlarkTypeVoid starlarkType = iota ) +func (t starlarkType) String() string { + switch t { + case starlarkTypeList: + return "list" + case starlarkTypeString: + return "string" + case starlarkTypeInt: + return "int" + case starlarkTypeBool: + return "bool" + case starlarkTypeVoid: + return "void" + case starlarkTypeUnknown: + return "unknown" + default: + panic(fmt.Sprintf("Unknown starlark type %d", t)) + } +} + type hiddenArgType int const ( diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh index f34a480c9..b244f7950 100755 --- a/scripts/unpack-prebuilt-apex.sh +++ b/scripts/unpack-prebuilt-apex.sh @@ -17,23 +17,28 @@ set -eu # limitations under the License. # Tool to unpack an apex file and verify that the required files were extracted. -if [ $# -lt 5 ]; then - echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2 +if [ $# -lt 7 ]; then + echo "usage: $0 <deapaxer_path> <debugfs_path> <blkid_path> <fsck.erofs_path> <apex file> <output_dir> <required_files>+" >&2 exit 1 fi DEAPEXER_PATH=$1 DEBUGFS_PATH=$2 -APEX_FILE=$3 -OUTPUT_DIR=$4 -shift 4 +BLKID_PATH=$3 +FSCK_EROFS_PATH=$4 +APEX_FILE=$5 +OUTPUT_DIR=$6 +shift 6 REQUIRED_PATHS=$@ rm -fr $OUTPUT_DIR mkdir -p $OUTPUT_DIR # Unpack the apex file contents. -$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR +$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH \ + --blkid_path $BLKID_PATH \ + --fsckerofs_path $FSCK_EROFS_PATH \ + extract $APEX_FILE $OUTPUT_DIR # Verify that the files that the build expects to be in the .apex file actually # exist, and make sure they have a fresh mtime to not confuse ninja. diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh index bfcf42d86..d57942989 100755 --- a/tests/apex_comparison_tests.sh +++ b/tests/apex_comparison_tests.sh @@ -68,10 +68,10 @@ call_bazel build --config=bp2build --config=ci --config=android \ //system/timezone/apex:com.android.tzdata \ //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex -# Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg. +# # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg. call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs //system/apex/tools:deapexer -DEBUGFS_PATH="$BAZEL_OUT/linux_x86_64-fastbuild/bin/external/e2fsprogs/debugfs/debugfs" -DEAPEXER="$BAZEL_OUT/linux_x86_64-fastbuild/bin/system/apex/tools/deapexer --debugfs_path=$DEBUGFS_PATH" +DEBUGFS_PATH="$BAZEL_OUT/linux_x86_64-opt/bin/external/e2fsprogs/debugfs/debugfs" +DEAPEXER="$BAZEL_OUT/linux_x86_64-opt/bin/system/apex/tools/deapexer --debugfs_path=$DEBUGFS_PATH" ####### # Tests @@ -83,7 +83,7 @@ function compare_deapexer_list() { # Compare the outputs of `deapexer list`, which lists the contents of the apex filesystem image. local SOONG_APEX="$SOONG_OUTPUT_DIR/$APEX" - local BAZEL_APEX="$BAZEL_OUT/android_target-fastbuild/bin/$APEX_DIR/$APEX" + local BAZEL_APEX="$BAZEL_OUT/android_target-opt/bin/$APEX_DIR/$APEX" local SOONG_LIST="$OUTPUT_DIR/soong.list" local BAZEL_LIST="$OUTPUT_DIR/bazel.list" diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh index 2e7b9a892..07738b75a 100755 --- a/tests/bp2build_bazel_test.sh +++ b/tests/bp2build_bazel_test.sh @@ -140,7 +140,7 @@ EOF # NOTE: We don't actually use the extra BUILD file for anything here run_bazel build --config=android --package_path=out/soong/workspace //foo/... - local the_answer_file="bazel-out/android_target-fastbuild/bin/foo/convertible_soong_module/the_answer.txt" + local the_answer_file="bazel-out/android_target-opt/bin/foo/convertible_soong_module/the_answer.txt" if [[ ! -f "${the_answer_file}" ]]; then fail "Expected '${the_answer_file}' to be generated, but was missing" fi diff --git a/ui/build/soong.go b/ui/build/soong.go index b89ca20f0..370b1bc33 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -566,11 +566,12 @@ func runSoong(ctx Context, config Config) { targets = append(targets, config.SoongNinjaFile()) } + // TODO(juu): Stop embedding soong_build_metrics in soong_metrics. + soongBuildMetricsFile := filepath.Join(config.LogsDir(), "soong_build_metrics.pb") + if err := os.Remove(soongBuildMetricsFile); err != nil && !os.IsNotExist(err) { + ctx.Verbosef("Failed to remove %s", soongBuildMetricsFile) + } if shouldCollectBuildSoongMetrics(config) { - soongBuildMetricsFile := filepath.Join(config.LogsDir(), "soong_build_metrics.pb") - if err := os.Remove(soongBuildMetricsFile); err != nil && !os.IsNotExist(err) { - ctx.Verbosef("Failed to remove %s", soongBuildMetricsFile) - } defer func() { soongBuildMetrics := loadSoongBuildMetrics(ctx, soongBuildMetricsFile) if soongBuildMetrics != nil { |