diff options
| -rw-r--r-- | android/sdk_version.go | 4 | ||||
| -rw-r--r-- | bp2build/symlink_forest.go | 90 | ||||
| -rw-r--r-- | cc/library.go | 4 | ||||
| -rw-r--r-- | cc/ndk_library.go | 23 | ||||
| -rw-r--r-- | java/java.go | 8 | ||||
| -rw-r--r-- | java/testing.go | 29 |
6 files changed, 132 insertions, 26 deletions
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/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/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/java/java.go b/java/java.go index 403f503eb..d400b0cfb 100644 --- a/java/java.go +++ b/java/java.go @@ -1612,7 +1612,7 @@ func (ap *JavaApiContribution) GenerateAndroidBuildActions(ctx android.ModuleCon } type JavaApiLibraryDepsInfo struct { - StubsJar android.Path + JavaInfo StubsSrcJar android.Path } @@ -1821,7 +1821,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { staticLibs = append(staticLibs, provider.HeaderJars...) case depApiSrcsTag: provider := ctx.OtherModuleProvider(dep, JavaApiLibraryDepsProvider).(JavaApiLibraryDepsInfo) - classPaths = append(classPaths, provider.StubsJar) + classPaths = append(classPaths, provider.HeaderJars...) depApiSrcsStubsSrcJar = provider.StubsSrcJar } }) @@ -1900,7 +1900,9 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { }) ctx.SetProvider(JavaApiLibraryDepsProvider, JavaApiLibraryDepsInfo{ - StubsJar: al.stubsJar, + JavaInfo: JavaInfo{ + HeaderJars: android.PathsIfNonNil(al.stubsJar), + }, StubsSrcJar: al.stubsSrcJar, }) } diff --git a/java/testing.go b/java/testing.go index 8a0db9cb1..0764d264a 100644 --- a/java/testing.go +++ b/java/testing.go @@ -368,14 +368,6 @@ func gatherRequiredDepsForTest() string { "core.current.stubs", "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs", - "android_stubs_current.from-text", - "android_system_stubs_current.from-text", - "android_test_stubs_current.from-text", - "android_module_lib_stubs_current.from-text", - "android_system_server_stubs_current.from-text", - "core.current.stubs.from-text", - "legacy.core.platform.api.stubs.from-text", - "stable.core.platform.api.stubs.from-text", "kotlin-stdlib", "kotlin-stdlib-jdk7", @@ -396,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", |