Merge "Don't delete withres-withoutdex jar from R8 rules" into main
diff --git a/aconfig/java_aconfig_library.go b/aconfig/java_aconfig_library.go
index 4db0ef7..48cfb76 100644
--- a/aconfig/java_aconfig_library.go
+++ b/aconfig/java_aconfig_library.go
@@ -15,10 +15,13 @@
package aconfig
import (
- "android/soong/android"
- "android/soong/java"
"fmt"
+
+ "android/soong/android"
+ "android/soong/bazel"
+ "android/soong/java"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
type declarationsTagType struct {
@@ -32,7 +35,7 @@
Aconfig_declarations string
// whether to generate test mode version of the library
- Test bool
+ Test *bool
}
type JavaAconfigDeclarationsLibraryCallbacks struct {
@@ -68,7 +71,7 @@
// Generate the action to build the srcjar
srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
var mode string
- if callbacks.properties.Test {
+ if proptools.Bool(callbacks.properties.Test) {
mode = "test"
} else {
mode = "production"
@@ -89,3 +92,39 @@
return srcJarPath
}
+
+type bazelJavaAconfigLibraryAttributes struct {
+ Aconfig_declarations bazel.LabelAttribute
+ Test *bool
+ Sdk_version *string
+}
+
+func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) Bp2build(ctx android.Bp2buildMutatorContext, module *java.GeneratedJavaLibraryModule) {
+ if ctx.ModuleType() != "java_aconfig_library" {
+ return
+ }
+
+ // By default, soong builds the aconfig java library with private_current, however
+ // bazel currently doesn't support it so we default it to system_current. One reason
+ // is that the dependency of all java_aconfig_library aconfig-annotations-lib is
+ // built with system_current. For the java aconfig library itself it doesn't really
+ // matter whether it uses private API or system API because the only module it uses
+ // is DeviceConfig which is in system, and the rdeps of the java aconfig library
+ // won't change its sdk version either, so this should be fine.
+ // Ideally we should only use the default value if it is not set by the user, but
+ // bazel only supports a limited sdk versions, for example, the java_aconfig_library
+ // modules in framework/base use core_platform which is not supported by bazel yet.
+ // TODO(b/302148527): change soong to default to system_current as well.
+ sdkVersion := "system_current"
+ attrs := bazelJavaAconfigLibraryAttributes{
+ Aconfig_declarations: *bazel.MakeLabelAttribute(android.BazelLabelForModuleDepSingle(ctx, callbacks.properties.Aconfig_declarations).Label),
+ Test: callbacks.properties.Test,
+ Sdk_version: &sdkVersion,
+ }
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "java_aconfig_library",
+ Bzl_load_location: "//build/bazel/rules/java:java_aconfig_library.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: ctx.ModuleName()}, &attrs)
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 2e447c0..6014deb 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -524,6 +524,7 @@
}
Bp2buildModuleAlwaysConvertList = []string{
+ "AconfigJavaHostTest",
// aconfig
"libonce_cell",
"libanyhow",
@@ -1009,6 +1010,7 @@
"cc_prebuilt_library_static",
"combined_apis",
"droiddoc_exported_dir",
+ "java_aconfig_library",
"java_import",
"java_import_host",
"java_sdk_library",
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index d8effaa..09580a7 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "os"
"path/filepath"
"strings"
@@ -228,10 +229,18 @@
// 2. An Android.bp doesn't exist, but a checked-in BUILD/BUILD.bazel file exists, and that file
// is allowlisted by the bp2build configuration to be merged into the symlink forest workspace.
func isPackageBoundary(config Config, prefix string, components []string, componentIndex int) bool {
+ isSymlink := func(c Config, path string) bool {
+ f, err := c.fs.Lstat(path)
+ if err != nil {
+ // The file does not exist
+ return false
+ }
+ return f.Mode()&os.ModeSymlink == os.ModeSymlink
+ }
prefix = filepath.Join(prefix, filepath.Join(components[:componentIndex+1]...))
if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "Android.bp")); exists {
return true
- } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) {
+ } else if config.Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(prefix) || isSymlink(config, prefix) {
if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD")); exists {
return true
} else if exists, _, _ := config.fs.Exists(filepath.Join(prefix, "BUILD.bazel")); exists {
@@ -442,7 +451,8 @@
otherLabel := labelFromModule(ctx, m)
// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
- if tag != "" && m.Name() == "framework-res" {
+ if (tag != "" && m.Name() == "framework-res") ||
+ (tag == ".generated_srcjars" && ctx.OtherModuleType(m) == "java_aconfig_library") {
otherLabel += tag
}
diff --git a/android/config.go b/android/config.go
index f0fc15b..ed9b7b0 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2085,3 +2085,11 @@
func (c *deviceConfig) NextReleaseHideFlaggedApi() bool {
return Bool(c.config.productVariables.NextReleaseHideFlaggedApi)
}
+
+func (c *deviceConfig) ReleaseExposeFlaggedApi() bool {
+ return Bool(c.config.productVariables.Release_expose_flagged_api)
+}
+
+func (c *deviceConfig) HideFlaggedApis() bool {
+ return c.NextReleaseHideFlaggedApi() && !c.ReleaseExposeFlaggedApi()
+}
diff --git a/android/variable.go b/android/variable.go
index 73a4b2c..3a5ad63 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -490,6 +490,8 @@
PartitionVarsForBazelMigrationOnlyDoNotUse PartitionVariables `json:",omitempty"`
NextReleaseHideFlaggedApi *bool `json:",omitempty"`
+
+ Release_expose_flagged_api *bool `json:",omitempty"`
}
type PartitionVariables struct {
diff --git a/bp2build/aconfig_conversion_test.go b/bp2build/aconfig_conversion_test.go
index 51f0b2f..9d73ec0 100644
--- a/bp2build/aconfig_conversion_test.go
+++ b/bp2build/aconfig_conversion_test.go
@@ -20,11 +20,13 @@
"android/soong/aconfig"
"android/soong/android"
"android/soong/cc"
+ "android/soong/java"
)
func registerAconfigModuleTypes(ctx android.RegistrationContext) {
aconfig.RegisterBuildComponents(ctx)
ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
}
func TestAconfigDeclarations(t *testing.T) {
@@ -135,3 +137,101 @@
StubbedBuildDefinitions: []string{"server_configurable_flags"},
})
}
+
+func TestJavaAconfigLibrary(t *testing.T) {
+ bp := `
+ aconfig_declarations {
+ name: "foo_aconfig_declarations",
+ srcs: [
+ "foo1.aconfig",
+ ],
+ package: "com.android.foo",
+ }
+ java_aconfig_library {
+ name: "foo",
+ aconfig_declarations: "foo_aconfig_declarations",
+ test: true,
+ }
+ `
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions(
+ "aconfig_declarations",
+ "foo_aconfig_declarations",
+ AttrNameToString{
+ "srcs": `["foo1.aconfig"]`,
+ "package": `"com.android.foo"`,
+ },
+ ),
+ MakeBazelTargetNoRestrictions(
+ "java_aconfig_library",
+ "foo",
+ AttrNameToString{
+ "aconfig_declarations": `":foo_aconfig_declarations"`,
+ "test": `True`,
+ "sdk_version": `"system_current"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ )}
+ RunBp2BuildTestCase(t, registerAconfigModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+}
+
+func TestJavaAconfigLibraryAsTaggedOutput(t *testing.T) {
+ bp := `
+ aconfig_declarations {
+ name: "foo_aconfig_declarations",
+ srcs: [
+ "foo.aconfig",
+ ],
+ package: "com.android.foo",
+ }
+ java_library {
+ name: "foo_library",
+ srcs: [":foo_aconfig_library{.generated_srcjars}"],
+ sdk_version: "current",
+ bazel_module: { bp2build_available: true },
+ }
+ java_aconfig_library {
+ name: "foo_aconfig_library",
+ aconfig_declarations: "foo_aconfig_declarations",
+ test: true,
+ }
+ `
+ expectedBazelTargets := []string{
+ MakeBazelTargetNoRestrictions(
+ "aconfig_declarations",
+ "foo_aconfig_declarations",
+ AttrNameToString{
+ "srcs": `["foo.aconfig"]`,
+ "package": `"com.android.foo"`,
+ },
+ ),
+ MakeBazelTargetNoRestrictions(
+ "java_aconfig_library",
+ "foo_aconfig_library",
+ AttrNameToString{
+ "aconfig_declarations": `":foo_aconfig_declarations"`,
+ "test": `True`,
+ "sdk_version": `"system_current"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ ),
+ MakeBazelTargetNoRestrictions(
+ "java_library",
+ "foo_library",
+ AttrNameToString{
+ "srcs": `[":foo_aconfig_library.generated_srcjars"]`,
+ "sdk_version": `"current"`,
+ "target_compatible_with": `["//build/bazel/platforms/os:android"]`,
+ },
+ ),
+ MakeNeverlinkDuplicateTarget("java_library", "foo_library"),
+ }
+
+ RunBp2BuildTestCase(t, registerAconfigModuleTypes, Bp2buildTestCase{
+ Blueprint: bp,
+ ExpectedBazelTargets: expectedBazelTargets,
+ })
+}
diff --git a/bp2build/symlink_forest.go b/bp2build/symlink_forest.go
index 06e63ca..966b94a 100644
--- a/bp2build/symlink_forest.go
+++ b/bp2build/symlink_forest.go
@@ -22,6 +22,7 @@
"regexp"
"sort"
"strconv"
+ "strings"
"sync"
"sync/atomic"
@@ -31,12 +32,19 @@
)
// A tree structure that describes what to do at each directory in the created
-// symlink tree. Currently, it is used to enumerate which files/directories
+// symlink tree. Currently it is used to enumerate which files/directories
// should be excluded from symlinking. Each instance of "node" represents a file
// or a directory. If excluded is true, then that file/directory should be
// 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 = 2
+
type instructionsNode struct {
name string
excluded bool // If false, this is just an intermediate node
@@ -185,7 +193,7 @@
srcPath := shared.JoinPath(topdir, src)
dstPath := shared.JoinPath(topdir, dst)
- // Check whether a symlink already exists.
+ // Check if a symlink already exists.
if dstInfo, err := os.Lstat(dstPath); err != nil {
if !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Failed to lstat '%s': %s", dst, err)
@@ -232,49 +240,44 @@
return false
}
-// Returns the hash of the soong_build binary to determine whether we should
-// force symlink_forest to re-execute
-// This is similar to a version number increment - but that shouldn't be required
-// for every update to this file
-func getSoongBuildMTime() int64 {
- binaryPath, err := os.Executable()
- if err != nil {
- fmt.Fprintf(os.Stderr, "error finding executable path %s\n", err)
- os.Exit(1)
- }
-
- info, err := os.Stat(binaryPath)
- if err != nil {
- fmt.Fprintf(os.Stderr, "error stating executable path %s\n", err)
- }
-
- return info.ModTime().UnixMilli()
-}
-
-// maybeCleanSymlinkForest will remove the whole symlink forest directory if the soong_build
-// binary has changed since the last execution.
-func maybeCleanSymlinkForest(topdir, forest string, verbose bool, soongBuildMTime int64) error {
- mtimeFilePath := shared.JoinPath(topdir, forest, "soong_build_mtime")
- mtimeFileContents, err := os.ReadFile(mtimeFilePath)
+// 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
}
-
- if string(soongBuildMTime) != string(mtimeFileContents) {
+ 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
}
-func writeSoongBuildMTimeFile(topdir, forest string, mtime int64) error {
- hashFilePath := shared.JoinPath(topdir, forest, "soong_build_mtime")
- contents := []byte(strconv.FormatInt(mtime, 10))
-
- return os.WriteFile(hashFilePath, contents, 0666)
+// 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
@@ -470,10 +473,7 @@
symlinkCount: atomic.Uint64{},
}
- // Check whether soong_build has been modified since the last run
- soongBuildMTime := getSoongBuildMTime()
-
- err := maybeCleanSymlinkForest(topdir, forest, verbose, soongBuildMTime)
+ err := maybeCleanSymlinkForest(topdir, forest, verbose)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
@@ -491,10 +491,11 @@
deps = append(deps, dep)
}
- err = writeSoongBuildMTimeFile(topdir, forest, soongBuildMTime)
+ 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/java/base.go b/java/base.go
index 4168e08..c685fba 100644
--- a/java/base.go
+++ b/java/base.go
@@ -719,6 +719,10 @@
return j.SdkVersion(ctx).ApiLevel
}
+func (j *Module) GetDeviceProperties() *DeviceProperties {
+ return &j.deviceProperties
+}
+
func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
if j.deviceProperties.Max_sdk_version != nil {
return android.ApiLevelFrom(ctx, *j.deviceProperties.Max_sdk_version)
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 67a55bd..b059c0a 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -540,6 +540,10 @@
// See b/285312164 for more information.
cmd.FlagWithArg("--format-defaults ", "overloaded-method-order=source")
+ if ctx.DeviceConfig().HideFlaggedApis() {
+ cmd.FlagWithArg("--hide-annotation ", "android.annotation.FlaggedApi")
+ }
+
return cmd
}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 7a04d73..e149b98 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -22,6 +22,8 @@
"testing"
"android/soong/android"
+
+ "github.com/google/blueprint/proptools"
)
func TestDroidstubs(t *testing.T) {
@@ -403,3 +405,35 @@
ctx.ModuleForTests("bar", "android_common")
}
+
+func TestDroidstubsHideFlaggedApi(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.NextReleaseHideFlaggedApi = proptools.BoolPtr(true)
+ variables.Release_expose_flagged_api = proptools.BoolPtr(false)
+ }),
+ android.FixtureMergeMockFs(map[string][]byte{
+ "a/A.java": nil,
+ "a/current.txt": nil,
+ "a/removed.txt": nil,
+ }),
+ ).RunTestWithBp(t, `
+ droidstubs {
+ name: "foo",
+ srcs: ["a/A.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "a/current.txt",
+ removed_api_file: "a/removed.txt",
+ }
+ },
+ }
+ `)
+
+ m := result.ModuleForTests("foo", "android_common")
+ manifest := m.Output("metalava.sbox.textproto")
+ cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+ android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "--hide-annotation android.annotation.FlaggedApi")
+}
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index 578237e..930bfd2 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -35,6 +35,8 @@
// Called from inside GenerateAndroidBuildActions. Add the build rules to
// make the srcjar, and return the path to it.
GenerateSourceJarBuildActions(module *GeneratedJavaLibraryModule, ctx android.ModuleContext) android.Path
+
+ Bp2build(ctx android.Bp2buildMutatorContext, module *GeneratedJavaLibraryModule)
}
// GeneratedJavaLibraryModuleFactory provides a utility for modules that are generated
@@ -108,3 +110,7 @@
module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
module.Library.GenerateAndroidBuildActions(ctx)
}
+
+func (module *GeneratedJavaLibraryModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
+ module.callbacks.Bp2build(ctx, module)
+}
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
index 7f52fd1..7fbbfee 100644
--- a/java/generated_java_library_test.go
+++ b/java/generated_java_library_test.go
@@ -41,6 +41,9 @@
return android.PathForOutput(ctx, "blah.srcjar")
}
+func (callbacks *JavaGenLibTestCallbacks) Bp2build(ctx android.Bp2buildMutatorContext, module *GeneratedJavaLibraryModule) {
+}
+
func testGenLib(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
return android.GroupFixturePreparers(
PrepareForIntegrationTestWithJava,
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 2349993..6b9ff8b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -10,7 +10,6 @@
"$TOP/build/soong/tests/persistent_bazel_test.sh"
"$TOP/build/soong/tests/soong_test.sh"
"$TOP/build/soong/tests/stale_metrics_files_test.sh"
-"$TOP/build/soong/tests/symlink_forest_rerun_test.sh"
"$TOP/prebuilts/build-tools/linux-x86/bin/py3-cmd" "$TOP/build/bazel/ci/rbc_dashboard.py" aosp_arm64-userdebug
# The following tests build against the full source tree and don't rely on the
diff --git a/tests/symlink_forest_rerun_test.sh b/tests/symlink_forest_rerun_test.sh
deleted file mode 100755
index b704222..0000000
--- a/tests/symlink_forest_rerun_test.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash -eu
-
-set -o pipefail
-
-# Tests that symlink_Forest will rerun if soong_build has schanged
-
-source "$(dirname "$0")/lib.sh"
-
-function test_symlink_forest_reruns {
- setup
-
- mkdir -p a
- touch a/g.txt
- cat > a/Android.bp <<'EOF'
-filegroup {
- name: "g",
- srcs: ["g.txt"],
- }
-EOF
-
- run_soong g
-
- mtime=`cat out/soong/workspace/soong_build_mtime`
- # rerun with no changes - ensure that it hasn't changed
- run_soong g
- newmtime=`cat out/soong/workspace/soong_build_mtime`
- if [[ ! "$mtime" == "$mtime" ]]; then
- fail "symlink forest reran when it shouldn't have"
- fi
-
- # change exit codes to force a soong_build rebuild.
- sed -i 's/os.Exit(1)/os.Exit(2)/g' build/soong/bp2build/symlink_forest.go
-
- run_soong g
- newmtime=`cat out/soong/workspace/soong_build_mtime`
- if [[ "$mtime" == "$newmtime" ]]; then
- fail "symlink forest did not rerun when it should have"
- fi
-
-}
-
-scan_and_run_tests