Merge "Bp2build support for soong config variables + os"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index df47a5c..71281a6 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -313,6 +313,7 @@
"prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
"prebuilts/gradle-plugin": Bp2BuildDefaultTrueRecursively,
"prebuilts/runtime/mainline/platform/sdk": Bp2BuildDefaultTrueRecursively,
+ "prebuilts/sdk": Bp2BuildDefaultTrue,
"prebuilts/sdk/current/androidx": Bp2BuildDefaultTrue,
"prebuilts/sdk/current/androidx-legacy": Bp2BuildDefaultTrue,
"prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue,
diff --git a/android/bazel.go b/android/bazel.go
index 3fe063c..114b1f5 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -71,7 +71,7 @@
MissingDeps []string `blueprint:"mutated"`
}
-type bazelModuleProperties struct {
+type BazelModuleProperties struct {
// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
// will import the handcrafted build target into the autogenerated file. Note: this may result in
// a conflict due to duplicate targets if bp2build_available is also set.
@@ -96,7 +96,7 @@
type properties struct {
// In "Bazel mixed build" mode, this represents the Bazel target replacing
// this Soong module.
- Bazel_module bazelModuleProperties
+ Bazel_module BazelModuleProperties
}
// namespacedVariableProperties is a map from a string representing a Soong
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 87b2c8f..77e2515 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -218,7 +218,7 @@
var bazelableBazelModuleBase = BazelModuleBase{
bazelProperties: properties{
- Bazel_module: bazelModuleProperties{
+ Bazel_module: BazelModuleProperties{
CanConvertToBazel: true,
},
},
@@ -344,7 +344,7 @@
},
BazelModuleBase: BazelModuleBase{
bazelProperties: properties{
- Bazel_module: bazelModuleProperties{
+ Bazel_module: BazelModuleProperties{
CanConvertToBazel: true,
Bp2build_available: proptools.BoolPtr(true),
},
diff --git a/android/filegroup.go b/android/filegroup.go
index 0ca5dc5..f30ee51 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -75,7 +75,8 @@
// https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct {
- Srcs bazel.LabelListAttribute
+ Srcs bazel.LabelListAttribute
+ Applicable_licenses bazel.LabelListAttribute
}
type bazelAidlLibraryAttributes struct {
diff --git a/android/package.go b/android/package.go
index 2bf6521..7fbc700 100644
--- a/android/package.go
+++ b/android/package.go
@@ -15,6 +15,8 @@
package android
import (
+ "path/filepath"
+
"android/soong/bazel"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -39,8 +41,8 @@
}
type bazelPackageAttributes struct {
- Default_visibility []string
- Default_applicable_licenses bazel.LabelListAttribute
+ Default_visibility []string
+ Default_package_metadata bazel.LabelListAttribute
}
type packageModule struct {
@@ -53,13 +55,32 @@
var _ Bazelable = &packageModule{}
func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+ defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses))
+ // If METADATA file exists in the package, add it to package(default_package_metadata=) using a
+ // filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel
+ // using attribute "applicable_licenses".
+ // Attribute applicable_licenses of filegroup "default_metadata_file" has to be set to [],
+ // otherwise Bazel reports cyclic reference error.
+ if existed, _, _ := ctx.Config().fs.Exists(filepath.Join(ctx.ModuleDir(), "METADATA")); existed {
+ ctx.CreateBazelTargetModule(
+ bazel.BazelTargetModuleProperties{
+ Rule_class: "filegroup",
+ },
+ CommonAttributes{Name: "default_metadata_file"},
+ &bazelFilegroupAttributes{
+ Srcs: bazel.MakeLabelListAttribute(BazelLabelForModuleSrc(ctx, []string{"METADATA"})),
+ Applicable_licenses: bazel.LabelListAttribute{Value: bazel.LabelList{Includes: []bazel.Label{}}, EmitEmptyList: true},
+ })
+ defaultPackageMetadata.Value.Add(&bazel.Label{Label: ":default_metadata_file"})
+ }
+
ctx.CreateBazelTargetModule(
bazel.BazelTargetModuleProperties{
Rule_class: "package",
},
CommonAttributes{},
&bazelPackageAttributes{
- Default_applicable_licenses: bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses)),
+ Default_package_metadata: defaultPackageMetadata,
// FIXME(asmundak): once b/221436821 is resolved
Default_visibility: []string{"//visibility:public"},
})
diff --git a/android/testing.go b/android/testing.go
index fc39a9c..2a9c658 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -813,6 +813,20 @@
return path.RelativeToTop()
}
+func allOutputs(p BuildParams) []string {
+ outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
+ if p.Output != nil {
+ outputs = append(outputs, p.Output)
+ }
+ return outputs.Strings()
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (p TestingBuildParams) AllOutputs() []string {
+ return allOutputs(p.BuildParams)
+}
+
// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
type baseTestingComponent struct {
config Config
@@ -954,12 +968,7 @@
func (b baseTestingComponent) allOutputs() []string {
var outputFullPaths []string
for _, p := range b.provider.BuildParamsForTests() {
- outputs := append(WritablePaths(nil), p.Outputs...)
- outputs = append(outputs, p.ImplicitOutputs...)
- if p.Output != nil {
- outputs = append(outputs, p.Output)
- }
- outputFullPaths = append(outputFullPaths, outputs.Strings()...)
+ outputFullPaths = append(outputFullPaths, allOutputs(p)...)
}
return outputFullPaths
}
diff --git a/apex/apex.go b/apex/apex.go
index 50c9957..6a64ad6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2681,7 +2681,7 @@
}
pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex()
- if pathInApex != "" && !java.SkipDexpreoptBootJars(ctx) {
+ if pathInApex != "" {
pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index d784818..3e55ccc 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -497,6 +497,26 @@
})
})
+ t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+ java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+ dexpreopt.FixtureDisableDexpreoptBootImages(true),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/boot-image.prof",
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+ })
+
t.Run("boot image disable generate profile", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 928a1f2..7f7aa6a 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -80,6 +80,7 @@
static_libs: ["static_lib_dep"],
java_version: "7",
certificate: "foocert",
+ required: ["static_lib_dep"],
}
`,
ExpectedBazelTargets: []string{
diff --git a/bp2build/package_conversion_test.go b/bp2build/package_conversion_test.go
index 3704b2d..ce848e4 100644
--- a/bp2build/package_conversion_test.go
+++ b/bp2build/package_conversion_test.go
@@ -15,9 +15,10 @@
package bp2build
import (
+ "testing"
+
"android/soong/android"
"android/soong/genrule"
- "testing"
)
func registerDependentModules(ctx android.RegistrationContext) {
@@ -29,6 +30,7 @@
tests := []struct {
description string
modules string
+ fs map[string]string
expected []ExpectedRuleTarget
}{
{
@@ -50,8 +52,8 @@
"package",
"",
AttrNameToString{
- "default_applicable_licenses": `[":my_license"]`,
- "default_visibility": `["//visibility:public"]`,
+ "default_package_metadata": `[":my_license"]`,
+ "default_visibility": `["//visibility:public"]`,
},
android.HostAndDeviceDefault,
},
@@ -67,6 +69,57 @@
},
},
},
+ {
+ description: "package has METADATA file",
+ fs: map[string]string{
+ "METADATA": ``,
+ },
+ modules: `
+license {
+ name: "my_license",
+ visibility: [":__subpackages__"],
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ license_text: ["NOTICE"],
+}
+
+package {
+ default_applicable_licenses: ["my_license"],
+}
+`,
+ expected: []ExpectedRuleTarget{
+ {
+ "package",
+ "",
+ AttrNameToString{
+ "default_package_metadata": `[
+ ":my_license",
+ ":default_metadata_file",
+ ]`,
+ "default_visibility": `["//visibility:public"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ {
+ "android_license",
+ "my_license",
+ AttrNameToString{
+ "license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+ "license_text": `"NOTICE"`,
+ "visibility": `[":__subpackages__"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ {
+ "filegroup",
+ "default_metadata_file",
+ AttrNameToString{
+ "applicable_licenses": `[]`,
+ "srcs": `["METADATA"]`,
+ },
+ android.HostAndDeviceDefault,
+ },
+ },
+ },
}
for _, test := range tests {
expected := make([]string, 0, len(test.expected))
@@ -80,6 +133,7 @@
ModuleTypeUnderTestFactory: android.PackageFactory,
Blueprint: test.modules,
ExpectedBazelTargets: expected,
+ Filesystem: test.fs,
})
}
}
diff --git a/cc/cc.go b/cc/cc.go
index 3fbefcd..7237686 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1086,7 +1086,7 @@
panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
}
-func (c *Module) FuzzSharedLibraries() android.Paths {
+func (c *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
return fuzzer.sharedLibraries
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 7aa8b91..dfefc11 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -103,7 +103,7 @@
*baseCompiler
fuzzPackagedModule fuzz.FuzzPackagedModule
installedSharedDeps []string
- sharedLibraries android.Paths
+ sharedLibraries android.RuleBuilderInstalls
}
func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -213,19 +213,19 @@
}
func SharedLibraryInstallLocation(
- libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
+ libraryBase string, isHost bool, fuzzDir string, archString string) string {
installLocation := "$(PRODUCT_OUT)/data"
if isHost {
installLocation = "$(HOST_OUT)"
}
installLocation = filepath.Join(
- installLocation, fuzzDir, archString, "lib", libraryPath.Base())
+ installLocation, fuzzDir, archString, "lib", libraryBase)
return installLocation
}
// Get the device-only shared library symbols install directory.
-func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
- return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
+func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
+ return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
}
func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
@@ -242,15 +242,16 @@
// Grab the list of required shared libraries.
fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
- for _, lib := range fuzzBin.sharedLibraries {
+ for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
+ install := ruleBuilderInstall.To
fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
SharedLibraryInstallLocation(
- lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+ install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
// Also add the dependency on the shared library symbols dir.
if !ctx.Host() {
fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
- SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
}
}
}
@@ -422,7 +423,7 @@
files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
// The executable.
- files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
+ files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
if !ok {
@@ -453,19 +454,25 @@
// GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
// packaging.
-func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
var files []fuzz.FileToZip
fuzzDir := "fuzz"
- for _, library := range sharedLibraries {
- files = append(files, fuzz.FileToZip{library, destinationPathPrefix})
+ for _, ruleBuilderInstall := range sharedLibraries {
+ library := ruleBuilderInstall.From
+ install := ruleBuilderInstall.To
+ files = append(files, fuzz.FileToZip{
+ SourceFilePath: library,
+ DestinationPathPrefix: destinationPathPrefix,
+ DestinationPath: install,
+ })
// For each architecture-specific shared library dependency, we need to
// install it to the output directory. Setup the install destination here,
// which will be used by $(copy-many-files) in the Make backend.
installDestination := SharedLibraryInstallLocation(
- library, module.Host(), fuzzDir, archString)
+ install, module.Host(), fuzzDir, archString)
if (*sharedLibraryInstalled)[installDestination] {
continue
}
@@ -483,7 +490,7 @@
// we want symbolization tools (like `stack`) to be able to find the symbols
// in $ANDROID_PRODUCT_OUT/symbols automagically.
if !module.Host() {
- symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
+ symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
library.String()+":"+symbolsInstallDestination)
@@ -497,12 +504,12 @@
// VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
// runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
// have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.ModuleContext) (android.Paths, []android.Module) {
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
seen := make(map[string]bool)
recursed := make(map[string]bool)
deps := []android.Module{}
- var sharedLibraries android.Paths
+ var sharedLibraries android.RuleBuilderInstalls
// Enumerate the first level of dependencies, as we discard all non-library
// modules in the BFS loop below.
@@ -510,22 +517,36 @@
if !IsValidSharedDependency(dep) {
return
}
+ if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
+ return
+ }
if seen[ctx.OtherModuleName(dep)] {
return
}
seen[ctx.OtherModuleName(dep)] = true
deps = append(deps, dep)
- sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+
+ sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ installDestination := sharedLibraryInfo.SharedLibrary.Base()
+ ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, dep, "unstripped"), installDestination}
+ sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
})
ctx.WalkDeps(func(child, parent android.Module) bool {
if !IsValidSharedDependency(child) {
return false
}
+ if !ctx.OtherModuleHasProvider(child, SharedLibraryInfoProvider) {
+ return false
+ }
if !seen[ctx.OtherModuleName(child)] {
seen[ctx.OtherModuleName(child)] = true
deps = append(deps, child)
- sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+
+ sharedLibraryInfo := ctx.OtherModuleProvider(child, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ installDestination := sharedLibraryInfo.SharedLibrary.Base()
+ ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, child, "unstripped"), installDestination}
+ sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
}
if recursed[ctx.OtherModuleName(child)] {
diff --git a/cc/linkable.go b/cc/linkable.go
index 9578807..557f5d2 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -130,7 +130,7 @@
// FuzzSharedLibraries returns the shared library dependencies for this module.
// Expects that IsFuzzModule returns true.
- FuzzSharedLibraries() android.Paths
+ FuzzSharedLibraries() android.RuleBuilderInstalls
Device() bool
Host() bool
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 51f23c5..d2531c0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -250,7 +250,11 @@
for _, path := range m.VintfFragments() {
prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
}
- prop.MinSdkVersion = m.MinSdkVersion()
+ if m.IsPrebuilt() {
+ prop.MinSdkVersion = "apex_inherit"
+ } else {
+ prop.MinSdkVersion = m.MinSdkVersion()
+ }
// install config files. ignores any duplicates.
for _, path := range append(m.InitRc(), m.VintfFragments()...) {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 5b69a10..c5431b3 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -23,6 +23,17 @@
"testing"
)
+func checkJsonContents(t *testing.T, ctx android.TestingSingleton, jsonPath string, key string, value string) {
+ jsonOut := ctx.MaybeOutput(jsonPath)
+ if jsonOut.Rule == nil {
+ t.Errorf("%q expected but not found", jsonPath)
+ return
+ }
+ if !strings.Contains(jsonOut.Args["content"], fmt.Sprintf("%q:%q", key, value)) {
+ t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
+ }
+}
+
func TestVendorSnapshotCapture(t *testing.T) {
bp := `
cc_library {
@@ -52,6 +63,7 @@
name: "libvendor_available",
vendor_available: true,
nocrt: true,
+ min_sdk_version: "29",
}
cc_library_headers {
@@ -155,6 +167,9 @@
filepath.Join(staticDir, "libvendor_available.a.json"),
filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
+ checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
+ checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
+
// For binary executables, all vendor:true and vendor_available modules are captured.
if archType == "arm64" {
binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 301246a..1c6aaad 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -94,12 +94,12 @@
}, {
flag: "--upload-metrics-only",
description: "upload metrics without building anything",
- config: uploadOnlyConfig,
+ config: build.UploadOnlyConfig,
stdio: stdio,
// Upload-only mode mostly skips to the metrics-uploading phase of soong_ui.
// However, this invocation marks the true "end of the build", and thus we
// need to update the total runtime of the build to include this upload step.
- run: updateTotalRealTime,
+ run: finalizeBazelMetrics,
},
}
@@ -203,8 +203,6 @@
bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
- //the profile file generated by Bazel"
- bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
metricsFiles := []string{
buildErrorFile, // build error strings
rbeMetricsFile, // high level metrics related to remote build execution.
@@ -226,7 +224,7 @@
criticalPath.WriteToMetrics(met)
met.Dump(soongMetricsFile)
if !config.SkipMetricsUpload() {
- build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
+ build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
}
}()
c.run(buildCtx, config, args)
@@ -451,14 +449,6 @@
return build.NewConfig(ctx)
}
-// uploadOnlyConfig explicitly requires no arguments.
-func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
- if len(args) > 0 {
- fmt.Printf("--upload-only does not require arguments.")
- }
- return build.UploadOnlyConfig(ctx)
-}
-
func buildActionConfig(ctx build.Context, args ...string) build.Config {
flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
flags.SetOutput(ctx.Writer)
@@ -700,6 +690,15 @@
}
}
+func finalizeBazelMetrics(ctx build.Context, config build.Config, args []string) {
+ updateTotalRealTime(ctx, config, args)
+
+ logsDir := config.LogsDir()
+ logsPrefix := config.GetLogsPrefix()
+ bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
+ bazelProfileFile := filepath.Join(logsDir, logsPrefix+"analyzed_bazel_profile.txt")
+ build.ProcessBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx, config)
+}
func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
@@ -710,7 +709,7 @@
}
met := ctx.ContextImpl.Metrics
- err = met.UpdateTotalRealTime(data)
+ err = met.UpdateTotalRealTimeAndNonZeroExit(data, config.BazelExitCode())
if err != nil {
ctx.Fatal(err)
}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 609a29c..0cc3bd6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -475,7 +475,16 @@
ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
}
+func IsDex2oatNeeded(ctx android.PathContext) bool {
+ global := GetGlobalConfig(ctx)
+ return !global.DisablePreopt || !global.DisablePreoptBootImages
+}
+
func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+ if !IsDex2oatNeeded(ctx) {
+ return nil
+ }
+
dex2oatBin := dex2oatModuleName(ctx.Config())
// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index a590c72..2b38793 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -100,11 +100,19 @@
return rule, nil
}
+// If dexpreopt is applicable to the module, returns whether dexpreopt is disabled. Otherwise, the
+// behavior is undefined.
+// When it returns true, dexpreopt artifacts will not be generated, but profile will still be
+// generated if profile-guided compilation is requested.
func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
if ctx.Config().UnbundledBuild() {
return true
}
+ if global.DisablePreopt {
+ return true
+ }
+
if contains(global.DisablePreoptModules, module.Name) {
return true
}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 47ae494..6ed0736 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -174,3 +174,17 @@
dexpreoptConfig.DisableGenerateProfile = disable
})
}
+
+// FixtureDisableDexpreoptBootImages sets the DisablePreoptBootImages property in the global config.
+func FixtureDisableDexpreoptBootImages(disable bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.DisablePreoptBootImages = disable
+ })
+}
+
+// FixtureDisableDexpreopt sets the DisablePreopt property in the global config.
+func FixtureDisableDexpreopt(disable bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.DisablePreopt = disable
+ })
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index f76529d..2a1b404 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -61,6 +61,7 @@
type FileToZip struct {
SourceFilePath android.Path
DestinationPathPrefix string
+ DestinationPath string
}
type ArchOs struct {
@@ -443,7 +444,7 @@
FlagWithOutput("-o ", corpusZip)
rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
- files = append(files, FileToZip{corpusZip, ""})
+ files = append(files, FileToZip{SourceFilePath: corpusZip})
}
// Package the data into a zipfile.
@@ -456,17 +457,17 @@
command.FlagWithArg("-C ", intermediateDir)
command.FlagWithInput("-f ", f)
}
- files = append(files, FileToZip{dataZip, ""})
+ files = append(files, FileToZip{SourceFilePath: dataZip})
}
// The dictionary.
if fuzzModule.Dictionary != nil {
- files = append(files, FileToZip{fuzzModule.Dictionary, ""})
+ files = append(files, FileToZip{SourceFilePath: fuzzModule.Dictionary})
}
// Additional fuzz config.
if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
- files = append(files, FileToZip{fuzzModule.Config, ""})
+ files = append(files, FileToZip{SourceFilePath: fuzzModule.Config})
}
return files
@@ -485,6 +486,9 @@
} else {
command.Flag("-P ''")
}
+ if file.DestinationPath != "" {
+ command.FlagWithArg("-e ", file.DestinationPath)
+ }
command.FlagWithInput("-f ", file.SourceFilePath)
}
@@ -502,7 +506,7 @@
}
s.FuzzTargets[module.Name()] = true
- archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
+ archDirs[archOs] = append(archDirs[archOs], FileToZip{SourceFilePath: fuzzZip})
return archDirs[archOs], true
}
diff --git a/java/app.go b/java/app.go
index da9c6f3..4656cae 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1622,7 +1622,7 @@
ctx.CreateBazelTargetModule(
props,
- android.CommonAttributes{Name: a.Name()},
+ android.CommonAttributes{Name: a.Name(), SkipData: proptools.BoolPtr(true)},
appAttrs,
)
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f692563..6ccf5a3 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -506,7 +506,7 @@
}
}
- if SkipDexpreoptBootJars(ctx) {
+ if !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
@@ -901,10 +901,6 @@
// produceBootImageFiles builds the boot image files from the source if it is required.
func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
- if SkipDexpreoptBootJars(ctx) {
- return bootImageOutputs{}
- }
-
// Only generate the boot image if the configuration does not skip it.
return b.generateBootImageBuildActions(ctx, imageConfig)
}
@@ -929,6 +925,13 @@
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
+ // If dexpreopt of boot image jars should be skipped, generate only a profile.
+ if SkipDexpreoptBootJars(ctx) {
+ return bootImageOutputs{
+ profile: profile,
+ }
+ }
+
// Build boot image files for the host variants.
buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
diff --git a/java/dex.go b/java/dex.go
index 4d6aa34..f7c1361 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -140,7 +140,7 @@
`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
`--no-data-resources ` +
`-printmapping ${outDict} ` +
- `--pg-conf-output ${outConfig} ` +
+ `-printconfiguration ${outConfig} ` +
`-printusage ${outUsage} ` +
`--deps-file ${out}.d ` +
`$r8Flags && ` +
diff --git a/java/dex_test.go b/java/dex_test.go
index 97fc3d0..2ba3831 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -23,7 +23,7 @@
)
func TestR8(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app {
name: "app",
srcs: ["foo.java"],
@@ -191,7 +191,7 @@
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
- fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+ fixturePreparer := PrepareForTestWithJavaDefaultModules
if tc.unbundled {
fixturePreparer = android.GroupFixturePreparers(
fixturePreparer,
@@ -258,7 +258,7 @@
}
func TestR8Flags(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app {
name: "app",
srcs: ["foo.java"],
@@ -287,7 +287,7 @@
}
func TestD8(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["foo.java"],
@@ -328,7 +328,7 @@
}
func TestProguardFlagsInheritance(t *testing.T) {
- result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
android_app {
name: "app",
static_libs: [
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0ffedf6..a96b312 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -180,6 +180,8 @@
return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
}
+// Returns whether dexpreopt is applicable to the module.
+// When it returns true, neither profile nor dexpreopt artifacts will be generated.
func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
if !ctx.Device() {
return true
@@ -205,14 +207,6 @@
global := dexpreopt.GetGlobalConfig(ctx)
- if global.DisablePreopt {
- return true
- }
-
- if inList(moduleName(ctx), global.DisablePreoptModules) {
- return true
- }
-
isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
if isApexVariant(ctx) {
// Don't preopt APEX variant module unless the module is an APEX system server jar.
@@ -232,7 +226,7 @@
}
func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
- if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+ if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
dexpreopt.RegisterToolDeps(ctx)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f4c0935..8e79674 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -500,9 +500,6 @@
// Generate build rules for boot images.
func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
- if SkipDexpreoptBootJars(ctx) {
- return
- }
if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
// No module has enabled dexpreopting, so we assume there will be no boot image to make.
return
@@ -1002,7 +999,7 @@
// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
// correct output directories.
func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
- if d.dexpreoptConfigForMake != nil {
+ if d.dexpreoptConfigForMake != nil && !SkipDexpreoptBootJars(ctx) {
ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
}
@@ -1014,6 +1011,10 @@
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
}
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
global := dexpreopt.GetGlobalConfig(ctx)
dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 3d2c5c3..f91ac5c 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -438,3 +438,28 @@
android.AssertIntEquals(t, "entries count", 0, len(entriesList))
}
+
+func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
+ preparers := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithFakeApexMutator,
+ dexpreopt.FixtureDisableDexpreopt(true),
+ )
+
+ result := preparers.RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ installable: true,
+ dex_preopt: {
+ profile: "art-profile",
+ },
+ srcs: ["a.java"],
+ }`)
+
+ ctx := result.TestContext
+ dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+
+ expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+
+ android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
+}
diff --git a/java/fuzz.go b/java/fuzz.go
index 1d6b913..9a0c908 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -250,11 +250,11 @@
files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
// Add .jar
- files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""})
+ files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile})
// Add jni .so files
for _, fPath := range javaFuzzModule.jniFilePaths {
- files = append(files, fuzz.FileToZip{fPath, ""})
+ files = append(files, fuzz.FileToZip{SourceFilePath: fPath})
}
archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
diff --git a/java/java.go b/java/java.go
index b751b9a..06130cd 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2980,7 +2980,7 @@
func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
return bazel.BazelTargetModuleProperties{
Rule_class: "java_library",
- Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ Bzl_load_location: "//build/bazel/rules/java:library.bzl",
}
}
@@ -3089,7 +3089,7 @@
props := bazel.BazelTargetModuleProperties{
Rule_class: "java_binary",
- Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ Bzl_load_location: "@rules_java//java:defs.bzl",
}
binAttrs := &javaBinaryHostAttributes{
Runtime_deps: runtimeDeps,
@@ -3145,7 +3145,7 @@
}
props := bazel.BazelTargetModuleProperties{
Rule_class: "java_import",
- Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+ Bzl_load_location: "//build/bazel/rules/java:import.bzl",
}
name := android.RemoveOptionalPrebuiltPrefix(i.Name())
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0ea3609..d5779f7 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -103,7 +103,7 @@
func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
b.hiddenAPIDepsMutator(ctx)
- if SkipDexpreoptBootJars(ctx) {
+ if !dexpreopt.IsDex2oatNeeded(ctx) {
return
}
@@ -187,11 +187,6 @@
bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
- // Nothing to do if skipping the dexpreopt of boot image jars.
- if SkipDexpreoptBootJars(ctx) {
- return
- }
-
b.generateBootImageBuildActions(ctx, platformModules, apexModules)
}
@@ -429,6 +424,12 @@
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
+ // If dexpreopt of boot image jars should be skipped, generate only a profile.
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.DisablePreoptBootImages {
+ return
+ }
+
// Build boot image files for the android variants.
androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 0740467..044802e 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -104,20 +104,51 @@
return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
}
+func hasBazelPrebuilt(module string) bool {
+ return module == "android" || module == "core-for-system-modules"
+}
+
+func bazelPrebuiltApiModuleName(module, scope, version string) string {
+ bazelModule := module
+ switch module {
+ case "android":
+ bazelModule = "android_jar"
+ case "core-for-system-modules":
+ bazelModule = "core_jar"
+ }
+ bazelVersion := version
+ if version == "current" {
+ bazelVersion = strconv.Itoa(android.FutureApiLevelInt)
+ }
+ bazelScope := scope
+ switch scope {
+ case "module-lib":
+ bazelScope = "module"
+ case "system-server":
+ bazelScope = "system_server"
+ }
+ return fmt.Sprintf("//prebuilts/sdk:%s_%s_%s", bazelScope, bazelVersion, bazelModule)
+}
+
func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
props := struct {
- Name *string
- Jars []string
- Sdk_version *string
- Installable *bool
- Compile_dex *bool
- }{}
- 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)
- props.Compile_dex = proptools.BoolPtr(compileDex)
-
+ Name *string
+ Jars []string
+ Sdk_version *string
+ Installable *bool
+ Compile_dex *bool
+ Bazel_module android.BazelModuleProperties
+ }{
+ Name: proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)),
+ Jars: []string{path},
+ Sdk_version: proptools.StringPtr(sdkVersion),
+ Installable: proptools.BoolPtr(false),
+ Compile_dex: proptools.BoolPtr(compileDex),
+ }
+ if hasBazelPrebuilt(module) {
+ props.Bazel_module = android.BazelModuleProperties{
+ Label: proptools.StringPtr(bazelPrebuiltApiModuleName(module, scope, version))}
+ }
mctx.CreateModule(ImportFactory, &props)
}
diff --git a/rust/fuzz.go b/rust/fuzz.go
index d7e7ddf..c2b9405 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -31,7 +31,7 @@
*binaryDecorator
fuzzPackagedModule fuzz.FuzzPackagedModule
- sharedLibraries android.Paths
+ sharedLibraries android.RuleBuilderInstalls
installedSharedDeps []string
}
@@ -119,15 +119,17 @@
// Grab the list of required shared libraries.
fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
- for _, lib := range fuzz.sharedLibraries {
+ for _, ruleBuilderInstall := range fuzz.sharedLibraries {
+ install := ruleBuilderInstall.To
+
fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
cc.SharedLibraryInstallLocation(
- lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+ install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
// Also add the dependency on the shared library symbols dir.
if !ctx.Host() {
fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
- cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+ cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
}
}
}
diff --git a/rust/rust.go b/rust/rust.go
index 7b520cd..dc53cc0 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -649,7 +649,7 @@
panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
}
-func (mod *Module) FuzzSharedLibraries() android.Paths {
+func (mod *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
return fuzzer.sharedLibraries
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 3a2ecc0..6159ea9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -19,12 +19,14 @@
"testing"
"android/soong/android"
+ "android/soong/dexpreopt"
"android/soong/java"
)
var prepareForSdkTestWithJava = android.GroupFixturePreparers(
java.PrepareForTestWithJavaBuildComponents,
PrepareForTestWithSdkBuildComponents,
+ dexpreopt.PrepareForTestWithFakeDex2oatd,
// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
// consistent and all files referenced from the snapshot's Android.bp file have actually been
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index b79754c..959ae4c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -46,6 +46,7 @@
"soong-ui-tracer",
],
srcs: [
+ "bazel_metrics.go",
"build.go",
"cleanbuild.go",
"config.go",
diff --git a/ui/build/bazel_metrics.go b/ui/build/bazel_metrics.go
new file mode 100644
index 0000000..c0690c1
--- /dev/null
+++ b/ui/build/bazel_metrics.go
@@ -0,0 +1,135 @@
+// Copyright 2023 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 build
+
+// This file contains functionality to parse bazel profile data into
+// a bazel_metrics proto, defined in build/soong/ui/metrics/bazel_metrics_proto
+// These metrics are later uploaded in upload.go
+
+import (
+ "bufio"
+ "os"
+ "strconv"
+ "strings"
+
+ "android/soong/shared"
+ "google.golang.org/protobuf/proto"
+
+ bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
+)
+
+func parseTimingToNanos(str string) int64 {
+ millisString := removeDecimalPoint(str)
+ timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+ return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+ percentageString := removeDecimalPoint(str)
+ //remove the % at the end of the string
+ percentage := strings.ReplaceAll(percentageString, "%", "")
+ percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+ return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+ // The format is always 0.425 or 10.425
+ return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+ words := strings.Fields(line)
+ timing := words[3]
+ return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+ words := strings.Fields(line)
+ getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+ // Sample lines include:
+ // Total launch phase time 0.011 s 2.59%
+ // Total target pattern evaluation phase time 0.011 s 2.59%
+ var beginning int
+ var end int
+ for ind, word := range words {
+ if word == "Total" {
+ beginning = ind + 1
+ } else if beginning > 0 && word == "phase" {
+ end = ind
+ break
+ }
+ }
+ phaseName := strings.Join(words[beginning:end], " ")
+
+ // end is now "phase" - advance by 2 for timing and 4 for percentage
+ percentageString := words[end+4]
+ timingString := words[end+2]
+ timing := parseTimingToNanos(timingString)
+ percentagePortion := parsePercentageToTenThousandths(percentageString)
+ return phaseName, timing, percentagePortion
+ }
+
+ phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+ phaseTiming := bazel_metrics_proto.PhaseTiming{}
+ phaseTiming.DurationNanos = &timing
+ phaseTiming.PortionOfBuildTime = &portion
+
+ phaseTiming.PhaseName = &phaseName
+ return phaseTiming
+}
+
+// This method takes a file created by bazel's --analyze-profile mode and
+// writes bazel metrics data to the provided filepath.
+func ProcessBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context, config Config) {
+ if bazelProfileFile == "" {
+ return
+ }
+
+ readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+ //serialize the proto, write it
+ bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+ file, err := os.ReadFile(filepath)
+ if err != nil {
+ ctx.Fatalln("Error reading metrics file\n", err)
+ }
+
+ scanner := bufio.NewScanner(strings.NewReader(string(file)))
+ scanner.Split(bufio.ScanLines)
+
+ var phaseTimings []*bazel_metrics_proto.PhaseTiming
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "Total run time") {
+ total := parseTotal(line)
+ bazelMetrics.Total = &total
+ } else if strings.HasPrefix(line, "Total") {
+ phaseTiming := parsePhaseTiming(line)
+ phaseTimings = append(phaseTimings, &phaseTiming)
+ }
+ }
+ bazelMetrics.PhaseTimings = phaseTimings
+
+ return bazelMetrics
+ }
+
+ if _, err := os.Stat(bazelProfileFile); err != nil {
+ // We can assume bazel didn't run if the profile doesn't exist
+ return
+ }
+ bazelProto := readBazelProto(bazelProfileFile)
+ bazelProto.ExitCode = proto.Int32(config.bazelExitCode)
+ shared.Save(&bazelProto, bazelMetricsFile)
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index 2dda52a..8ec9680 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -89,7 +89,8 @@
skipMetricsUpload bool
buildStartedTime int64 // For metrics-upload-only - manually specify a build-started time
buildFromTextStub bool
- ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
+ ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
+ bazelExitCode int32 // For b-runs - necessary for updating NonZeroExit
// From the product config
katiArgs []string
@@ -298,11 +299,12 @@
return true
}
-func UploadOnlyConfig(ctx Context, _ ...string) Config {
+func UploadOnlyConfig(ctx Context, args ...string) Config {
ret := &configImpl{
environ: OsEnvironment(),
sandboxConfig: &SandboxConfig{},
}
+ ret.parseArgs(ctx, args)
srcDir := absPath(ctx, ".")
bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
if err := loadEnvConfig(ctx, ret, bc); err != nil {
@@ -883,6 +885,14 @@
}
} else if arg == "--ensure-allowlist-integrity" {
c.ensureAllowlistIntegrity = true
+ } else if strings.HasPrefix(arg, "--bazel-exit-code=") {
+ bazelExitCodeStr := strings.TrimPrefix(arg, "--bazel-exit-code=")
+ val, err := strconv.Atoi(bazelExitCodeStr)
+ if err == nil {
+ c.bazelExitCode = int32(val)
+ } else {
+ ctx.Fatalf("Error parsing bazel-exit-code", err)
+ }
} else if len(arg) > 0 && arg[0] == '-' {
parseArgNum := func(def int) int {
if len(arg) > 2 {
@@ -1723,6 +1733,10 @@
return time.UnixMilli(c.buildStartedTime)
}
+func (c *configImpl) BazelExitCode() int32 {
+ return c.bazelExitCode
+}
+
func GetMetricsUploader(topDir string, env *Environment) string {
if p, ok := env.Get("METRICS_UPLOADER"); ok {
metricsUploader := filepath.Join(topDir, p)
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index efe7478..b83b5cb 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -150,6 +150,7 @@
"PRODUCT_INCLUDE_TAGS",
"PRODUCT_SOURCE_ROOT_DIRS",
"TARGET_PRODUCT",
+ "TARGET_RELEASE",
"TARGET_BUILD_VARIANT",
"TARGET_BUILD_APPS",
"TARGET_BUILD_UNBUNDLED",
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 3f628cf..62079fe 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -87,6 +87,8 @@
"TEST_MAPPING",
// Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
+ // METADATA file of packages
+ "METADATA",
},
// Bazel Starlark configuration files and all .mk files for product/board configuration.
IncludeSuffixes: []string{".bzl", ".mk"},
@@ -189,6 +191,13 @@
ctx.Fatalf("Could not find OWNERS: %v", err)
}
+ // Recursively look for all METADATA files.
+ metadataFiles := f.FindNamedAt(".", "METADATA")
+ err = dumpListToFile(ctx, config, metadataFiles, filepath.Join(dumpDir, "METADATA.list"))
+ if err != nil {
+ ctx.Fatalf("Could not find METADATA: %v", err)
+ }
+
// Recursively look for all TEST_MAPPING files.
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 563199b..18bf3b9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -400,6 +400,8 @@
pbi.Inputs = append(pbi.Inputs,
config.Bp2BuildFilesMarkerFile(),
filepath.Join(config.FileListDir(), "bazel.list"))
+ case bp2buildFilesTag:
+ pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list"))
}
invocations = append(invocations, pbi)
}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 1e6d94a..9f14bdd 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,21 +18,16 @@
// another.
import (
- "bufio"
"fmt"
"io/ioutil"
"os"
"path/filepath"
- "strconv"
- "strings"
"time"
- "android/soong/shared"
"android/soong/ui/metrics"
"google.golang.org/protobuf/proto"
- bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
upload_proto "android/soong/ui/metrics/upload_proto"
)
@@ -78,122 +73,16 @@
return metricsFiles
}
-func parseTimingToNanos(str string) int64 {
- millisString := removeDecimalPoint(str)
- timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
- return timingMillis * 1000000
-}
-
-func parsePercentageToTenThousandths(str string) int32 {
- percentageString := removeDecimalPoint(str)
- //remove the % at the end of the string
- percentage := strings.ReplaceAll(percentageString, "%", "")
- percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
- return int32(percentagePortion)
-}
-
-func removeDecimalPoint(numString string) string {
- // The format is always 0.425 or 10.425
- return strings.ReplaceAll(numString, ".", "")
-}
-
-func parseTotal(line string) int64 {
- words := strings.Fields(line)
- timing := words[3]
- return parseTimingToNanos(timing)
-}
-
-func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
- words := strings.Fields(line)
- getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
- // Sample lines include:
- // Total launch phase time 0.011 s 2.59%
- // Total target pattern evaluation phase time 0.011 s 2.59%
- var beginning int
- var end int
- for ind, word := range words {
- if word == "Total" {
- beginning = ind + 1
- } else if beginning > 0 && word == "phase" {
- end = ind
- break
- }
- }
- phaseName := strings.Join(words[beginning:end], " ")
-
- // end is now "phase" - advance by 2 for timing and 4 for percentage
- percentageString := words[end+4]
- timingString := words[end+2]
- timing := parseTimingToNanos(timingString)
- percentagePortion := parsePercentageToTenThousandths(percentageString)
- return phaseName, timing, percentagePortion
- }
-
- phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
- phaseTiming := bazel_metrics_proto.PhaseTiming{}
- phaseTiming.DurationNanos = &timing
- phaseTiming.PortionOfBuildTime = &portion
-
- phaseTiming.PhaseName = &phaseName
- return phaseTiming
-}
-
-// This method takes a file created by bazel's --analyze-profile mode and
-// writes bazel metrics data to the provided filepath.
-// TODO(b/279987768) - move this outside of upload.go
-func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
- if bazelProfileFile == "" {
- return
- }
-
- readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
- //serialize the proto, write it
- bazelMetrics := bazel_metrics_proto.BazelMetrics{}
-
- file, err := os.ReadFile(filepath)
- if err != nil {
- ctx.Fatalln("Error reading metrics file\n", err)
- }
-
- scanner := bufio.NewScanner(strings.NewReader(string(file)))
- scanner.Split(bufio.ScanLines)
-
- var phaseTimings []*bazel_metrics_proto.PhaseTiming
- for scanner.Scan() {
- line := scanner.Text()
- if strings.HasPrefix(line, "Total run time") {
- total := parseTotal(line)
- bazelMetrics.Total = &total
- } else if strings.HasPrefix(line, "Total") {
- phaseTiming := parsePhaseTiming(line)
- phaseTimings = append(phaseTimings, &phaseTiming)
- }
- }
- bazelMetrics.PhaseTimings = phaseTimings
-
- return bazelMetrics
- }
-
- if _, err := os.Stat(bazelProfileFile); err != nil {
- // We can assume bazel didn't run if the profile doesn't exist
- return
- }
- bazelProto := readBazelProto(bazelProfileFile)
- shared.Save(&bazelProto, bazelMetricsFile)
-}
-
// UploadMetrics uploads a set of metrics files to a server for analysis.
// The metrics files are first copied to a temporary directory
// and the uploader is then executed in the background to allow the user/system
// to continue working. Soong communicates to the uploader through the
// upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
uploader := config.MetricsUploaderApp()
- processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
-
if uploader == "" {
// If the uploader path was not specified, no metrics shall be uploaded.
return
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 58d9237..1fcded9 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -166,7 +166,7 @@
metricsUploader: tt.uploader,
}}
- UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
+ UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
})
}
}
@@ -221,7 +221,7 @@
metricsUploader: "echo",
}}
- UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
+ UploadMetrics(ctx, config, true, time.Now(), metricsFile)
t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
})
}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 82d11ed..a282e20 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -228,7 +228,7 @@
m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
-func (m *Metrics) UpdateTotalRealTime(data []byte) error {
+func (m *Metrics) UpdateTotalRealTimeAndNonZeroExit(data []byte, bazelExitCode int32) error {
if err := proto.Unmarshal(data, &m.metrics); err != nil {
return fmt.Errorf("Failed to unmarshal proto", err)
}
@@ -236,6 +236,9 @@
endTime := uint64(time.Now().UnixNano())
*m.metrics.Total.RealTime = *proto.Uint64(endTime - startTime)
+
+ bazelError := bazelExitCode != 0
+ m.metrics.NonZeroExit = proto.Bool(bazelError)
return nil
}
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index def76aa..a2ccc20 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -78,6 +78,15 @@
return nil
}
+type explicitFile struct{}
+
+func (explicitFile) String() string { return `""` }
+
+func (explicitFile) Set(s string) error {
+ fileArgsBuilder.ExplicitPathInZip(s)
+ return nil
+}
+
type dir struct{}
func (dir) String() string { return `""` }
@@ -173,6 +182,7 @@
flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
+ flags.Var(&explicitFile{}, "e", "filename to use in the zip file for the next -f argument")
flags.Parse(expandedArgs[1:])
diff --git a/zip/zip.go b/zip/zip.go
index 6f1a8ad..5e1a104 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -80,6 +80,7 @@
type FileArg struct {
PathPrefixInZip, SourcePrefixToStrip string
+ ExplicitPathInZip string
SourceFiles []string
JunkPaths bool
GlobDir string
@@ -124,6 +125,10 @@
arg := b.state
arg.SourceFiles = []string{name}
b.fileArgs = append(b.fileArgs, arg)
+
+ if b.state.ExplicitPathInZip != "" {
+ b.state.ExplicitPathInZip = ""
+ }
return b
}
@@ -189,6 +194,12 @@
return b
}
+// ExplicitPathInZip sets the path in the zip file for the next File call.
+func (b *FileArgsBuilder) ExplicitPathInZip(s string) *FileArgsBuilder {
+ b.state.ExplicitPathInZip = s
+ return b
+}
+
func (b *FileArgsBuilder) Error() error {
if b == nil {
return nil
@@ -425,7 +436,9 @@
var dest string
- if fa.JunkPaths {
+ if fa.ExplicitPathInZip != "" {
+ dest = fa.ExplicitPathInZip
+ } else if fa.JunkPaths {
dest = filepath.Base(src)
} else {
var err error
diff --git a/zip/zip_test.go b/zip/zip_test.go
index e7fdea8..c64c3f4 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -454,6 +454,60 @@
fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
},
},
+ {
+ name: "explicit path",
+ args: fileArgsBuilder().
+ ExplicitPathInZip("foo").
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("foo", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "explicit path with prefix",
+ args: fileArgsBuilder().
+ PathPrefixInZip("prefix").
+ ExplicitPathInZip("foo").
+ File("a/a/a").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("prefix/foo", fileA, zip.Deflate),
+ fh("prefix/a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "explicit path with glob",
+ args: fileArgsBuilder().
+ ExplicitPathInZip("foo").
+ File("a/a/a*").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("foo", fileA, zip.Deflate),
+ fh("a/a/b", fileB, zip.Deflate),
+ },
+ },
+ {
+ name: "explicit path with junk paths",
+ args: fileArgsBuilder().
+ JunkPaths(true).
+ ExplicitPathInZip("foo/bar").
+ File("a/a/a*").
+ File("a/a/b"),
+ compressionLevel: 9,
+
+ files: []zip.FileHeader{
+ fh("foo/bar", fileA, zip.Deflate),
+ fh("b", fileB, zip.Deflate),
+ },
+ },
// errors
{
@@ -490,6 +544,22 @@
File("d/a/a"),
err: ConflictingFileError{},
},
+ {
+ name: "error explicit path conflicting",
+ args: fileArgsBuilder().
+ ExplicitPathInZip("foo").
+ File("a/a/a").
+ ExplicitPathInZip("foo").
+ File("a/a/b"),
+ err: ConflictingFileError{},
+ },
+ {
+ name: "error explicit path conflicting glob",
+ args: fileArgsBuilder().
+ ExplicitPathInZip("foo").
+ File("a/a/*"),
+ err: ConflictingFileError{},
+ },
}
for _, test := range testCases {