diff options
36 files changed, 1119 insertions, 279 deletions
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go index ff0d33e4c..1ece9fa7d 100644 --- a/android/allowlists/allowlists.go +++ b/android/allowlists/allowlists.go @@ -132,6 +132,7 @@ var ( "external/f2fs-tools": Bp2BuildDefaultTrue, "external/flac": Bp2BuildDefaultTrueRecursively, "external/fmtlib": Bp2BuildDefaultTrueRecursively, + "external/guava": Bp2BuildDefaultTrueRecursively, "external/google-benchmark": Bp2BuildDefaultTrueRecursively, "external/googletest": Bp2BuildDefaultTrueRecursively, "external/gwp_asan": Bp2BuildDefaultTrueRecursively, @@ -385,6 +386,7 @@ var ( "build/bazel":/* recursive = */ true, "build/make/core":/* recursive = */ false, "build/bazel_common_rules":/* recursive = */ true, + "build/make/target/product/security":/* recursive = */ false, // build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive. "build/make/tools":/* recursive = */ false, "build/pesto":/* recursive = */ true, @@ -396,7 +398,6 @@ var ( "external/bazelbuild-rules_license":/* recursive = */ true, "external/bazelbuild-kotlin-rules":/* recursive = */ true, "external/bazel-skylib":/* recursive = */ true, - "external/guava":/* recursive = */ true, "external/protobuf":/* recursive = */ false, "external/python/absl-py":/* recursive = */ true, @@ -710,6 +711,8 @@ var ( // allowlisting for kotlinx_coroutines "kotlinx_coroutines", + "kotlinx_coroutines-device", + "kotlinx_coroutines-host", "annotations", "kotlinx-coroutines-android-annotation-stubs", diff --git a/android/api_levels.go b/android/api_levels.go index ea2afdf92..7214ccbb6 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -344,14 +344,17 @@ func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) { } } - canonical := ReplaceFinalizedCodenames(config, raw) - asInt, err := strconv.Atoi(canonical) - if err != nil { - return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical) + canonical, ok := getApiLevelsMapReleasedVersions()[raw] + if !ok { + asInt, err := strconv.Atoi(raw) + if err != nil { + return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw) + } + return uncheckedFinalApiLevel(asInt), nil } - apiLevel := uncheckedFinalApiLevel(asInt) - return apiLevel, nil + return uncheckedFinalApiLevel(canonical), nil + } // ApiLevelForTest returns an ApiLevel constructed from the supplied raw string. diff --git a/android/config.go b/android/config.go index 33deba500..6765f1f71 100644 --- a/android/config.go +++ b/android/config.go @@ -913,8 +913,16 @@ func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel { return c.PlatformSdkVersion() } codename := c.PlatformSdkCodename() + hostOnlyBuild := c.productVariables.DeviceArch == nil if codename == "" { - return NoneApiLevel + // There are some host-only builds (those are invoked by build-prebuilts.sh) which + // don't set platform sdk codename. Platform sdk codename makes sense only when we + // are building the platform. So we don't enforce the below panic for the host-only + // builds. + if hostOnlyBuild { + return NoneApiLevel + } + panic("Platform_sdk_codename must be set") } if codename == "REL" { panic("Platform_sdk_codename should not be REL when Platform_sdk_final is true") @@ -1418,6 +1426,21 @@ func (c *deviceConfig) PgoAdditionalProfileDirs() []string { return c.config.productVariables.PgoAdditionalProfileDirs } +// AfdoProfile returns fully qualified path associated to the given module name +func (c *deviceConfig) AfdoProfile(name string) (*string, error) { + for _, afdoProfile := range c.config.productVariables.AfdoProfiles { + split := strings.Split(afdoProfile, ":") + if len(split) != 3 { + return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+ + "The expected format is <module>:<fully-qualified-path-to-fdo_profile>", afdoProfile) + } + if split[0] == name { + return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil + } + } + return nil, nil +} + func (c *deviceConfig) VendorSepolicyDirs() []string { return c.config.productVariables.BoardVendorSepolicyDirs } @@ -1894,3 +1917,7 @@ func (c *config) ApiSurfacesDir(s ApiSurface, version string) string { func (c *config) BuildFromTextStub() bool { return c.buildFromTextStub } + +func (c *config) SetBuildFromTextStub(b bool) { + c.buildFromTextStub = b +} diff --git a/android/filegroup.go b/android/filegroup.go index 38de8558f..c259f2106 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -126,7 +126,7 @@ func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) { props := bazel.BazelTargetModuleProperties{ Rule_class: "aidl_library", - Bzl_load_location: "//build/bazel/rules/aidl:library.bzl", + Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl", } ctx.CreateBazelTargetModule( diff --git a/android/packaging.go b/android/packaging.go index 4a9b59172..c764a6df5 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -238,11 +238,11 @@ func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]Packa // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec // entries into the specified directory. -func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) { +func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) { seenDir := make(map[string]bool) for _, k := range SortedKeys(specs) { ps := specs[k] - destPath := dir.Join(ctx, ps.relPathInPackage).String() + destPath := filepath.Join(dir.String(), ps.relPathInPackage) destDir := filepath.Dir(destPath) entries = append(entries, ps.relPathInPackage) if _, ok := seenDir[destDir]; !ok { diff --git a/android/sdk_version.go b/android/sdk_version.go index 1f01dc64f..0ae807352 100644 --- a/android/sdk_version.go +++ b/android/sdk_version.go @@ -86,7 +86,7 @@ func (k SdkKind) String() string { // JavaLibraryName returns the soong module containing the Java APIs of that API surface. func (k SdkKind) JavaLibraryName(c Config) string { - name := k.defaultJavaLibraryName() + name := k.DefaultJavaLibraryName() return JavaApiLibraryName(c, name) } @@ -100,7 +100,7 @@ func JavaApiLibraryName(c Config, name string) string { return name } -func (k SdkKind) defaultJavaLibraryName() string { +func (k SdkKind) DefaultJavaLibraryName() string { switch k { case SdkPublic: return "android_stubs_current" diff --git a/android/variable.go b/android/variable.go index 1da5974ea..249d53b07 100644 --- a/android/variable.go +++ b/android/variable.go @@ -464,6 +464,8 @@ type productVariables struct { IncludeTags []string `json:",omitempty"` SourceRootDirs []string `json:",omitempty"` + + AfdoProfiles []string `json:",omitempty"` } func boolPtr(v bool) *bool { diff --git a/apex/apex.go b/apex/apex.go index 367863660..5451a0400 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1986,9 +1986,9 @@ func (a *apexBundle) ProcessBazelQueryResponse(ctx android.ModuleContext) { // Set the output file to .apex or .capex depending on the compression configuration. a.setCompression(ctx) if a.isCompressed { - a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedCompressedOutput) + a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedCompressedOutput) } else { - a.outputApexFile = android.PathForBazelOut(ctx, outputs.SignedOutput) + a.outputApexFile = android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), outputs.SignedOutput) } a.outputFile = a.outputApexFile diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 6edd78aef..598ca323d 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -61,6 +61,7 @@ bootstrap_go_package { "genrule_conversion_test.go", "gensrcs_conversion_test.go", "java_binary_host_conversion_test.go", + "java_host_for_device_conversion_test.go", "java_import_conversion_test.go", "java_library_conversion_test.go", "java_library_host_conversion_test.go", diff --git a/bp2build/java_host_for_device_conversion_test.go b/bp2build/java_host_for_device_conversion_test.go new file mode 100644 index 000000000..d908d0002 --- /dev/null +++ b/bp2build/java_host_for_device_conversion_test.go @@ -0,0 +1,63 @@ +// 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 bp2build + +import ( + "testing" + + "android/soong/android" + "android/soong/java" +) + +func runJavaHostForDeviceTestCaseWithRegistrationCtxFunc(t *testing.T, tc Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) { + t.Helper() + (&tc).ModuleTypeUnderTest = "java_host_for_device" + (&tc).ModuleTypeUnderTestFactory = java.HostForDeviceFactory + RunBp2BuildTestCase(t, registrationCtxFunc, tc) +} + +func runJavaHostForDeviceTestCase(t *testing.T, tc Bp2buildTestCase) { + t.Helper() + runJavaHostForDeviceTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("java_library", java.LibraryFactory) + }) +} + +func TestJavaHostForDevice(t *testing.T) { + runJavaHostForDeviceTestCase(t, Bp2buildTestCase{ + Description: "java_host_for_device test", + Blueprint: `java_host_for_device { + name: "java-lib-1", + libs: ["java-lib-2"], + bazel_module: { bp2build_available: true }, +} + +java_library { + name: "java-lib-2", + srcs: ["b.java"], + bazel_module: { bp2build_available: true }, +}`, + ExpectedBazelTargets: []string{ + MakeBazelTarget("java_host_for_device", "java-lib-1", AttrNameToString{ + "deps": `[":java-lib-2"]`, + }), + MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"), + MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{ + "srcs": `["b.java"]`, + }), + MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"), + }, + }) +} diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go index 69d0db91c..683ee27cc 100644 --- a/bp2build/java_library_conversion_test.go +++ b/bp2build/java_library_conversion_test.go @@ -740,7 +740,7 @@ func TestJavaLibraryKotlinCommonSrcs(t *testing.T) { }) } -func TestJavaLibraryArchVariantLibs(t *testing.T) { +func TestJavaLibraryArchVariantDeps(t *testing.T) { runJavaLibraryTestCase(t, Bp2buildTestCase{ Description: "java_library with arch variant libs", Blueprint: `java_library { @@ -750,6 +750,7 @@ func TestJavaLibraryArchVariantLibs(t *testing.T) { target: { android: { libs: ["java-lib-3"], + static_libs: ["java-lib-4"], }, }, bazel_module: { bp2build_available: true }, @@ -762,12 +763,23 @@ func TestJavaLibraryArchVariantLibs(t *testing.T) { java_library{ name: "java-lib-3", } + + java_library{ + name: "java-lib-4", +} `, ExpectedBazelTargets: []string{ MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{ "srcs": `["a.java"]`, + "exports": `select({ + "//build/bazel/platforms/os:android": [":java-lib-4"], + "//conditions:default": [], + })`, "deps": `[":java-lib-2-neverlink"] + select({ - "//build/bazel/platforms/os:android": [":java-lib-3-neverlink"], + "//build/bazel/platforms/os:android": [ + ":java-lib-3-neverlink", + ":java-lib-4", + ], "//conditions:default": [], })`, }), @@ -776,6 +788,8 @@ func TestJavaLibraryArchVariantLibs(t *testing.T) { MakeNeverlinkDuplicateTarget("java_library", "java-lib-2"), MakeBazelTarget("java_library", "java-lib-3", AttrNameToString{}), MakeNeverlinkDuplicateTarget("java_library", "java-lib-3"), + MakeBazelTarget("java_library", "java-lib-4", AttrNameToString{}), + MakeNeverlinkDuplicateTarget("java_library", "java-lib-4"), }, }) } diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go index aac5e7d2c..5c333085d 100644 --- a/bp2build/symlink_forest.go +++ b/bp2build/symlink_forest.go @@ -21,10 +21,13 @@ import ( "path/filepath" "regexp" "sort" + "strconv" + "strings" "sync" "sync/atomic" "android/soong/shared" + "github.com/google/blueprint/pathtools" ) @@ -35,6 +38,13 @@ import ( // excluded from symlinking. Otherwise, the node is not excluded, but one of its // descendants is (otherwise the node in question would not exist) +// This is a version int written to a file called symlink_forest_version at the root of the +// symlink forest. If the version here does not match the version in the file, then we'll +// clean the whole symlink forest and recreate it. This number can be bumped whenever there's +// an incompatible change to the forest layout or a bug in incrementality that needs to be fixed +// on machines that may still have the bug present in their forest. +const symlinkForestVersion = 1 + type instructionsNode struct { name string excluded bool // If false, this is just an intermediate node @@ -123,6 +133,34 @@ func mergeBuildFiles(output string, srcBuildFile string, generatedBuildFile stri } newContents = append(newContents, srcBuildFileContent...) + // Say you run bp2build 4 times: + // - The first time there's only an Android.bp file. bp2build will convert it to a build file + // under out/soong/bp2build, then symlink from the forest to that generated file + // - Then you add a handcrafted BUILD file in the same directory. bp2build will merge this with + // the generated one, and write the result to the output file in the forest. But the output + // file was a symlink to out/soong/bp2build from the previous step! So we erroneously update + // the file in out/soong/bp2build instead. So far this doesn't cause any problems... + // - You run a 3rd bp2build with no relevant changes. Everything continues to work. + // - You then add a comment to the handcrafted BUILD file. This causes a merge with the + // generated file again. But since we wrote to the generated file in step 2, the generated + // file has an old copy of the handcrafted file in it! This probably causes duplicate bazel + // targets. + // To solve this, if we see that the output file is a symlink from a previous build, remove it. + stat, err := os.Lstat(output) + if err != nil && !os.IsNotExist(err) { + return err + } else if err == nil { + if stat.Mode()&os.ModeSymlink == os.ModeSymlink { + if verbose { + fmt.Fprintf(os.Stderr, "Removing symlink so that we can replace it with a merged file: %s\n", output) + } + err = os.Remove(output) + if err != nil { + return err + } + } + } + return pathtools.WriteFileIfChanged(output, newContents, 0666) } @@ -202,6 +240,46 @@ func isDir(path string, fi os.FileInfo) bool { return false } +// maybeCleanSymlinkForest will remove the whole symlink forest directory if the version recorded +// in the symlink_forest_version file is not equal to symlinkForestVersion. +func maybeCleanSymlinkForest(topdir, forest string, verbose bool) error { + versionFilePath := shared.JoinPath(topdir, forest, "symlink_forest_version") + versionFileContents, err := os.ReadFile(versionFilePath) + if err != nil && !os.IsNotExist(err) { + return err + } + versionFileString := strings.TrimSpace(string(versionFileContents)) + symlinkForestVersionString := strconv.Itoa(symlinkForestVersion) + if err != nil || versionFileString != symlinkForestVersionString { + if verbose { + fmt.Fprintf(os.Stderr, "Old symlink_forest_version was %q, current is %q. Cleaning symlink forest before recreating...\n", versionFileString, symlinkForestVersionString) + } + err = os.RemoveAll(shared.JoinPath(topdir, forest)) + if err != nil { + return err + } + } + return nil +} + +// maybeWriteVersionFile will write the symlink_forest_version file containing symlinkForestVersion +// if it doesn't exist already. If it exists we know it must contain symlinkForestVersion because +// we checked for that already in maybeCleanSymlinkForest +func maybeWriteVersionFile(topdir, forest string) error { + versionFilePath := shared.JoinPath(topdir, forest, "symlink_forest_version") + _, err := os.Stat(versionFilePath) + if err != nil { + if !os.IsNotExist(err) { + return err + } + err = os.WriteFile(versionFilePath, []byte(strconv.Itoa(symlinkForestVersion)+"\n"), 0666) + if err != nil { + return err + } + } + return nil +} + // Recursively plants a symlink forest at forestDir. The symlink tree will // contain every file in buildFilesDir and srcDir excluding the files in // instructions. Collects every directory encountered during the traversal of @@ -395,6 +473,12 @@ func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles s symlinkCount: atomic.Uint64{}, } + err := maybeCleanSymlinkForest(topdir, forest, verbose) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + instructions := instructionsFromExcludePathList(exclude) go func() { context.wg.Add(1) @@ -407,5 +491,11 @@ func PlantSymlinkForest(verbose bool, topdir string, forest string, buildFiles s deps = append(deps, dep) } + err = maybeWriteVersionFile(topdir, forest) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + return deps, context.mkdirCount.Load(), context.symlinkCount.Load() } diff --git a/cc/Android.bp b/cc/Android.bp index 5fd9afea9..be2cc5a34 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -21,6 +21,8 @@ bootstrap_go_package { ], srcs: [ "afdo.go", + "fdo_profile.go", + "androidmk.go", "api_level.go", "bp2build.go", diff --git a/cc/afdo.go b/cc/afdo.go index d36f4afcf..be4f50adb 100644 --- a/cc/afdo.go +++ b/cc/afdo.go @@ -18,11 +18,13 @@ import ( "fmt" "strings" - "github.com/google/blueprint/proptools" - "android/soong/android" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) +// TODO(b/267229066): Remove globalAfdoProfileProjects after implementing bp2build converter for fdo_profile var ( globalAfdoProfileProjects = []string{ "vendor/google_data/pgo_profile/sampling/", @@ -34,23 +36,23 @@ var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects") const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s" -func getAfdoProfileProjects(config android.DeviceConfig) []string { - return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string { - return globalAfdoProfileProjects - }) -} - func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) { getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true) } +type afdoRdep struct { + VariationName *string + ProfilePath *string +} + type AfdoProperties struct { // Afdo allows developers self-service enroll for // automatic feedback-directed optimization using profile data. Afdo bool - AfdoTarget *string `blueprint:"mutated"` - AfdoDeps []string `blueprint:"mutated"` + FdoProfilePath *string `blueprint:"mutated"` + + AfdoRDeps []afdoRdep `blueprint:"mutated"` } type afdo struct { @@ -61,116 +63,131 @@ func (afdo *afdo) props() []interface{} { return []interface{}{&afdo.Properties} } -func (afdo *afdo) AfdoEnabled() bool { - return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil +// afdoEnabled returns true for binaries and shared libraries +// that set afdo prop to True and there is a profile available +func (afdo *afdo) afdoEnabled() bool { + return afdo != nil && afdo.Properties.Afdo && afdo.Properties.FdoProfilePath != nil } -// Get list of profile file names, ordered by level of specialisation. For example: -// 1. libfoo_arm64.afdo -// 2. libfoo.afdo -// -// Add more specialisation as needed. -func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string { - var files []string - files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo") - files = append(files, moduleName+".afdo") - return files -} - -func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath { - // Test if the profile_file is present in any of the Afdo profile projects - for _, profileFile := range getProfileFiles(ctx, module) { - for _, profileProject := range getAfdoProfileProjects(ctx.DeviceConfig()) { - path := android.ExistentPathForSource(ctx, profileProject, profileFile) - if path.Valid() { - return path - } - } +func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { + if path := afdo.Properties.FdoProfilePath; path != nil { + // The flags are prepended to allow overriding. + profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path) + flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...) + flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...) + + // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt + // if profileFile gets updated + pathForSrc := android.PathForSource(ctx, *path) + flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc) + flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc) } - // Record that this module's profile file is absent - missing := ctx.ModuleDir() + ":" + module - recordMissingAfdoProfileFile(ctx, missing) - - return android.OptionalPathForPath(nil) + return flags } -func (afdo *afdo) begin(ctx BaseModuleContext) { +func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) { if ctx.Host() { return } + if ctx.static() && !ctx.staticBinary() { return } - if afdo.Properties.Afdo { - module := ctx.ModuleName() - if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() { - afdo.Properties.AfdoTarget = proptools.StringPtr(module) + + if c, ok := ctx.Module().(*Module); ok && c.Enabled() { + if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil { + actx.AddFarVariationDependencies( + []blueprint.Variation{ + {Mutator: "arch", Variation: actx.Target().ArchVariation()}, + {Mutator: "os", Variation: "android"}, + }, + FdoProfileTag, + []string{*fdoProfileName}..., + ) } } } -func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { - if profile := afdo.Properties.AfdoTarget; profile != nil { - if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() { - profileFilePath := profileFile.Path() - - profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile) - flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag) - flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag) - flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true") - - // Update CFlagsDeps and LdFlagsDeps so the module is rebuilt - // if profileFile gets updated - flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath) - flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath) - } +// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag +// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property +func (c *Module) fdoProfileMutator(ctx android.BottomUpMutatorContext) { + if !c.Enabled() { + return } - return flags + ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) { + if ctx.OtherModuleHasProvider(m, FdoProfileProvider) { + info := ctx.OtherModuleProvider(m, FdoProfileProvider).(FdoProfileInfo) + c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String()) + } + }) } -// Propagate afdo requirements down from binaries +var _ FdoProfileMutatorInterface = (*Module)(nil) + +// Propagate afdo requirements down from binaries and shared libraries func afdoDepsMutator(mctx android.TopDownMutatorContext) { - if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() { - afdoTarget := *m.afdo.Properties.AfdoTarget - mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { - tag := mctx.OtherModuleDependencyTag(dep) - libTag, isLibTag := tag.(libraryDependencyTag) - - // Do not recurse down non-static dependencies - if isLibTag { - if !libTag.static() { - return false + if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() { + if path := m.afdo.Properties.FdoProfilePath; path != nil { + mctx.WalkDeps(func(dep android.Module, parent android.Module) bool { + tag := mctx.OtherModuleDependencyTag(dep) + libTag, isLibTag := tag.(libraryDependencyTag) + + // Do not recurse down non-static dependencies + if isLibTag { + if !libTag.static() { + return false + } + } else { + if tag != objDepTag && tag != reuseObjTag { + return false + } } - } else { - if tag != objDepTag && tag != reuseObjTag { - return false - } - } - if dep, ok := dep.(*Module); ok { - dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget) - } + if dep, ok := dep.(*Module); ok { + dep.afdo.Properties.AfdoRDeps = append( + dep.afdo.Properties.AfdoRDeps, + afdoRdep{ + VariationName: proptools.StringPtr(encodeTarget(m.Name())), + ProfilePath: path, + }, + ) + } - return true - }) + return true + }) + } } } // Create afdo variants for modules that need them func afdoMutator(mctx android.BottomUpMutatorContext) { if m, ok := mctx.Module().(*Module); ok && m.afdo != nil { - if m.afdo.AfdoEnabled() && !m.static() { - afdoTarget := *m.afdo.Properties.AfdoTarget - mctx.SetDependencyVariation(encodeTarget(afdoTarget)) + if !m.static() && m.afdo.Properties.Afdo && m.afdo.Properties.FdoProfilePath != nil { + mctx.SetDependencyVariation(encodeTarget(m.Name())) + return } variationNames := []string{""} - afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps) - for _, dep := range afdoDeps { - variationNames = append(variationNames, encodeTarget(dep)) + + variantNameToProfilePath := make(map[string]*string) + + for _, afdoRDep := range m.afdo.Properties.AfdoRDeps { + variantName := *afdoRDep.VariationName + // An rdep can be set twice in AfdoRDeps because there can be + // more than one path from an afdo-enabled module to + // a static dep such as + // afdo_enabled_foo -> static_bar ----> static_baz + // \ ^ + // ----------------------| + // We only need to create one variant per unique rdep + if variantNameToProfilePath[variantName] == nil { + variationNames = append(variationNames, variantName) + variantNameToProfilePath[variantName] = afdoRDep.ProfilePath + } } + if len(variationNames) > 1 { modules := mctx.CreateVariations(variationNames...) for i, name := range variationNames { @@ -180,7 +197,7 @@ func afdoMutator(mctx android.BottomUpMutatorContext) { variation := modules[i].(*Module) variation.Properties.PreventInstall = true variation.Properties.HideFromMake = true - variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name)) + variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name] } } } diff --git a/cc/afdo_test.go b/cc/afdo_test.go index 40f705b87..1c20bfc8c 100644 --- a/cc/afdo_test.go +++ b/cc/afdo_test.go @@ -58,38 +58,77 @@ func TestAfdoDeps(t *testing.T) { srcs: ["bar.c"], } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST") result := android.GroupFixturePreparers( + PrepareForTestWithFdoProfile, prepareForCcTest, - prepareForAfdoTest, + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) + expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" + libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") - libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") - libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") + libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") + libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest") + + // Check cFlags of afdo-enabled module and the afdo-variant of its static deps + cFlags := libTest.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } + + cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } - if !hasDirectDep(result, libTest.Module(), libFoo.Module()) { + cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"] + if !strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags) + } + + // Check dependency edge from afdo-enabled module to static deps + if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) { t.Errorf("libTest missing dependency on afdo variant of libFoo") } - if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { + if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) { t.Errorf("libTest missing dependency on afdo variant of libBar") } - cFlags := libTest.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", w, cFlags) - } + // Verify non-afdo variant exists and doesn't contain afdo + libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static") + libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static") cFlags = libFoo.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libFoo' to enable afdo, but did not find %q in cflags %q", w, cFlags) + if strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) } - cFlags = libBar.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-accurate"; !strings.Contains(cFlags, w) { - t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", w, cFlags) + if strings.Contains(cFlags, expectedCFlag) { + t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags) + } + + // Check dependency edges of static deps + if hasDirectDep(result, libTest.Module(), libFoo.Module()) { + t.Errorf("libTest should not depend on non-afdo variant of libFoo") + } + + if !hasDirectDep(result, libFoo.Module(), libBar.Module()) { + t.Errorf("libFoo missing dependency on non-afdo variant of libBar") } } @@ -113,11 +152,21 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) { name: "libBar", } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", "TEST") result := android.GroupFixturePreparers( prepareForCcTest, - prepareForAfdoTest, + PrepareForTestWithFdoProfile, + android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libFoo.afdo", ""), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + soong_namespace { + } + fdo_profile { + name: "libFoo_afdo", + profile: "libFoo.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module() @@ -150,7 +199,6 @@ func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) { t.Errorf("Expected no afdo variant of 'bar', got %q", v) } } - } func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { @@ -166,11 +214,24 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { name: "libFoo", } ` - prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST") result := android.GroupFixturePreparers( prepareForCcTest, - prepareForAfdoTest, + PrepareForTestWithFdoProfile, + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) libFooVariants := result.ModuleVariantsForTests("libFoo") @@ -182,7 +243,6 @@ func TestAfdoEnabledWithRuntimeDepNoAfdo(t *testing.T) { } func TestAfdoEnabledWithMultiArchs(t *testing.T) { - t.Parallel() bp := ` cc_library_shared { name: "foo", @@ -192,20 +252,43 @@ func TestAfdoEnabledWithMultiArchs(t *testing.T) { } ` result := android.GroupFixturePreparers( + PrepareForTestWithFdoProfile, prepareForCcTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm.afdo", "TEST"), - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm64.afdo", "TEST"), + android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""), + android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "foo://afdo_profiles_package:foo_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + soong_namespace { + } + fdo_profile { + name: "foo_afdo", + arch: { + arm: { + profile: "foo_arm.afdo", + }, + arm64: { + profile: "foo_arm64.afdo", + } + } + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon_shared") fooArmCFlags := fooArm.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { + if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm.afdo"; !strings.Contains(fooArmCFlags, w) { t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArmCFlags) } fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a_shared") fooArm64CFlags := fooArm64.Rule("cc").Args["cFlags"] - if w := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { + if w := "-fprofile-sample-use=afdo_profiles_package/foo_arm64.afdo"; !strings.Contains(fooArm64CFlags, w) { t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", w, fooArm64CFlags) } } @@ -234,46 +317,65 @@ func TestMultipleAfdoRDeps(t *testing.T) { ` result := android.GroupFixturePreparers( + PrepareForTestWithFdoProfile, prepareForCcTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST"), - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libBar.afdo", "TEST"), + android.FixtureAddTextFile("afdo_profiles_package/libTest.afdo", ""), + android.FixtureAddTextFile("afdo_profiles_package/libBar.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "libTest://afdo_profiles_package:libTest_afdo", + "libBar://afdo_profiles_package:libBar_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "libTest_afdo", + profile: "libTest.afdo", + } + fdo_profile { + name: "libBar_afdo", + profile: "libBar.afdo", + } + `), + }.AddToFixture(), ).RunTestWithBp(t, bp) - expectedCFlagLibTest := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libTest.afdo" - expectedCFlagLibBar := "-fprofile-sample-use=toolchain/pgo-profiles/sampling/libBar.afdo" + expectedCFlagLibTest := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo" + expectedCFlagLibBar := "-fprofile-sample-use=afdo_profiles_package/libBar.afdo" libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared") - libTestAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") + libFooAfdoVariantWithLibTest := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest") libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_shared") - libBarAfdoVariantOfLibFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") + libFooAfdoVariantWithLibBar := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libBar") - // Check cFlags of afdo-enabled modules and the afdo-variant of their static deps + // Check cFlags of afdo-enabled module and the afdo-variant of its static deps cFlags := libTest.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibTest) { t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) } cFlags = libBar.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibBar) { - t.Errorf("Expected 'libBar' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) + t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) } - cFlags = libTestAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] + cFlags = libFooAfdoVariantWithLibTest.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibTest) { - t.Errorf("Expected 'libTestAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) + t.Errorf("Expected 'libFooAfdoVariantWithLibTest' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibTest, cFlags) } - cFlags = libBarAfdoVariantOfLibFoo.Rule("cc").Args["cFlags"] + cFlags = libFooAfdoVariantWithLibBar.Rule("cc").Args["cFlags"] if !strings.Contains(cFlags, expectedCFlagLibBar) { - t.Errorf("Expected 'libBarAfdoVariantOfLibFoo' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) + t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlagLibBar, cFlags) } // Check dependency edges of static deps - if !hasDirectDep(result, libTest.Module(), libTestAfdoVariantOfLibFoo.Module()) { + if !hasDirectDep(result, libTest.Module(), libFooAfdoVariantWithLibTest.Module()) { t.Errorf("libTest missing dependency on afdo variant of libFoo") } - if !hasDirectDep(result, libBar.Module(), libBarAfdoVariantOfLibFoo.Module()) { - t.Errorf("libBar missing dependency on afdo variant of libFoo") + if !hasDirectDep(result, libBar.Module(), libFooAfdoVariantWithLibBar.Module()) { + t.Errorf("libFoo missing dependency on non-afdo variant of libBar") } } diff --git a/cc/bp2build.go b/cc/bp2build.go index 7c817a2b1..c8f516cdc 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -915,7 +915,7 @@ func bp2buildCcAidlLibrary( ctx.CreateBazelTargetModule( bazel.BazelTargetModuleProperties{ Rule_class: "aidl_library", - Bzl_load_location: "//build/bazel/rules/aidl:library.bzl", + Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl", }, android.CommonAttributes{Name: aidlLibName}, &aidlLibraryAttributes{ @@ -52,6 +52,7 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel() ctx.BottomUp("version", versionMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel() + ctx.BottomUp("fdo_profile", fdoProfileMutator) }) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { @@ -763,6 +764,7 @@ var ( testPerSrcDepTag = dependencyTag{name: "test_per_src"} stubImplDepTag = dependencyTag{name: "stub_impl"} JniFuzzLibTag = dependencyTag{name: "jni_fuzz_lib_tag"} + FdoProfileTag = dependencyTag{name: "fdo_profile"} ) func IsSharedDepTag(depTag blueprint.DependencyTag) bool { @@ -1336,7 +1338,7 @@ func (c *Module) IsVndk() bool { func (c *Module) isAfdoCompile() bool { if afdo := c.afdo; afdo != nil { - return afdo.Properties.AfdoTarget != nil + return afdo.Properties.FdoProfilePath != nil } return false } @@ -2162,9 +2164,6 @@ func (c *Module) begin(ctx BaseModuleContext) { if c.lto != nil { c.lto.begin(ctx) } - if c.afdo != nil { - c.afdo.begin(ctx) - } if c.pgo != nil { c.pgo.begin(ctx) } @@ -2239,6 +2238,10 @@ func (c *Module) beginMutator(actx android.BottomUpMutatorContext) { } ctx.ctx = ctx + if !actx.Host() || !ctx.static() || ctx.staticBinary() { + c.afdo.addDep(ctx, actx) + } + c.begin(ctx) } diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go new file mode 100644 index 000000000..7fbe71940 --- /dev/null +++ b/cc/fdo_profile.go @@ -0,0 +1,85 @@ +// 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 cc + +import ( + "android/soong/android" + + "github.com/google/blueprint" +) + +func init() { + RegisterFdoProfileBuildComponents(android.InitRegistrationContext) +} + +func RegisterFdoProfileBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("fdo_profile", fdoProfileFactory) +} + +type fdoProfile struct { + android.ModuleBase + + properties fdoProfileProperties +} + +type fdoProfileProperties struct { + Profile *string `android:"arch_variant"` +} + +// FdoProfileInfo is provided by FdoProfileProvider +type FdoProfileInfo struct { + Path android.Path +} + +// FdoProfileProvider is used to provide path to an fdo profile +var FdoProfileProvider = blueprint.NewMutatorProvider(FdoProfileInfo{}, "fdo_profile") + +// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type +// module types that can depend on an fdo_profile module +type FdoProfileMutatorInterface interface { + // FdoProfileMutator eithers set or get FdoProfileProvider + fdoProfileMutator(ctx android.BottomUpMutatorContext) +} + +var _ FdoProfileMutatorInterface = (*fdoProfile)(nil) + +// GenerateAndroidBuildActions of fdo_profile does not have any build actions +func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {} + +// FdoProfileMutator sets FdoProfileProvider to fdo_profile module +// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile +func (fp *fdoProfile) fdoProfileMutator(ctx android.BottomUpMutatorContext) { + if fp.properties.Profile != nil { + path := android.PathForModuleSrc(ctx, *fp.properties.Profile) + ctx.SetProvider(FdoProfileProvider, FdoProfileInfo{ + Path: path, + }) + } +} + +// fdoProfileMutator calls the generic fdoProfileMutator function of fdoProfileMutator +// which is implemented by cc and cc.FdoProfile +func fdoProfileMutator(ctx android.BottomUpMutatorContext) { + if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok { + f.fdoProfileMutator(ctx) + } +} + +func fdoProfileFactory() android.Module { + m := &fdoProfile{} + m.AddProperties(&m.properties) + android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibBoth) + return m +} diff --git a/cc/library.go b/cc/library.go index a9ada97d9..7504302fd 100644 --- a/cc/library.go +++ b/cc/library.go @@ -26,7 +26,6 @@ import ( "android/soong/android" "android/soong/bazel" "android/soong/bazel/cquery" - "android/soong/cc/config" "github.com/google/blueprint" "github.com/google/blueprint/pathtools" @@ -2261,8 +2260,7 @@ func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) { !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() && library.baseLinker.sanitize.isUnsanitizedVariant() && ctx.isForPlatform() && !ctx.isPreventInstall() { - installPath := getNdkSysrootBase(ctx).Join( - ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base()) + installPath := getUnversionedLibraryInstallPath(ctx).Join(ctx, file.Base()) ctx.ModuleBuild(pctx, android.ModuleBuildParams{ Rule: android.Cp, diff --git a/cc/ndk_library.go b/cc/ndk_library.go index 2473ba2a3..a82436135 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -528,17 +528,20 @@ func (stub *stubDecorator) nativeCoverage() bool { return false } -func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { - arch := ctx.Target().Arch.ArchType.Name - // arm64 isn't actually a multilib toolchain, so unlike the other LP64 - // architectures it's just installed to lib. - libDir := "lib" - if ctx.toolchain().Is64Bit() && arch != "arm64" { - libDir = "lib64" - } +// Returns the install path for unversioned NDK libraries (currently only static +// libraries). +func getUnversionedLibraryInstallPath(ctx ModuleContext) android.InstallPath { + return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain())) +} - installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf( - "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir)) +// Returns the install path for versioned NDK libraries. These are most often +// stubs, but the same paths are used for CRT objects. +func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.InstallPath { + return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String()) +} + +func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { + installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel) stub.installPath = ctx.InstallFile(installDir, path.Base(), path) } diff --git a/cc/testing.go b/cc/testing.go index f78ea0f80..ced09290f 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -670,6 +670,12 @@ var PrepareForTestWithHostMusl = android.GroupFixturePreparers( `), ) +// PrepareForTestWithFdoProfile registers module types to test with fdo_profile +var PrepareForTestWithFdoProfile = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { + ctx.RegisterModuleType("soong_namespace", android.NamespaceFactory) + ctx.RegisterModuleType("fdo_profile", fdoProfileFactory) +}) + // TestConfig is the legacy way of creating a test Config for testing cc modules. // // See testCc for an explanation as to how to stop using this deprecated method. diff --git a/java/base.go b/java/base.go index 1bcff2ed5..991132321 100644 --- a/java/base.go +++ b/java/base.go @@ -1923,7 +1923,7 @@ type moduleWithSdkDep interface { func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) { switch name { - case "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", + case android.SdkCore.JavaLibraryName(ctx.Config()), "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", "stub-annotations", "private-stub-annotations-jar", "core-lambda-stubs", "core-generated-annotation-stubs": return javaCore, true diff --git a/java/config/config.go b/java/config/config.go index 13670ee38..b82a137e7 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -95,11 +95,13 @@ func init() { "-JXX:TieredStopAtLevel=1", "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", + "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex", }, dexerJavaVmFlagsList...)) exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{ "-JXmx2048M", "-JDcom.android.tools.r8.emitRecordAnnotationsInDex", "-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex", + "-JDcom.android.tools.r8.emitRecordAnnotationsExInDex", }, dexerJavaVmFlagsList...)) exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{ diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp index b9332dda5..958f4cead 100644 --- a/java/core-libraries/Android.bp +++ b/java/core-libraries/Android.bp @@ -79,16 +79,25 @@ java_library { ], } +// Defaults module to strip out android annotations +java_defaults { + name: "system-modules-no-annotations", + sdk_version: "none", + system_modules: "none", + jarjar_rules: "jarjar-strip-annotations-rules.txt", +} + // Same as core-current-stubs-for-system-modules, but android annotations are // stripped. java_library { name: "core-current-stubs-for-system-modules-no-annotations", visibility: ["//development/sdk"], + defaults: [ + "system-modules-no-annotations", + ], static_libs: [ "core-current-stubs-for-system-modules", ], - sdk_version: "none", - system_modules: "none", dists: [ { // Legacy dist location for the public file. @@ -100,7 +109,6 @@ java_library { targets: dist_targets, }, ], - jarjar_rules: "jarjar-strip-annotations-rules.txt", } // Used when compiling higher-level code against core.current.stubs. @@ -158,16 +166,16 @@ java_library { java_library { name: "core-module-lib-stubs-for-system-modules-no-annotations", visibility: ["//visibility:private"], + defaults: [ + "system-modules-no-annotations", + ], static_libs: [ "core-module-lib-stubs-for-system-modules", ], - sdk_version: "none", - system_modules: "none", dist: { dest: "system-modules/module-lib/core-for-system-modules-no-annotations.jar", targets: dist_targets, }, - jarjar_rules: "jarjar-strip-annotations-rules.txt", } // Used when compiling higher-level code with sdk_version "module_current" @@ -212,16 +220,16 @@ java_library { java_library { name: "legacy.core.platform.api.no.annotations.stubs", visibility: core_platform_visibility, + defaults: [ + "system-modules-no-annotations", + ], hostdex: true, compile_dex: true, - sdk_version: "none", - system_modules: "none", static_libs: [ "legacy.core.platform.api.stubs", ], patch_module: "java.base", - jarjar_rules: "jarjar-strip-annotations-rules.txt", } java_library { @@ -247,16 +255,16 @@ java_library { java_library { name: "stable.core.platform.api.no.annotations.stubs", visibility: core_platform_visibility, + defaults: [ + "system-modules-no-annotations", + ], hostdex: true, compile_dex: true, - sdk_version: "none", - system_modules: "none", static_libs: [ "stable.core.platform.api.stubs", ], patch_module: "java.base", - jarjar_rules: "jarjar-strip-annotations-rules.txt", } // Used when compiling higher-level code against *.core.platform.api.stubs. @@ -307,12 +315,6 @@ java_system_modules { // the UnsupportedAppUsage, CorePlatformApi and IntraCoreApi // annotations. "art.module.api.annotations.for.system.modules", - - // Make nullability annotations available when compiling public stubs. - // They are provided as a separate library because while the - // annotations are not themselves part of the public API provided by - // this module they are used in the stubs. - "stub-annotations", ], } @@ -349,3 +351,7 @@ java_system_modules { "art-module-intra-core-api-stubs-system-modules-lib", ], } + +build = [ + "TxtStubLibraries.bp", +] diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp new file mode 100644 index 000000000..813187e54 --- /dev/null +++ b/java/core-libraries/TxtStubLibraries.bp @@ -0,0 +1,156 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains java_system_modules provided by the SDK. +// These system modules transitively depend on core stub libraries generated from .txt files. + +// Same as core-public-stubs-system-modules, but the stubs are generated from .txt files +java_system_modules { + name: "core-public-stubs-system-modules.from-text", + visibility: ["//visibility:public"], + libs: [ + "core-current-stubs-for-system-modules-no-annotations.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_library { + name: "core-current-stubs-for-system-modules-no-annotations.from-text", + visibility: ["//visibility:private"], + defaults: [ + "system-modules-no-annotations", + ], + static_libs: [ + "core.current.stubs.from-text", + "core-lambda-stubs.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +// Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files +java_system_modules { + name: "core-module-lib-stubs-system-modules.from-text", + visibility: ["//visibility:public"], + libs: [ + "core-module-lib-stubs-for-system-modules-no-annotations.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_library { + name: "core-module-lib-stubs-for-system-modules-no-annotations.from-text", + visibility: ["//visibility:private"], + defaults: [ + "system-modules-no-annotations", + ], + static_libs: [ + "core.module_lib.stubs.from-text", + "core-lambda-stubs.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_library { + name: "core.module_lib.stubs.from-text", + static_libs: [ + "art.module.public.api.stubs.module_lib.from-text", + + // Replace the following with the module-lib correspondence when Conscrypt or i18N module + // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides + // @SystemApi(MODULE_LIBRARIES). + "conscrypt.module.public.api.stubs.from-text", + "i18n.module.public.api.stubs.from-text", + ], + sdk_version: "none", + system_modules: "none", + visibility: ["//visibility:private"], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +// Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files +java_system_modules { + name: "legacy-core-platform-api-stubs-system-modules.from-text", + visibility: core_platform_visibility, + libs: [ + "legacy.core.platform.api.no.annotations.stubs.from-text", + "core-lambda-stubs.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_library { + name: "legacy.core.platform.api.no.annotations.stubs.from-text", + visibility: core_platform_visibility, + defaults: [ + "system-modules-no-annotations", + ], + hostdex: true, + compile_dex: true, + + static_libs: [ + "legacy.core.platform.api.stubs.from-text", + ], + patch_module: "java.base", + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +// Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files +java_system_modules { + name: "stable-core-platform-api-stubs-system-modules.from-text", + visibility: core_platform_visibility, + libs: [ + "stable.core.platform.api.no.annotations.stubs.from-text", + "core-lambda-stubs.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_library { + name: "stable.core.platform.api.no.annotations.stubs.from-text", + visibility: core_platform_visibility, + defaults: [ + "system-modules-no-annotations", + ], + hostdex: true, + compile_dex: true, + + static_libs: [ + "stable.core.platform.api.stubs.from-text", + ], + patch_module: "java.base", + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} + +java_api_library { + name: "core-lambda-stubs.from-text", + api_surface: "toolchain", + api_contributions: [ + "art.module.toolchain.api.api.contribution", + ], + libs: [ + // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface + "core.current.stubs.from-text", + ], + // TODO: Enable after stub generation from .txt file is available + enabled: false, +} diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 4abdcc6e9..656c866ed 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -19,12 +19,14 @@ import ( "io" "android/soong/android" + "android/soong/bazel" "android/soong/dexpreopt" ) type DeviceHostConverter struct { android.ModuleBase android.DefaultableModuleBase + android.BazelModuleBase properties DeviceHostConverterProperties @@ -76,6 +78,7 @@ func HostForDeviceFactory() android.Module { module.AddProperties(&module.properties) InitJavaModule(module, android.DeviceSupported) + android.InitBazelModule(module) return module } @@ -186,3 +189,30 @@ func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData { }, } } + +type bazelDeviceHostConverterAttributes struct { + Deps bazel.LabelListAttribute +} + +func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + ctx.CreateBazelTargetModule( + bazel.BazelTargetModuleProperties{ + Rule_class: "java_host_for_device", + Bzl_load_location: "//build/bazel/rules/java:host_for_device.bzl", + }, + android.CommonAttributes{Name: d.Name()}, + &bazelDeviceHostConverterAttributes{ + Deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, d.properties.Libs)), + }, + ) + neverlinkProp := true + neverLinkAttrs := &javaLibraryAttributes{ + Exports: bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + d.Name()}), + Neverlink: bazel.BoolAttribute{Value: &neverlinkProp}, + } + ctx.CreateBazelTargetModule( + javaLibraryBazelTargetModuleProperties(), + android.CommonAttributes{Name: d.Name() + "-neverlink"}, + neverLinkAttrs) + +} diff --git a/java/java.go b/java/java.go index 499a6b6cd..d400b0cfb 100644 --- a/java/java.go +++ b/java/java.go @@ -388,6 +388,8 @@ var ( jniLibTag = dependencyTag{name: "jnilib", runtimeLinked: true} r8LibraryJarTag = dependencyTag{name: "r8-libraryjar", runtimeLinked: true} syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} + javaApiContributionTag = dependencyTag{name: "java-api-contribution"} + depApiSrcsTag = dependencyTag{name: "dep-api-srcs"} jniInstallTag = installDependencyTag{name: "jni install"} binaryInstallTag = installDependencyTag{name: "binary install"} usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false) @@ -1609,6 +1611,13 @@ func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleCon }) } +type JavaApiLibraryDepsInfo struct { + JavaInfo + StubsSrcJar android.Path +} + +var JavaApiLibraryDepsProvider = blueprint.NewProvider(JavaApiLibraryDepsInfo{}) + type ApiLibrary struct { android.ModuleBase android.DefaultableModuleBase @@ -1618,8 +1627,10 @@ type ApiLibrary struct { properties JavaApiLibraryProperties - stubsSrcJar android.WritablePath - stubsJar android.WritablePath + stubsSrcJar android.WritablePath + stubsJar android.WritablePath + stubsJarWithoutStaticLibs android.WritablePath + extractedSrcJar android.WritablePath // .dex of stubs, used for hiddenapi processing dexJarFile OptionalDexJarPath } @@ -1645,8 +1656,13 @@ type JavaApiLibraryProperties struct { Libs []string // List of java libs that this module has static dependencies to and will be - // passed in metalava invocation + // merge zipped after metalava invocation Static_libs []string + + // Java Api library to provide the full API surface text files and jar file. + // If this property is set, the provided full API surface text files and + // jar file are passed to metalava invocation. + Dep_api_srcs *string } func ApiLibraryFactory() android.Module { @@ -1725,7 +1741,36 @@ func (al *ApiLibrary) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBui } } -var javaApiContributionTag = dependencyTag{name: "java-api-contribution"} +// This method extracts the stub java files from the srcjar file provided from dep_api_srcs module +// and replaces the java stubs generated by invoking metalava in this module. +// This method is used because metalava can generate compilable from-text stubs only when +// the codebase encompasses all classes listed in the input API text file, but a class can extend +// a class that is not within the same API domain. +func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, depApiSrcsSrcJar android.Path) { + generatedStubsList := android.PathForModuleOut(ctx, "metalava", "sources.txt") + unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir") + + rule.Command(). + BuiltTool("list_files"). + Text(stubsDir.String()). + FlagWithOutput("--out ", generatedStubsList). + FlagWithArg("--extensions ", ".java"). + FlagWithArg("--root ", unzippedSrcJarDir.String()) + + rule.Command(). + Text("unzip"). + Flag("-q"). + Input(depApiSrcsSrcJar). + FlagWithArg("-d ", unzippedSrcJarDir.String()) + + rule.Command(). + BuiltTool("soong_zip"). + Flag("-srcjar"). + Flag("-write_if_changed"). + FlagWithArg("-C ", unzippedSrcJarDir.String()). + FlagWithInput("-l ", generatedStubsList). + FlagWithOutput("-o ", al.stubsSrcJar) +} func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { apiContributions := al.properties.Api_contributions @@ -1734,6 +1779,9 @@ func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { } ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...) ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...) + if al.properties.Dep_api_srcs != nil { + ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Dep_api_srcs)) + } } func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -1754,6 +1802,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { var srcFiles android.Paths var classPaths android.Paths var staticLibs android.Paths + var depApiSrcsStubsSrcJar android.Path ctx.VisitDirectDeps(func(dep android.Module) { tag := ctx.OtherModuleDependencyTag(dep) switch tag { @@ -1770,6 +1819,10 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { case staticLibTag: provider := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) staticLibs = append(staticLibs, provider.HeaderJars...) + case depApiSrcsTag: + provider := ctx.OtherModuleProvider(dep, JavaApiLibraryDepsProvider).(JavaApiLibraryDepsInfo) + classPaths = append(classPaths, provider.HeaderJars...) + depApiSrcsStubsSrcJar = provider.StubsSrcJar } }) @@ -1780,21 +1833,31 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { srcFiles = append(srcFiles, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), api)) } + if srcFiles == nil { + ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName()) + } + cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir) al.stubsFlags(ctx, cmd, stubsDir) al.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar") - rule.Command(). - BuiltTool("soong_zip"). - Flag("-write_if_changed"). - Flag("-jar"). - FlagWithOutput("-o ", al.stubsSrcJar). - FlagWithArg("-C ", stubsDir.String()). - FlagWithArg("-D ", stubsDir.String()) + + if depApiSrcsStubsSrcJar != nil { + al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsSrcJar) + } else { + rule.Command(). + BuiltTool("soong_zip"). + Flag("-write_if_changed"). + Flag("-jar"). + FlagWithOutput("-o ", al.stubsSrcJar). + FlagWithArg("-C ", stubsDir.String()). + FlagWithArg("-D ", stubsDir.String()) + } rule.Build("metalava", "metalava merged") - compiledStubs := android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar") + + al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, ctx.ModuleName(), "stubs.jar") al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName())) var flags javaBuilderFlags @@ -1802,14 +1865,14 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { flags.javacFlags = strings.Join(al.properties.Javacflags, " ") flags.classpath = classpath(classPaths) - TransformJavaToClasses(ctx, compiledStubs, 0, android.Paths{}, + TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{}, android.Paths{al.stubsSrcJar}, flags, android.Paths{}) builder := android.NewRuleBuilder(pctx, ctx) builder.Command(). BuiltTool("merge_zips"). Output(al.stubsJar). - Inputs(android.Paths{compiledStubs}). + Inputs(android.Paths{al.stubsJarWithoutStaticLibs}). Inputs(staticLibs) builder.Build("merge_zips", "merge jar files") @@ -1835,6 +1898,13 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { ImplementationJars: android.PathsIfNonNil(al.stubsJar), AidlIncludeDirs: android.Paths{}, }) + + ctx.SetProvider(JavaApiLibraryDepsProvider, JavaApiLibraryDepsInfo{ + JavaInfo: JavaInfo{ + HeaderJars: android.PathsIfNonNil(al.stubsJar), + }, + StubsSrcJar: al.stubsSrcJar, + }) } func (al *ApiLibrary) DexJarBuildPath() OptionalDexJarPath { @@ -2772,7 +2842,7 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) ctx.CreateBazelTargetModule( bazel.BazelTargetModuleProperties{ Rule_class: "aidl_library", - Bzl_load_location: "//build/bazel/rules/aidl:library.bzl", + Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl", }, android.CommonAttributes{Name: aidlLibName}, &aidlLibraryAttributes{ @@ -2787,7 +2857,7 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) ctx.CreateBazelTargetModule( bazel.BazelTargetModuleProperties{ Rule_class: "java_aidl_library", - Bzl_load_location: "//build/bazel/rules/java:aidl_library.bzl", + Bzl_load_location: "//build/bazel/rules/java:java_aidl_library.bzl", }, android.CommonAttributes{Name: javaAidlLibName}, &javaAidlLibraryAttributes{ @@ -2837,10 +2907,6 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) } } - if m.properties.Static_libs != nil { - staticDeps.Append(android.BazelLabelForModuleDeps(ctx, android.LastUniqueStrings(android.CopyOf(m.properties.Static_libs)))) - } - protoDepLabel := bp2buildProto(ctx, &m.Module, srcPartitions[protoSrcPartition]) // Soong does not differentiate between a java_library and the Bazel equivalent of // a java_proto_library + proto_library pair. Instead, in Soong proto sources are @@ -2852,7 +2918,18 @@ func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) depLabels := &javaDependencyLabels{} depLabels.Deps = deps - depLabels.StaticDeps = bazel.MakeLabelListAttribute(staticDeps) + + for axis, configToProps := range archVariantProps { + for config, _props := range configToProps { + if archProps, ok := _props.(*CommonProperties); ok { + archStaticLibs := android.BazelLabelForModuleDeps( + ctx, + android.LastUniqueStrings(android.CopyOf(archProps.Static_libs))) + depLabels.StaticDeps.SetSelectValue(axis, config, archStaticLibs) + } + } + } + depLabels.StaticDeps.Value.Append(staticDeps) hasKotlin := !kotlinSrcs.IsEmpty() commonAttrs.kotlinAttributes = &kotlinAttributes{ diff --git a/java/java_test.go b/java/java_test.go index 68b749b6d..553b762ee 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -2209,6 +2209,50 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { } } +func TestJavaApiLibraryDepApiSrcs(t *testing.T) { + provider_bp_a := ` + java_api_contribution { + name: "foo1", + api_file: "foo1.txt", + } + ` + provider_bp_b := ` + java_api_contribution { + name: "foo2", + api_file: "foo2.txt", + } + ` + lib_bp_a := ` + java_api_library { + name: "lib1", + api_surface: "public", + api_contributions: ["foo1", "foo2"], + } + ` + + ctx, _ := testJavaWithFS(t, ` + java_api_library { + name: "bar1", + api_surface: "public", + api_contributions: ["foo1"], + dep_api_srcs: "lib1", + } + `, + map[string][]byte{ + "a/Android.bp": []byte(provider_bp_a), + "b/Android.bp": []byte(provider_bp_b), + "c/Android.bp": []byte(lib_bp_a), + }) + + m := ctx.ModuleForTests("bar1", "android_common") + manifest := m.Output("metalava.sbox.textproto") + sboxProto := android.RuleBuilderSboxProtoForTests(t, manifest) + manifestCommand := sboxProto.Commands[0].GetCommand() + + android.AssertStringDoesContain(t, "Command expected to contain module srcjar file", manifestCommand, "bar1-stubs.srcjar") + android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt") +} + func TestTradefedOptions(t *testing.T) { result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, ` java_test_host { diff --git a/java/sdk.go b/java/sdk.go index 72a50067c..8b4918add 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -148,10 +148,11 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) toModule := func(module string, aidl android.Path) sdkDep { // Select the kind of system modules needed for the sdk version. systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel) + systemModules := android.JavaApiLibraryName(ctx.Config(), fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind)) return sdkDep{ useModule: true, - bootclasspath: []string{module, config.DefaultLambdaStubsLibrary}, - systemModules: fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind), + bootclasspath: []string{module, android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)}, + systemModules: systemModules, java9Classpath: []string{module}, frameworkResModule: "framework-res", aidl: android.OptionalPathForPath(aidl), @@ -196,8 +197,8 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) case android.SdkCore: return sdkDep{ useModule: true, - bootclasspath: []string{"core.current.stubs", config.DefaultLambdaStubsLibrary}, - systemModules: "core-public-stubs-system-modules", + bootclasspath: []string{android.SdkCore.JavaLibraryName(ctx.Config()), android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)}, + systemModules: android.JavaApiLibraryName(ctx.Config(), "core-public-stubs-system-modules"), noFrameworksLibs: true, } case android.SdkModule: diff --git a/java/testing.go b/java/testing.go index 63d7dba69..0764d264a 100644 --- a/java/testing.go +++ b/java/testing.go @@ -368,6 +368,7 @@ func gatherRequiredDepsForTest() string { "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", + "kotlin-stdlib", "kotlin-stdlib-jdk7", "kotlin-stdlib-jdk8", @@ -387,6 +388,27 @@ func gatherRequiredDepsForTest() string { `, extra) } + extraApiLibraryModules := map[string]string{ + "android_stubs_current.from-text": "api/current.txt", + "android_system_stubs_current.from-text": "api/system-current.txt", + "android_test_stubs_current.from-text": "api/test-current.txt", + "android_module_lib_stubs_current.from-text": "api/module-lib-current.txt", + "android_system_server_stubs_current.from-text": "api/system-server-current.txt", + "core.current.stubs.from-text": "api/current.txt", + "legacy.core.platform.api.stubs.from-text": "api/current.txt", + "stable.core.platform.api.stubs.from-text": "api/current.txt", + "core-lambda-stubs.from-text": "api/current.txt", + } + + for libName, apiFile := range extraApiLibraryModules { + bp += fmt.Sprintf(` + java_api_library { + name: "%s", + api_files: ["%s"], + } + `, libName, apiFile) + } + bp += ` java_library { name: "framework", @@ -409,6 +431,10 @@ func gatherRequiredDepsForTest() string { "core-module-lib-stubs-system-modules", "legacy-core-platform-api-stubs-system-modules", "stable-core-platform-api-stubs-system-modules", + "core-public-stubs-system-modules.from-text", + "core-module-lib-stubs-system-modules.from-text", + "legacy-core-platform-api-stubs-system-modules.from-text", + "stable-core-platform-api-stubs-system-modules.from-text", } for _, extra := range systemModules { diff --git a/rust/afdo.go b/rust/afdo.go index 996fd7e0c..3534ee6e4 100644 --- a/rust/afdo.go +++ b/rust/afdo.go @@ -17,7 +17,10 @@ package rust import ( "fmt" + "android/soong/android" "android/soong/cc" + + "github.com/google/blueprint" ) const afdoFlagFormat = "-Zprofile-sample-use=%s" @@ -30,19 +33,49 @@ func (afdo *afdo) props() []interface{} { return []interface{}{&afdo.Properties} } -func (afdo *afdo) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { +func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) { + // afdo is not supported outside of Android + if ctx.Host() { + return + } + + if mod, ok := ctx.Module().(*Module); ok && mod.Enabled() { + fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()) + if err != nil { + ctx.ModuleErrorf("%s", err.Error()) + } + if fdoProfileName != nil { + actx.AddFarVariationDependencies( + []blueprint.Variation{ + {Mutator: "arch", Variation: actx.Target().ArchVariation()}, + {Mutator: "os", Variation: "android"}, + }, + cc.FdoProfileTag, + []string{*fdoProfileName}..., + ) + } + } +} + +func (afdo *afdo) flags(ctx android.ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) { if ctx.Host() { return flags, deps } - if afdo != nil && afdo.Properties.Afdo { - if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, ctx.ModuleName()); profileFile.Valid() { - profileUseFlag := fmt.Sprintf(afdoFlagFormat, profileFile) + if !afdo.Properties.Afdo { + return flags, deps + } + + ctx.VisitDirectDepsWithTag(cc.FdoProfileTag, func(m android.Module) { + if ctx.OtherModuleHasProvider(m, cc.FdoProfileProvider) { + info := ctx.OtherModuleProvider(m, cc.FdoProfileProvider).(cc.FdoProfileInfo) + path := info.Path + profileUseFlag := fmt.Sprintf(afdoFlagFormat, path.String()) flags.RustFlags = append(flags.RustFlags, profileUseFlag) - profileFilePath := profileFile.Path() - deps.AfdoProfiles = append(deps.AfdoProfiles, profileFilePath) + deps.AfdoProfiles = append(deps.AfdoProfiles, path) } - } + }) + return flags, deps } diff --git a/rust/afdo_test.go b/rust/afdo_test.go index fa20eef26..0cdf70491 100644 --- a/rust/afdo_test.go +++ b/rust/afdo_test.go @@ -16,6 +16,7 @@ package rust import ( "android/soong/android" + "android/soong/cc" "fmt" "strings" "testing" @@ -31,13 +32,27 @@ func TestAfdoEnabled(t *testing.T) { ` result := android.GroupFixturePreparers( prepareForRustTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo.afdo", ""), + cc.PrepareForTestWithFdoProfile, + android.FixtureAddTextFile("afdo_profiles_package/foo.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "foo://afdo_profiles_package:foo_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "foo_afdo", + profile: "foo.afdo", + } + `), + }.AddToFixture(), rustMockedFiles.AddToFixture(), ).RunTestWithBp(t, bp) foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc") - expectedCFlag := fmt.Sprintf(afdoFlagFormat, "toolchain/pgo-profiles/sampling/foo.afdo") + expectedCFlag := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo.afdo") if !strings.Contains(foo.Args["rustcFlags"], expectedCFlag) { t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", expectedCFlag, foo.Args["rustcFlags"]) @@ -55,16 +70,37 @@ func TestAfdoEnabledWithMultiArchs(t *testing.T) { ` result := android.GroupFixturePreparers( prepareForRustTest, - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm.afdo", ""), - android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/foo_arm64.afdo", ""), + cc.PrepareForTestWithFdoProfile, + android.FixtureAddTextFile("afdo_profiles_package/foo_arm.afdo", ""), + android.FixtureAddTextFile("afdo_profiles_package/foo_arm64.afdo", ""), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.AfdoProfiles = []string{ + "foo://afdo_profiles_package:foo_afdo", + } + }), + android.MockFS{ + "afdo_profiles_package/Android.bp": []byte(` + fdo_profile { + name: "foo_afdo", + arch: { + arm: { + profile: "foo_arm.afdo", + }, + arm64: { + profile: "foo_arm64.afdo", + } + } + } + `), + }.AddToFixture(), rustMockedFiles.AddToFixture(), ).RunTestWithBp(t, bp) fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon").Rule("rustc") fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc") - expectedCFlagArm := fmt.Sprintf(afdoFlagFormat, "toolchain/pgo-profiles/sampling/foo_arm.afdo") - expectedCFlagArm64 := fmt.Sprintf(afdoFlagFormat, "toolchain/pgo-profiles/sampling/foo_arm64.afdo") + expectedCFlagArm := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm.afdo") + expectedCFlagArm64 := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm64.afdo") if !strings.Contains(fooArm.Args["rustcFlags"], expectedCFlagArm) { t.Errorf("Expected 'fooArm' to enable afdo, but did not find %q in cflags %q", expectedCFlagArm, fooArm.Args["rustcFlags"]) diff --git a/rust/builder.go b/rust/builder.go index b89e7ad0a..0aef13d44 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -208,6 +208,9 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { outDirPrefix = "" } envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String())) + } else { + // TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value. + envVars = append(envVars, "OUT_DIR=out") } return envVars diff --git a/rust/rust.go b/rust/rust.go index f85babca4..56b463160 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -39,7 +39,6 @@ func init() { ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel() ctx.BottomUp("rust_begin", BeginMutator).Parallel() - }) android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel() @@ -920,7 +919,7 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { // Calculate rustc flags if mod.afdo != nil { - flags, deps = mod.afdo.flags(ctx, flags, deps) + flags, deps = mod.afdo.flags(actx, flags, deps) } if mod.compiler != nil { flags = mod.compiler.compilerFlags(ctx, flags) @@ -1516,7 +1515,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { for _, lib := range deps.Rustlibs { if autoDep.depTag == rlibDepTag { // Handle the rlib deptag case - addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations) + addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations) } else { // autoDep.depTag is a dylib depTag. Not all rustlibs may be available as a dylib however. // Check for the existence of the dylib deptag variant. Select it if available, @@ -1527,7 +1526,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib) } else { // If there's no dylib dependency available, try to add the rlib dependency instead. - addRlibDependency(actx, lib, mod, snapshotInfo, rlibDepVariations) + addRlibDependency(actx, lib, mod, &snapshotInfo, rlibDepVariations) } } } @@ -1613,11 +1612,13 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { // proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy. actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...) + + mod.afdo.addDep(ctx, actx) } // addRlibDependency will add an rlib dependency, rewriting to the snapshot library if available. -func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo *cc.SnapshotInfo, variations []blueprint.Variation) { - lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs) +func addRlibDependency(actx android.BottomUpMutatorContext, lib string, mod *Module, snapshotInfo **cc.SnapshotInfo, variations []blueprint.Variation) { + lib = cc.GetReplaceModuleName(lib, cc.GetSnapshot(mod, snapshotInfo, actx).Rlibs) actx.AddVariationDependencies(variations, rlibDepTag, lib) } diff --git a/tests/lib.sh b/tests/lib.sh index 26608b8f3..715eac143 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -94,9 +94,11 @@ function create_mock_soong { symlink_directory external/compiler-rt symlink_directory external/go-cmp symlink_directory external/golang-protobuf + symlink_directory external/licenseclassifier symlink_directory external/starlark-go symlink_directory external/python symlink_directory external/sqlite + symlink_directory external/spdx-tools touch "$MOCK_TOP/Android.bp" } diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh index 6066d70ef..2f154cde5 100755 --- a/tests/sbom_test.sh +++ b/tests/sbom_test.sh @@ -37,9 +37,14 @@ if [ $debug = "true" ]; then out_dir=out droid_target= fi + +function run_soong { + TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \ + build/soong/soong_ui.bash --make-mode "$@" +} + # m droid, build sbom later in case additional dependencies might be built and included in partition images. -TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \ - build/soong/soong_ui.bash --make-mode $droid_target dump.erofs +run_soong $droid_target dump.erofs lz4 product_out=$out_dir/target/product/vsoc_x86_64 sbom_test=$product_out/sbom_test @@ -47,11 +52,11 @@ mkdir $sbom_test cp $product_out/*.img $sbom_test # m sbom -TARGET_PRODUCT="aosp_cf_x86_64_phone" TARGET_BUILD_VARIANT=userdebug OUT_DIR=$out_dir \ - build/soong/soong_ui.bash --make-mode sbom +run_soong sbom # Generate installed file list from .img files in PRODUCT_OUT dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs +lz4=$out_dir/host/linux-x86/bin/lz4 declare -A diff_excludes diff_excludes[odm]="-I /odm/lib/modules" @@ -60,24 +65,12 @@ diff_excludes[vendor]=\ -I /vendor/lib/modules \ -I /vendor/odm" diff_excludes[system]=\ -"-I /acct/ \ - -I /adb_keys \ - -I /apex/ \ - -I /bin \ +"-I /bin \ -I /bugreports \ -I /cache \ - -I /config/ \ -I /d \ - -I /data/ \ - -I /data_mirror/ \ - -I /debug_ramdisk/ \ - -I /dev/ \ -I /etc \ -I /init \ - -I /init.environ.rc \ - -I /linkerconfig/ \ - -I /metadata/ \ - -I /mnt/ \ -I /odm/app \ -I /odm/bin \ -I /odm_dlkm/etc \ @@ -89,16 +82,7 @@ diff_excludes[system]=\ -I /odm/overlay \ -I /odm/priv-app \ -I /odm/usr \ - -I /oem/ \ - -I /postinstall/ \ - -I /proc/ \ - -I /product/ \ -I /sdcard \ - -I /second_stage_resources/ \ - -I /storage/ \ - -I /sys/ \ - -I /system_dlkm/ \ - -I /system_ext/ \ -I /system/lib64/android.hardware.confirmationui@1.0.so \ -I /system/lib64/android.hardware.confirmationui-V1-ndk.so \ -I /system/lib64/android.hardware.keymaster@4.1.so \ @@ -114,22 +98,32 @@ diff_excludes[system]=\ -I /system/lib64/libkm_compat.so \ -I /system/lib64/vndk-29 \ -I /system/lib64/vndk-sp-29 \ - -I /system/lib/modules \ -I /system/lib/vndk-29 \ -I /system/lib/vndk-sp-29 \ - -I /system/product \ - -I /system/system_ext \ -I /system/usr/icu \ - -I /system/vendor \ - -I /vendor/ \ -I /vendor_dlkm/etc" +function diff_files { + file_list_file="$1"; shift + files_in_spdx_file="$1"; shift + partition_name="$1"; shift + exclude= + if [ -v 'diff_excludes[$partition_name]' ]; then + exclude=${diff_excludes[$partition_name]} + fi + + diff "$file_list_file" "$files_in_spdx_file" $exclude + if [ $? != "0" ]; then + echo Found diffs in $f and SBOM. + exit 1 + else + echo No diffs. + fi + } + # Example output of dump.erofs is as below, and the data used in the test start # at line 11. Column 1 is inode id, column 2 is inode type and column 3 is name. -# Each line is captured in variable "entry", sed is used to trim the leading -# spaces and cut is used to get field 1 every time. Once a field is extracted, -# "cut --complement" is used to remove the extracted field so next field can be -# processed in the same way and to be processed field is always field 1. +# Each line is captured in variable "entry", awk is used to get type and name. # Output of dump.erofs: # File : / # Size: 160 On-disk size: 160 directory @@ -170,16 +164,13 @@ for f in $EROFS_IMAGES; do all_dirs=$(echo "$all_dirs" | cut -d ' ' -f1 --complement -s) entries=$($dump_erofs --ls --path "$dir" $f | tail -n +11) while read -r entry; do - nid=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1) - entry=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1 --complement) - type=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1) - entry=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1 --complement) - name=$(echo $entry | sed 's/^\s*//' | cut -d ' ' -f1) - case $type in + inode_type=$(echo $entry | awk -F ' ' '{print $2}') + name=$(echo $entry | awk -F ' ' '{print $3}') + case $inode_type in "2") # directory all_dirs=$(echo "$all_dirs $dir/$name" | sed 's/^\s*//') ;; - *) + "1"|"7") # 1: file, 7: symlink ( if [ "$partition_name" != "system" ]; then # system partition is mounted to /, not to prepend partition name. @@ -193,18 +184,31 @@ for f in $EROFS_IMAGES; do done sort -n -o "$file_list_file" "$file_list_file" - # Diff + grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file" + if [ "$partition_name" = "system" ]; then + # system partition is mounted to /, so include FileName starts with /root/ too. + grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file" + fi + sort -n -o "$files_in_spdx_file" "$files_in_spdx_file" + echo ============ Diffing files in $f and SBOM + diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" +done + +RAMDISK_IMAGES="$product_out/ramdisk.img" +for f in $RAMDISK_IMAGES; do + partition_name=$(basename $f | cut -d. -f1) + file_list_file="${sbom_test}/sbom-${partition_name}-files.txt" + files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt" + # lz4 decompress $f to stdout + # cpio list all entries like ls -l + # grep filter normal files and symlinks + # awk get entry names + # sed remove partition name from entry names + $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file" + grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file" - exclude= - if [ -v 'diff_excludes[$partition_name]' ]; then - exclude=${diff_excludes[$partition_name]} - fi - diff "$file_list_file" "$files_in_spdx_file" $exclude - if [ $? != "0" ]; then - echo Found diffs in $f and SBOM. - exit 1 - else - echo No diffs. - fi + + echo ============ Diffing files in $f and SBOM + diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" done
\ No newline at end of file |