Merge "Expand preprocessed flag to work on android_app_imports"
diff --git a/android/Android.bp b/android/Android.bp
index 641c438..118087d 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -17,6 +17,7 @@
"soong-remoteexec",
"soong-response",
"soong-shared",
+ "soong-starlark",
"soong-starlark-format",
"soong-ui-metrics_proto",
"soong-android-allowlists",
@@ -60,7 +61,6 @@
"license_metadata.go",
"license_sdk_member.go",
"licenses.go",
- "makefile_goal.go",
"makevars.go",
"metrics.go",
"module.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index c4e49c8..a4df34c 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -291,6 +291,7 @@
"packages/modules/Gki/libkver": Bp2BuildDefaultTrue,
"packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue,
"packages/modules/NeuralNetworks/apex": Bp2BuildDefaultTrue,
+ "packages/modules/NeuralNetworks/apex/testing": Bp2BuildDefaultTrue,
"packages/providers/MediaProvider/tools/dialogs": Bp2BuildDefaultFalse, // TODO(b/242834374)
"packages/screensavers/Basic": Bp2BuildDefaultTrue,
"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
@@ -1521,6 +1522,11 @@
"adb_tls_connection_test",
// M9: mixed builds for mainline trains launch
"api_fingerprint",
+ // M11: neuralnetworks launch
+ "com.android.neuralnetworks",
+ "test_com.android.neuralnetworks",
+ "libneuralnetworks",
+ "libneuralnetworks_static",
}
// Staging-mode allowlist. Modules in this list are only built
@@ -1528,11 +1534,7 @@
// which will soon be added to the prod allowlist.
// It is implicit that all modules in ProdMixedBuildsEnabledList will
// also be built - do not add them to this list.
- StagingMixedBuildsEnabledList = []string{
- "com.android.neuralnetworks",
- "libneuralnetworks",
- "libneuralnetworks_static",
- }
+ StagingMixedBuildsEnabledList = []string{}
// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
ProdDclaMixedBuildsEnabledList = []string{
diff --git a/android/apex.go b/android/apex.go
index 823afbb..5dcf73b 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -513,8 +513,9 @@
// exactly the same set of APEXes (and platform), i.e. if their apex_available
// properties have the same elements.
func AvailableToSameApexes(mod1, mod2 ApexModule) bool {
- mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available)
- mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available)
+ // Use CopyOf to prevent non-determinism (b/275313114#comment1)
+ mod1ApexAvail := SortedUniqueStrings(CopyOf(mod1.apexModuleBase().ApexProperties.Apex_available))
+ mod2ApexAvail := SortedUniqueStrings(CopyOf(mod2.apexModuleBase().ApexProperties.Apex_available))
if len(mod1ApexAvail) != len(mod2ApexAvail) {
return false
}
diff --git a/android/bazel.go b/android/bazel.go
index 1646883..58d9d87 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,6 +32,22 @@
Bp2BuildTopLevel = "."
)
+type MixedBuildEnabledStatus int
+
+const (
+ // This module can be mixed_built.
+ MixedBuildEnabled = iota
+
+ // There is a technical incompatibility preventing this module from being
+ // bazel-analyzed. Note: the module might also be incompatible.
+ TechnicalIncompatibility
+
+ // This module cannot be mixed_built due to some incompatibility with it
+ // that is not a platform incompatibility. Example: the module-type is not
+ // enabled, or is not bp2build-converted.
+ ModuleIncompatibility
+)
+
// FileGroupAsLibrary describes a filegroup module that is converted to some library
// such as aidl_library or proto_library.
type FileGroupAsLibrary interface {
@@ -346,24 +362,31 @@
}).(Bp2BuildConversionAllowlist)
}
-// MixedBuildsEnabled returns true if a module is ready to be replaced by a
-// converted or handcrafted Bazel target. As a side effect, calling this
-// method will also log whether this module is mixed build enabled for
-// metrics reporting.
-func MixedBuildsEnabled(ctx BaseModuleContext) bool {
+// MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether
+// a module is ready to be replaced by a converted or handcrafted Bazel target.
+// As a side effect, calling this method will also log whether this module is
+// mixed build enabled for metrics reporting.
+func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
module := ctx.Module()
apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
withinApex := !apexInfo.IsForPlatform()
+
+ platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
+ if platformIncompatible {
+ ctx.Config().LogMixedBuild(ctx, false)
+ return TechnicalIncompatibility
+ }
+
mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
- ctx.Os() != Windows && // Windows toolchains are not currently supported.
- ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported.
- ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326).
- ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported.
module.Enabled() &&
convertedToBazel(ctx, module) &&
ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
- return mixedBuildEnabled
+
+ if mixedBuildEnabled {
+ return MixedBuildEnabled
+ }
+ return ModuleIncompatibility
}
// ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -388,6 +411,13 @@
OtherModuleDir(m blueprint.Module) string
}
+func isPlatformIncompatible(osType OsType, arch ArchType) bool {
+ return osType == Windows || // Windows toolchains are not currently supported.
+ osType == LinuxBionic || // Linux Bionic toolchains are not currently supported.
+ osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326).
+ arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported.
+}
+
func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
if !b.bazelProps().Bazel_module.CanConvertToBazel {
return false
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 9c273d9..dafb610 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -18,7 +18,6 @@
"bytes"
"fmt"
"os"
- "os/exec"
"path"
"path/filepath"
"runtime"
@@ -30,7 +29,6 @@
"android/soong/bazel/cquery"
"android/soong/shared"
"android/soong/starlark_fmt"
-
"github.com/google/blueprint"
"github.com/google/blueprint/metrics"
@@ -86,12 +84,10 @@
func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
if m := ctx.Module(); m.Enabled() {
if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
- queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx)
+ mixedBuildEnabled := MixedBuildsEnabled(ctx)
+ queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && mixedBuildEnabled == MixedBuildEnabled
if queueMixedBuild {
mixedBuildMod.QueueBazelCall(ctx)
- } else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok {
- // TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added
- ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name())
}
}
}
@@ -220,8 +216,7 @@
}
type bazelRunner interface {
- createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
- issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
+ issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
}
type bazelPaths struct {
@@ -660,36 +655,6 @@
expression string
}
-type mockBazelRunner struct {
- bazelCommandResults map[bazelCommand]string
- // use *exec.Cmd as a key to get the bazelCommand, the map will be used in issueBazelCommand()
- // Register createBazelCommand() invocations. Later, an
- // issueBazelCommand() invocation can be mapped to the *exec.Cmd instance
- // and then to the expected result via bazelCommandResults
- tokens map[*exec.Cmd]bazelCommand
- commands []bazelCommand
- extraFlags []string
-}
-
-func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName,
- command bazelCommand, extraFlags ...string) *exec.Cmd {
- r.commands = append(r.commands, command)
- r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
- cmd := &exec.Cmd{}
- if r.tokens == nil {
- r.tokens = make(map[*exec.Cmd]bazelCommand)
- }
- r.tokens[cmd] = command
- return cmd
-}
-
-func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
- if command, ok := r.tokens[bazelCmd]; ok {
- return r.bazelCommandResults[command], "", nil
- }
- return "", "", nil
-}
-
type builtinBazelRunner struct {
useBazelProxy bool
outDir string
@@ -699,17 +664,12 @@
// Returns (stdout, stderr, error). The first and second return values are strings
// containing the stdout and stderr of the run command, and an error is returned if
// the invocation returned an error code.
-func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
+func (r *builtinBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
if r.useBazelProxy {
eventHandler.Begin("client_proxy")
defer eventHandler.End("client_proxy")
proxyClient := bazel.NewProxyClient(r.outDir)
- // Omit the arg containing the Bazel binary, as that is handled by the proxy
- // server.
- bazelFlags := bazelCmd.Args[1:]
- // TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its
- // not actually executed for client proxying.
- resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env})
+ resp, err := proxyClient.IssueCommand(cmdRequest)
if err != nil {
return "", "", err
@@ -721,26 +681,19 @@
} else {
eventHandler.Begin("bazel command")
defer eventHandler.End("bazel command")
- stderr := &bytes.Buffer{}
- bazelCmd.Stderr = stderr
- if output, err := bazelCmd.Output(); err != nil {
- return "", string(stderr.Bytes()),
- fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
- err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
- } else {
- return string(output), string(stderr.Bytes()), nil
- }
+
+ stdout, stderr, err := bazel.ExecBazel(paths.bazelPath, absolutePath(paths.syntheticWorkspaceDir()), cmdRequest)
+ return string(stdout), string(stderr), err
}
}
-func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand,
- extraFlags ...string) *exec.Cmd {
+func (context *mixedBuildBazelContext) createBazelCommand(config Config, runName bazel.RunName, command bazelCommand,
+ extraFlags ...string) bazel.CmdRequest {
cmdFlags := []string{
- "--output_base=" + absolutePath(paths.outputBase),
+ "--output_base=" + absolutePath(context.paths.outputBase),
command.command,
command.expression,
- // TODO(asmundak): is it needed in every build?
- "--profile=" + shared.BazelMetricsFilename(paths, runName),
+ "--profile=" + shared.BazelMetricsFilename(context.paths, runName),
// We don't need to set --host_platforms because it's set in bazelrc files
// that the bazel shell script wrapper passes
@@ -755,15 +708,13 @@
}
cmdFlags = append(cmdFlags, extraFlags...)
- bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
- bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
extraEnv := []string{
- "HOME=" + paths.homeDir,
+ "HOME=" + context.paths.homeDir,
pwdPrefix(),
- "BUILD_DIR=" + absolutePath(paths.soongOutDir),
+ "BUILD_DIR=" + absolutePath(context.paths.soongOutDir),
// Make OUT_DIR absolute here so build/bazel/bin/bazel uses the correct
// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
- "OUT_DIR=" + absolutePath(paths.outDir()),
+ "OUT_DIR=" + absolutePath(context.paths.outDir()),
// Disables local host detection of gcc; toolchain information is defined
// explicitly in BUILD files.
"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
@@ -775,15 +726,15 @@
}
extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val))
}
- bazelCmd.Env = append(os.Environ(), extraEnv...)
+ envVars := append(os.Environ(), extraEnv...)
- return bazelCmd
+ return bazel.CmdRequest{cmdFlags, envVars}
}
-func printableCqueryCommand(bazelCmd *exec.Cmd) string {
- outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(bazelCmd.Args, "\" \"") + "\""
+func (context *mixedBuildBazelContext) printableCqueryCommand(bazelCmd bazel.CmdRequest) string {
+ args := append([]string{context.paths.bazelPath}, bazelCmd.Argv...)
+ outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(args, "\" \"") + "\""
return outputString
-
}
func (context *mixedBuildBazelContext) mainBzlFileContents() []byte {
@@ -1106,9 +1057,10 @@
const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
var (
- cqueryCmd = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
- aqueryCmd = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
- buildCmd = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+ cqueryCmd = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
+ aqueryCmd = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
+ buildCmd = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+ allBazelCommands = []bazelCommand{aqueryCmd, cqueryCmd, buildCmd}
)
// Issues commands to Bazel to receive results for all cquery requests
@@ -1170,12 +1122,12 @@
extraFlags = append(extraFlags, "--collect_code_coverage")
}
- cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
- cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
+ cqueryCmdRequest := context.createBazelCommand(config, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
+ cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCmdRequest, context.paths, eventHandler)
if cqueryErr != nil {
return cqueryErr
}
- cqueryCommandPrint := fmt.Sprintf("cquery command line:\n %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
+ cqueryCommandPrint := fmt.Sprintf("cquery command line:\n %s \n\n\n", context.printableCqueryCommand(cqueryCmdRequest))
if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
return err
}
@@ -1233,8 +1185,8 @@
extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
}
}
- aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
- extraFlags...), eventHandler)
+ aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, bazel.AqueryBuildRootRunName, aqueryCmd,
+ extraFlags...), context.paths, eventHandler)
if err != nil {
return err
}
@@ -1249,7 +1201,7 @@
// Issue a build command of the phony root to generate symlink forests for dependencies of the
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
// but some of symlinks may be required to resolve source dependencies of the build.
- _, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
+ _, _, err := context.issueBazelCommand(context.createBazelCommand(config, bazel.BazelBuildPhonyRootRunName, buildCmd), context.paths, eventHandler)
return err
}
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index c67d7fb..b17b765 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -8,6 +8,7 @@
"strings"
"testing"
+ "android/soong/bazel"
"android/soong/bazel/cquery"
analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
@@ -19,6 +20,34 @@
type testInvokeBazelContext struct{}
+type mockBazelRunner struct {
+ testHelper *testing.T
+ // Stores mock behavior. If an issueBazelCommand request is made for command
+ // k, and {k:v} is present in this map, then the mock will return v.
+ bazelCommandResults map[bazelCommand]string
+ // Requests actually made of the mockBazelRunner with issueBazelCommand,
+ // keyed by the command they represent.
+ bazelCommandRequests map[bazelCommand]bazel.CmdRequest
+}
+
+func (r *mockBazelRunner) bazelCommandForRequest(cmdRequest bazel.CmdRequest) bazelCommand {
+ for _, arg := range cmdRequest.Argv {
+ for _, cmdType := range allBazelCommands {
+ if arg == cmdType.command {
+ return cmdType
+ }
+ }
+ }
+ r.testHelper.Fatalf("Unrecognized bazel request: %s", cmdRequest)
+ return cqueryCmd
+}
+
+func (r *mockBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
+ command := r.bazelCommandForRequest(cmdRequest)
+ r.bazelCommandRequests[command] = cmdRequest
+ return r.bazelCommandResults[command], "", nil
+}
+
func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
return &metrics.EventHandler{}
}
@@ -36,9 +65,7 @@
`@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
`@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
}
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"),
- })
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{cqueryCmd: strings.Join(cmd_results, "\n")})
bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
@@ -139,8 +166,7 @@
if err != nil {
t.Error(err)
}
- bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
- bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
if err != nil {
@@ -166,30 +192,26 @@
testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
- verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`)
+ verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,+foo2,-bar1,-bar2")
testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
- verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`)
+ verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,-bar1")
testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
testConfig.productVariables.NativeCoverageExcludePaths = nil
- verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`)
+ verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1")
testConfig.productVariables.NativeCoveragePaths = nil
testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
- verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
+ verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=-bar1")
testConfig.productVariables.NativeCoveragePaths = []string{"*"}
testConfig.productVariables.NativeCoverageExcludePaths = nil
- verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+.*`)
+ verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+.*")
testConfig.productVariables.ClangCoverage = boolPtr(false)
- actual := verifyExtraFlags(t, testConfig, ``)
- if strings.Contains(actual, "--collect_code_coverage") ||
- strings.Contains(actual, "--instrumentation_filter=") {
- t.Errorf("Expected code coverage disabled, but got %#v", actual)
- }
+ verifyAqueryDoesNotContainSubstrings(t, testConfig, "collect_code_coverage", "instrumentation_filter")
}
func TestBazelRequestsSorted(t *testing.T) {
@@ -268,7 +290,8 @@
}
}
-func verifyExtraFlags(t *testing.T, config Config, expected string) string {
+func verifyAqueryContainsFlags(t *testing.T, config Config, expected ...string) {
+ t.Helper()
bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
@@ -276,17 +299,49 @@
t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
- flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags
- if expected := 3; len(flags) != expected {
- t.Errorf("Expected %d extra flags got %#v", expected, flags)
+ sliceContains := func(slice []string, x string) bool {
+ for _, s := range slice {
+ if s == x {
+ return true
+ }
+ }
+ return false
}
- actual := flags[1]
- if !strings.Contains(actual, expected) {
- t.Errorf("Expected %#v got %#v", expected, actual)
+ aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
+
+ for _, expectedFlag := range expected {
+ if !sliceContains(aqueryArgv, expectedFlag) {
+ t.Errorf("aquery does not contain expected flag %#v. Argv was: %#v", expectedFlag, aqueryArgv)
+ }
+ }
+}
+
+func verifyAqueryDoesNotContainSubstrings(t *testing.T, config Config, substrings ...string) {
+ t.Helper()
+ bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+ err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
+ if err != nil {
+ t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
}
- return actual
+ sliceContainsSubstring := func(slice []string, substring string) bool {
+ for _, s := range slice {
+ if strings.Contains(s, substring) {
+ return true
+ }
+ }
+ return false
+ }
+
+ aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
+
+ for _, substring := range substrings {
+ if sliceContainsSubstring(aqueryArgv, substring) {
+ t.Errorf("aquery contains unexpected substring %#v. Argv was: %#v", substring, aqueryArgv)
+ }
+ }
}
func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
@@ -296,11 +351,14 @@
outputBase: "outputbase",
workspaceDir: "workspace_dir",
}
- aqueryCommand := bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}
- if _, exists := bazelCommandResults[aqueryCommand]; !exists {
- bazelCommandResults[aqueryCommand] = ""
+ if _, exists := bazelCommandResults[aqueryCmd]; !exists {
+ bazelCommandResults[aqueryCmd] = ""
}
- runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
+ runner := &mockBazelRunner{
+ testHelper: t,
+ bazelCommandResults: bazelCommandResults,
+ bazelCommandRequests: map[bazelCommand]bazel.CmdRequest{},
+ }
return &mixedBuildBazelContext{
bazelRunner: runner,
paths: &p,
diff --git a/android/config.go b/android/config.go
index 032172d..7141e54 100644
--- a/android/config.go
+++ b/android/config.go
@@ -102,6 +102,8 @@
UseBazelProxy bool
BuildFromTextStub bool
+
+ EnsureAllowlistIntegrity bool
}
// Build modes that soong_build can run as.
@@ -278,6 +280,11 @@
// If buildFromTextStub is true then the Java API stubs are
// built from the signature text files, not the source Java files.
buildFromTextStub bool
+
+ // If ensureAllowlistIntegrity is true, then the presence of any allowlisted
+ // modules that aren't mixed-built for at least one variant will cause a build
+ // failure
+ ensureAllowlistIntegrity bool
}
type deviceConfig struct {
@@ -1904,6 +1911,10 @@
return Bool(c.productVariables.HostMusl)
}
+func (c *config) GetMixedBuildsEnabledModules() map[string]struct{} {
+ return c.mixedBuildEnabledModules
+}
+
func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
moduleName := ctx.Module().Name()
c.mixedBuildsLock.Lock()
diff --git a/android/makefile_goal.go b/android/makefile_goal.go
deleted file mode 100644
index 07354a6..0000000
--- a/android/makefile_goal.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 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 android
-
-import (
- "fmt"
- "io"
- "path/filepath"
-
- "github.com/google/blueprint/proptools"
-)
-
-func init() {
- RegisterModuleType("makefile_goal", MakefileGoalFactory)
-}
-
-type makefileGoalProperties struct {
- // Sources.
-
- // Makefile goal output file path, relative to PRODUCT_OUT.
- Product_out_path *string
-}
-
-type makefileGoal struct {
- ModuleBase
-
- properties makefileGoalProperties
-
- // Destination. Output file path of this module.
- outputFilePath OutputPath
-}
-
-var _ AndroidMkEntriesProvider = (*makefileGoal)(nil)
-var _ OutputFileProducer = (*makefileGoal)(nil)
-
-// Input file of this makefile_goal module. Nil if none specified. May use variable names in makefiles.
-func (p *makefileGoal) inputPath() *string {
- if p.properties.Product_out_path != nil {
- return proptools.StringPtr(filepath.Join("$(PRODUCT_OUT)", proptools.String(p.properties.Product_out_path)))
- }
- return nil
-}
-
-// OutputFileProducer
-func (p *makefileGoal) OutputFiles(tag string) (Paths, error) {
- if tag != "" {
- return nil, fmt.Errorf("unsupported tag %q", tag)
- }
- return Paths{p.outputFilePath}, nil
-}
-
-// AndroidMkEntriesProvider
-func (p *makefileGoal) DepsMutator(ctx BottomUpMutatorContext) {
- if p.inputPath() == nil {
- ctx.PropertyErrorf("product_out_path", "Path relative to PRODUCT_OUT required")
- }
-}
-
-func (p *makefileGoal) GenerateAndroidBuildActions(ctx ModuleContext) {
- filename := filepath.Base(proptools.String(p.inputPath()))
- p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
-
- ctx.InstallFile(PathForModuleInstall(ctx, "etc"), ctx.ModuleName(), p.outputFilePath)
-}
-
-func (p *makefileGoal) AndroidMkEntries() []AndroidMkEntries {
- return []AndroidMkEntries{AndroidMkEntries{
- Class: "ETC",
- OutputFile: OptionalPathForPath(p.outputFilePath),
- ExtraFooters: []AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string) {
- // Can't use Cp because inputPath() is not a valid Path.
- fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath)
- },
- },
- }}
-}
-
-// Import a Makefile goal to Soong by copying the file built by
-// the goal to a path visible to Soong. This rule only works on boot images.
-func MakefileGoalFactory() Module {
- module := &makefileGoal{}
- module.AddProperties(&module.properties)
- InitAndroidModule(module)
- return module
-}
diff --git a/android/module.go b/android/module.go
index ba47453..c8670c3 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2438,7 +2438,7 @@
func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
- if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+ if mixedBuildMod.IsMixedBuildSupported(ctx) && (MixedBuildsEnabled(ctx) == MixedBuildEnabled) {
return mixedBuildMod, true
}
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 2139c3c..5b5e613 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,7 +55,6 @@
AddNeverAllowRules(createJavaDeviceForHostRules()...)
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
- AddNeverAllowRules(createMakefileGoalRules()...)
AddNeverAllowRules(createInitFirstStageRules()...)
AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
AddNeverAllowRules(createBp2BuildRule())
@@ -236,20 +235,6 @@
}
}
-func createMakefileGoalRules() []Rule {
- allowlist := []string{
- // libwifi_hal uses makefile_goal for its dependencies
- "frameworks/opt/net/wifi/libwifi_hal",
- }
- return []Rule{
- NeverAllow().
- ModuleType("makefile_goal").
- WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
- NotIn(allowlist...).
- Because("Only boot images may be imported as a makefile goal if not in allowed projects"),
- }
-}
-
func createInitFirstStageRules() []Rule {
return []Rule{
NeverAllow().
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 5f5f9a1..ddd982d 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -313,45 +313,6 @@
"module \"outside_art_libraries\": violates neverallow",
},
},
- {
- name: "disallowed makefile_goal",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- makefile_goal {
- name: "foo",
- product_out_path: "boot/trap.img"
- }
- `),
- },
- expectedErrors: []string{
- "Only boot images.* may be imported as a makefile goal",
- },
- },
- {
- name: "disallowed makefile_goal outside external",
- fs: map[string][]byte{
- "project/Android.bp": []byte(`
- makefile_goal {
- name: "foo",
- product_out_path: "obj/EXE/foo",
- }
- `),
- },
- expectedErrors: []string{
- "not in allowed projects",
- },
- },
- {
- name: "allow makefile_goal within external",
- fs: map[string][]byte{
- "frameworks/opt/net/wifi/libwifi_hal/Android.bp": []byte(`
- makefile_goal {
- name: "foo",
- product_out_path: "obj/EXE/foo",
- }
- `),
- },
- },
// Tests for the rule prohibiting the use of framework
{
name: "prohibit framework",
@@ -391,7 +352,6 @@
ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
- ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
}),
)
@@ -489,22 +449,3 @@
func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
}
-
-type mockMakefileGoalProperties struct {
- Product_out_path *string
-}
-
-type mockMakefileGoalModule struct {
- ModuleBase
- properties mockMakefileGoalProperties
-}
-
-func newMockMakefileGoalModule() Module {
- m := &mockMakefileGoalModule{}
- m.AddProperties(&m.properties)
- InitAndroidModule(m)
- return m
-}
-
-func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) {
-}
diff --git a/android/ninja_deps.go b/android/ninja_deps.go
index 2f442d5..1d50a47 100644
--- a/android/ninja_deps.go
+++ b/android/ninja_deps.go
@@ -14,7 +14,10 @@
package android
-import "sort"
+import (
+ "android/soong/starlark_import"
+ "sort"
+)
func (c *config) addNinjaFileDeps(deps ...string) {
for _, dep := range deps {
@@ -40,4 +43,11 @@
func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...)
+
+ deps, err := starlark_import.GetNinjaDeps()
+ if err != nil {
+ ctx.Errorf("Error running starlark code: %s", err)
+ } else {
+ ctx.AddNinjaFileDeps(deps...)
+ }
}
diff --git a/android/override_module.go b/android/override_module.go
index 2d30a85..86f582b 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -304,8 +304,9 @@
for i, o := range overrides {
mods[i+1].(OverridableModule).override(ctx, o)
if o.getOverriddenByPrebuilt() {
- // The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
- mods[i+1].HideFromMake()
+ // The overriding module itself, too, is overridden by a prebuilt.
+ // Copy the flag and hide it in make
+ mods[i+1].ReplacedByPrebuilt()
}
}
} else if o, ok := ctx.Module().(OverrideModule); ok {
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 0ae8073..08762ef 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -100,6 +100,15 @@
return name
}
+// JavaApiLibraryNames applies JavaApiLibraryName to the list of java_library names.
+func JavaApiLibraryNames(c Config, names []string) []string {
+ apiLibs := make([]string, len(names))
+ for i, name := range names {
+ apiLibs[i] = JavaApiLibraryName(c, name)
+ }
+ return apiLibs
+}
+
func (k SdkKind) DefaultJavaLibraryName() string {
switch k {
case SdkPublic:
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 1581949..ebc35cf 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -23,7 +23,11 @@
)
func init() {
- android.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
+ registerApexDepsInfoComponents(android.InitRegistrationContext)
+}
+
+func registerApexDepsInfoComponents(ctx android.RegistrationContext) {
+ ctx.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
}
type apexDepsInfoSingleton struct {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 440afec..31b7ef7 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1980,6 +1980,93 @@
android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
}
+func TestTrackAllowedDeps(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: true,
+ native_shared_libs: [
+ "mylib",
+ "yourlib",
+ ],
+ min_sdk_version: "29",
+ }
+
+ apex {
+ name: "myapex2",
+ key: "myapex.key",
+ updatable: false,
+ native_shared_libs: ["yourlib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ shared_libs: ["libbar"],
+ min_sdk_version: "29",
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "libbar",
+ stubs: { versions: ["29", "30"] },
+ }
+
+ cc_library {
+ name: "yourlib",
+ srcs: ["mylib.cpp"],
+ min_sdk_version: "29",
+ apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+ }
+ `, withFiles(android.MockFS{
+ "packages/modules/common/build/allowed_deps.txt": nil,
+ }))
+
+ depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+ inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+ android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+ "out/soong/.intermediates/myapex/android_common_myapex_image/depsinfo/flatlist.txt")
+ android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+ "out/soong/.intermediates/myapex2/android_common_myapex2_image/depsinfo/flatlist.txt")
+
+ myapex := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ flatlist := strings.Split(myapex.Output("depsinfo/flatlist.txt").BuildParams.Args["content"], "\\n")
+ android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+ flatlist, "libbar(minSdkVersion:(no version)) (external)")
+ android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+ flatlist, "mylib:(minSdkVersion:29)")
+ android.AssertStringListContains(t, "track platform-available lib",
+ flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ updatable: true,
+ min_sdk_version: "29",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `)
+ depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+ if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output {
+ t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist")
+ }
+}
+
func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
ctx := testApex(t, `
apex {
@@ -5689,67 +5776,6 @@
})
}
-func TestPrebuiltSkipsSymbols(t *testing.T) {
- testCases := []struct {
- name string
- usePrebuilt bool
- installSymbolFiles bool
- }{
- {
- name: "Source module build rule doesn't install symbol files",
- usePrebuilt: true,
- installSymbolFiles: false,
- },
- {
- name: "Source module is installed with symbols",
- usePrebuilt: false,
- installSymbolFiles: true,
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- preferProperty := "prefer: false"
- if tc.usePrebuilt {
- preferProperty = "prefer: true"
- }
- ctx := testApex(t, `
- // Source module
- apex {
- name: "myapex",
- binaries: ["foo"],
- key: "myapex.key",
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- apex_set {
- name: "myapex",
- set: "myapex.apks",
- `+preferProperty+`
- }
-
- cc_binary {
- name: "foo",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "myapex" ],
- }
- `)
- // Symbol files are installed by installing entries under ${OUT}/apex/{apex name}
- android.AssertStringListContainsEquals(t, "Installs",
- ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().FilesToInstall().Strings(),
- filepath.Join(ctx.Config().SoongOutDir(), "target/product/test_device/apex/myapex/bin/foo"),
- tc.installSymbolFiles)
- })
- }
-}
-
func TestApexWithTests(t *testing.T) {
ctx := testApex(t, `
apex_test {
diff --git a/apex/builder.go b/apex/builder.go
index 3c7671b..7c6522d 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -231,7 +231,7 @@
apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` +
- `&& ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
+ ` && ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"},
Description: "run apex_sepolicy_tests",
})
@@ -468,10 +468,6 @@
imageDir := android.PathForModuleOut(ctx, "image"+suffix)
installSymbolFiles := (!ctx.Config().KatiEnabled() || a.ExportedToMake()) && a.installable()
- // We can't install symbol files when prebuilt is used.
- if a.IsReplacedByPrebuilt() {
- installSymbolFiles = false
- }
// set of dependency module:location mappings
installMapSet := make(map[string]bool)
@@ -876,7 +872,8 @@
args["outCommaList"] = signedOutputFile.String()
}
var validations android.Paths
- if suffix == imageApexSuffix {
+ // TODO(b/279688635) deapexer supports [ext4]
+ if suffix == imageApexSuffix && ext4 == a.payloadFsType {
validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
}
ctx.Build(pctx, android.BuildParams{
diff --git a/apex/testing.go b/apex/testing.go
index 69bd73e..3b200f0 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -19,6 +19,7 @@
var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
android.FixtureRegisterWithContext(registerApexBuildComponents),
android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+ android.FixtureRegisterWithContext(registerApexDepsInfoComponents),
// Additional files needed in tests that disallow non-existent source files.
// This includes files that are needed by all, or at least most, instances of an apex module type.
android.MockFS{
diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go
index d7f5e64..2940b99 100644
--- a/bazel/bazel_proxy.go
+++ b/bazel/bazel_proxy.go
@@ -128,6 +128,24 @@
}
}
+func ExecBazel(bazelPath string, workspaceDir string, request CmdRequest) (stdout []byte, stderr []byte, cmdErr error) {
+ bazelCmd := exec.Command(bazelPath, request.Argv...)
+ bazelCmd.Dir = workspaceDir
+ bazelCmd.Env = request.Env
+
+ stderrBuffer := &bytes.Buffer{}
+ bazelCmd.Stderr = stderrBuffer
+
+ if output, err := bazelCmd.Output(); err != nil {
+ cmdErr = fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+ err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderrBuffer)
+ } else {
+ stdout = output
+ }
+ stderr = stderrBuffer.Bytes()
+ return
+}
+
func (b *ProxyServer) handleRequest(conn net.Conn) error {
defer conn.Close()
@@ -137,23 +155,13 @@
return fmt.Errorf("Error decoding request: %s", err)
}
- bazelCmd := exec.Command("./build/bazel/bin/bazel", req.Argv...)
- bazelCmd.Dir = b.workspaceDir
- bazelCmd.Env = req.Env
-
- stderr := &bytes.Buffer{}
- bazelCmd.Stderr = stderr
- var stdout string
- var bazelErrString string
-
- if output, err := bazelCmd.Output(); err != nil {
- bazelErrString = fmt.Sprintf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
- err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
- } else {
- stdout = string(output)
+ stdout, stderr, cmdErr := ExecBazel("./build/bazel/bin/bazel", b.workspaceDir, req)
+ errorString := ""
+ if cmdErr != nil {
+ errorString = cmdErr.Error()
}
- resp := CmdResponse{stdout, string(stderr.Bytes()), bazelErrString}
+ resp := CmdResponse{string(stdout), string(stderr), errorString}
enc := gob.NewEncoder(conn)
if err := enc.Encode(&resp); err != nil {
return fmt.Errorf("Error encoding response: %s", err)
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index bf3a6b5..6a3b3c8 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -149,6 +149,7 @@
rootSharedLibraries = []
shared_info_tag = "//build/bazel/rules/cc:cc_library_shared.bzl%CcSharedLibraryOutputInfo"
+stubs_tag = "//build/bazel/rules/cc:cc_stub_library.bzl%CcStubInfo"
unstripped_tag = "//build/bazel/rules/cc:stripped_cc_common.bzl%CcUnstrippedInfo"
unstripped = ""
@@ -160,6 +161,8 @@
unstripped = path
if unstripped_tag in p:
unstripped = p[unstripped_tag].unstripped.path
+elif stubs_tag in p:
+ rootSharedLibraries.extend([f.path for f in target.files.to_list()])
else:
for linker_input in linker_inputs:
for library in linker_input.libraries:
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index d1dfb9d..b22cb28 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -15,6 +15,7 @@
package bp2build
import (
+ "android/soong/starlark_import"
"fmt"
"os"
"path/filepath"
@@ -93,6 +94,12 @@
os.Exit(1)
}
writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
+ starlarkDeps, err := starlark_import.GetNinjaDeps()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
+ ctx.AddNinjaFileDeps(starlarkDeps...)
return &res.metrics
}
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 0315732..89eac8a 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -996,3 +996,44 @@
},
})
}
+
+func TestCcBinaryHiddenVisibilityConvertedToFeature(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary changes hidden visibility to feature",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ cflags: ["-fvisibility=hidden"],
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `["visibility_hidden"]`,
+ }},
+ },
+ })
+}
+
+func TestCcBinaryHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+ runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+ description: "cc_binary changes hidden visibility to feature for specific os",
+ blueprint: `
+{rule_name} {
+ name: "foo",
+ target: {
+ android: {
+ cflags: ["-fvisibility=hidden"],
+ },
+ },
+}`,
+ targets: []testBazelTarget{
+ {"cc_binary", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["visibility_hidden"],
+ "//conditions:default": [],
+ })`,
+ }},
+ },
+ })
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index d2c463d..b61b0a7 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -4350,3 +4350,107 @@
},
})
}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeature(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library changes hidden visibility flag to feature",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ cflags: ["-fvisibility=hidden"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureSharedSpecific(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library changes hidden visibility flag to feature when specific to shared variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ shared: {
+ cflags: ["-fvisibility=hidden"],
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureStaticSpecific(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library changes hidden visibility flag to feature when specific to static variant",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ static: {
+ cflags: ["-fvisibility=hidden"],
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+ runCcLibraryTestCase(t, Bp2buildTestCase{
+ Description: "cc_library changes hidden visibility flag to feature when specific to an os",
+ ModuleTypeUnderTest: "cc_library",
+ ModuleTypeUnderTestFactory: cc.LibraryFactory,
+ Blueprint: `
+cc_library {
+ name: "foo",
+ target: {
+ android: {
+ cflags: ["-fvisibility=hidden"],
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["visibility_hidden"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["visibility_hidden"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index cbea943..f5d62c6 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -1250,3 +1250,107 @@
},
})
}
+
+func TestCcLibrarySharedHiddenVisibilityConvertedToFeature(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared changes hidden visibility flag to feature",
+ Blueprint: `
+cc_library_shared{
+ name: "foo",
+ cflags: ["-fvisibility=hidden"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared changes hidden visibility flag to feature for specific os",
+ Blueprint: `
+cc_library_shared{
+ name: "foo",
+ target: {
+ android: {
+ cflags: ["-fvisibility=hidden"],
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["visibility_hidden"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibrarySharedStubsDessertVersionConversion(t *testing.T) {
+ runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_shared converts dessert codename versions to numerical versions",
+ Blueprint: `
+cc_library_shared {
+ name: "a",
+ include_build_directory: false,
+ stubs: {
+ symbol_file: "a.map.txt",
+ versions: [
+ "Q",
+ "R",
+ "31",
+ ],
+ },
+}
+cc_library_shared {
+ name: "b",
+ include_build_directory: false,
+ stubs: {
+ symbol_file: "b.map.txt",
+ versions: [
+ "Q",
+ "R",
+ "31",
+ "current",
+ ],
+ },
+}
+`,
+ ExpectedBazelTargets: []string{
+ makeCcStubSuiteTargets("a", AttrNameToString{
+ "soname": `"a.so"`,
+ "source_library_label": `"//:a"`,
+ "stubs_symbol_file": `"a.map.txt"`,
+ "stubs_versions": `[
+ "29",
+ "30",
+ "31",
+ "current",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+ "stubs_symbol_file": `"a.map.txt"`,
+ }),
+ makeCcStubSuiteTargets("b", AttrNameToString{
+ "soname": `"b.so"`,
+ "source_library_label": `"//:b"`,
+ "stubs_symbol_file": `"b.map.txt"`,
+ "stubs_versions": `[
+ "29",
+ "30",
+ "31",
+ "current",
+ ]`,
+ }),
+ MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+ "stubs_symbol_file": `"b.map.txt"`,
+ }),
+ },
+ })
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index cd4cf51..f89f2dd 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -2032,3 +2032,44 @@
},
})
}
+
+func TestCcLibraryStaticHiddenVisibilityConvertedToFeature(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static changes hidden visibility flag to feature",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ cflags: ["-fvisibility=hidden"],
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `["visibility_hidden"]`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
+
+func TestCcLibraryStaticHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+ runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+ Description: "cc_library_static changes hidden visibility flag to feature for specific os",
+ Blueprint: `
+cc_library_static {
+ name: "foo",
+ target: {
+ android: {
+ cflags: ["-fvisibility=hidden"],
+ },
+ },
+}`,
+ ExpectedBazelTargets: []string{
+ MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+ "features": `select({
+ "//build/bazel/platforms/os:android": ["visibility_hidden"],
+ "//conditions:default": [],
+ })`,
+ "local_includes": `["."]`,
+ }),
+ },
+ })
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index c8f516c..ad9d702 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -67,6 +67,8 @@
Apex_available []string
+ Features bazel.StringListAttribute
+
sdkAttributes
tidyAttributes
@@ -226,7 +228,7 @@
attrs := staticOrSharedAttributes{}
setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
- attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
+ attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutHiddenVisibility))
attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
@@ -270,6 +272,8 @@
attrs.Apex_available = android.ConvertApexAvailableToTags(apexAvailable)
+ attrs.Features.Append(convertHiddenVisibilityToFeatureStaticOrShared(ctx, module, isStatic))
+
if !partitionedSrcs[protoSrcPartition].IsEmpty() {
// TODO(b/208815215): determine whether this is used and add support if necessary
ctx.ModuleErrorf("Migrating static/shared only proto srcs is not currently supported")
@@ -428,6 +432,12 @@
type filterOutFn func(string) bool
+// filterOutHiddenVisibility removes the flag specifying hidden visibility as
+// this flag is converted to a toolchain feature
+func filterOutHiddenVisibility(flag string) bool {
+ return flag == config.VisibilityHiddenFlag
+}
+
func filterOutStdFlag(flag string) bool {
return strings.HasPrefix(flag, "-std=")
}
@@ -490,7 +500,7 @@
// overridden. In Bazel we always allow overriding, via flags; however, this can cause
// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
// cases.
- ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags))
+ ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags, filterOutHiddenVisibility))
ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, filterOutClangUnknownCflags))
ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, filterOutClangUnknownCflags))
@@ -762,8 +772,13 @@
if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
if axis == bazel.NoConfigAxis {
- compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
- compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, libraryProps.Stubs.Versions)
+ if libraryProps.Stubs.Symbol_file != nil {
+ compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
+ versions := android.CopyOf(libraryProps.Stubs.Versions)
+ normalizeVersions(ctx, versions)
+ versions = addCurrentVersionIfNotPresent(versions)
+ compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, versions)
+ }
}
if suffix := libraryProps.Suffix; suffix != nil {
compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
@@ -833,6 +848,7 @@
features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
features = features.Append(bp2buildLtoFeatures(ctx, module))
+ features = features.Append(convertHiddenVisibilityToFeatureBase(ctx, module))
features.DeduplicateAxesFromBase()
addMuslSystemDynamicDeps(ctx, linkerAttrs)
@@ -1107,8 +1123,11 @@
// having stubs or not, so Bazel select() statement can be used to choose
// source/stub variants of them.
apexAvailable := module.ApexAvailable()
- setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0)
- setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1)
+ setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0, false)
+ setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1, false)
+ if len(systemSharedLibs) > 0 {
+ setStubsForDynamicDeps(ctx, axis, config, apexAvailable, bazelLabelForSharedDeps(ctx, systemSharedLibs), &la.systemDynamicDeps, 2, true)
+ }
}
if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
@@ -1178,7 +1197,7 @@
}
func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
- config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int) {
+ config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int, buildNonApexWithStubs bool) {
depsWithStubs := []bazel.Label{}
for _, l := range dynamicLibs.Includes {
@@ -1204,16 +1223,20 @@
inApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
nonApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
defaultSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+ nonApexDeps := depsWithStubs
+ if buildNonApexWithStubs {
+ nonApexDeps = stubLibLabels
+ }
if axis == bazel.NoConfigAxis {
(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
- (&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+ (&nonApexSelectValue).Append(bazel.MakeLabelList(nonApexDeps))
(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
} else if config == bazel.OsAndroid {
(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
- (&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
+ (&nonApexSelectValue).Append(bazel.MakeLabelList(nonApexDeps))
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
}
@@ -1547,3 +1570,38 @@
}
return ltoStringFeatures
}
+
+func convertHiddenVisibilityToFeatureBase(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute {
+ visibilityHiddenFeature := bazel.StringListAttribute{}
+ bp2BuildPropParseHelper(ctx, m, &BaseCompilerProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, baseCompilerProps.Cflags)
+ }
+ })
+ return visibilityHiddenFeature
+}
+
+func convertHiddenVisibilityToFeatureStaticOrShared(ctx android.BazelConversionPathContext, m *Module, isStatic bool) bazel.StringListAttribute {
+ visibilityHiddenFeature := bazel.StringListAttribute{}
+ if isStatic {
+ bp2BuildPropParseHelper(ctx, m, &StaticProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+ if staticProps, ok := props.(*StaticProperties); ok {
+ convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, staticProps.Static.Cflags)
+ }
+ })
+ } else {
+ bp2BuildPropParseHelper(ctx, m, &SharedProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+ if sharedProps, ok := props.(*SharedProperties); ok {
+ convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, sharedProps.Shared.Cflags)
+ }
+ })
+ }
+
+ return visibilityHiddenFeature
+}
+
+func convertHiddenVisibilityToFeatureHelper(feature *bazel.StringListAttribute, axis bazel.ConfigurationAxis, configString string, cflags []string) {
+ if inList(config.VisibilityHiddenFlag, cflags) {
+ feature.SetSelectValue(axis, configString, []string{"visibility_hidden"})
+ }
+}
diff --git a/cc/cc.go b/cc/cc.go
index 6054222..c1a1020 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1893,17 +1893,46 @@
// IsMixedBuildSupported returns true if the module should be analyzed by Bazel
// in any of the --bazel-mode(s).
func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
- // TODO(b/261058727): Remove this (enable mixed builds for modules with UBSan)
- // Currently we can only support ubsan when minimum runtime is used.
- return c.bazelHandler != nil && (!isUbsanEnabled(c) || c.MinimalRuntimeNeeded())
-}
-
-func isUbsanEnabled(c *Module) bool {
- if c.sanitize == nil {
+ if !allEnabledSanitizersSupportedByBazel(c) {
+ //TODO(b/278772861) support sanitizers in Bazel rules
return false
}
+ return c.bazelHandler != nil
+}
+
+func allEnabledSanitizersSupportedByBazel(c *Module) bool {
+ if c.sanitize == nil {
+ return true
+ }
sanitizeProps := &c.sanitize.Properties.SanitizeMutated
- return Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+
+ unsupportedSanitizers := []*bool{
+ sanitizeProps.Safestack,
+ sanitizeProps.Cfi,
+ sanitizeProps.Scudo,
+ BoolPtr(len(c.sanitize.Properties.Sanitize.Recover) > 0),
+ BoolPtr(c.sanitize.Properties.Sanitize.Blocklist != nil),
+ }
+ for _, san := range unsupportedSanitizers {
+ if Bool(san) {
+ return false
+ }
+ }
+
+ for _, san := range Sanitizers {
+ if san == intOverflow {
+ // TODO(b/261058727): enable mixed builds for all modules with UBSan
+ // Currently we can only support ubsan when minimum runtime is used.
+ ubsanEnabled := Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+ if ubsanEnabled && !c.MinimalRuntimeNeeded() {
+ return false
+ }
+ } else if c.sanitize.isSanitizerEnabled(san) {
+ return false
+ }
+ }
+
+ return true
}
func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
@@ -1947,6 +1976,17 @@
c.maybeInstall(mctx, apexInfo)
}
+func moduleContextFromAndroidModuleContext(actx android.ModuleContext, c *Module) ModuleContext {
+ ctx := &moduleContext{
+ ModuleContext: actx,
+ moduleContextImpl: moduleContextImpl{
+ mod: c,
+ },
+ }
+ ctx.ctx = ctx
+ return ctx
+}
+
func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
// Handle the case of a test module split by `test_per_src` mutator.
//
@@ -1966,13 +2006,7 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- ctx := &moduleContext{
- ModuleContext: actx,
- moduleContextImpl: moduleContextImpl{
- mod: c,
- },
- }
- ctx.ctx = ctx
+ ctx := moduleContextFromAndroidModuleContext(actx, c)
deps := c.depsToPaths(ctx)
if ctx.Failed() {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index b986511..fde6bff 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -59,7 +59,7 @@
func mixedBuildsPrepareMutator(ctx android.BottomUpMutatorContext) {
if m := ctx.Module(); m.Enabled() {
if mixedBuildMod, ok := m.(android.MixedBuildBuildable); ok {
- if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) {
+ if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) == android.MixedBuildEnabled {
mixedBuildMod.QueueBazelCall(ctx)
}
}
@@ -3114,6 +3114,11 @@
whole_static_libs: ["whole_static_dep"],
shared_libs: ["shared_dep"],
gtest: false,
+ sanitize: {
+ // cc_test modules default to memtag_heap: true,
+ // but this adds extra dependencies that we don't care about
+ never: true,
+ }
}
cc_binary {
name: "binary",
@@ -3573,9 +3578,6 @@
}
func TestStubsForLibraryInMultipleApexes(t *testing.T) {
- // TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
- // reenabled.
- t.Skip()
t.Parallel()
ctx := testCc(t, `
cc_library_shared {
@@ -3680,6 +3682,133 @@
}
}
+func TestMixedBuildUsesStubs(t *testing.T) {
+ // TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
+ // reenabled.
+ t.Skip()
+ t.Parallel()
+ bp := `
+ cc_library_shared {
+ name: "libFoo",
+ bazel_module: { label: "//:libFoo" },
+ srcs: ["foo.c"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["current"],
+ },
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libBar",
+ srcs: ["bar.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libA1",
+ srcs: ["a1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["a1"],
+ }
+
+ cc_library_shared {
+ name: "libBarA1",
+ srcs: ["bara1.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["bar", "a1"],
+ }
+
+ cc_library_shared {
+ name: "libAnyApex",
+ srcs: ["anyApex.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["//apex_available:anyapex"],
+ }
+
+ cc_library_shared {
+ name: "libBaz",
+ srcs: ["baz.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["baz"],
+ }
+
+ cc_library_shared {
+ name: "libQux",
+ srcs: ["qux.c"],
+ shared_libs: ["libFoo"],
+ apex_available: ["qux", "bar"],
+ }`
+
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "out/bazel",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//:libFoo": {
+ RootDynamicLibraries: []string{"libFoo.so"},
+ },
+ "//:libFoo_stub_libs-current": {
+ RootDynamicLibraries: []string{"libFoo_stub_libs-current.so"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp)
+ ctx := result.TestContext
+
+ variants := ctx.ModuleVariantsForTests("libFoo")
+ expectedVariants := []string{
+ "android_arm64_armv8-a_shared",
+ "android_arm64_armv8-a_shared_current",
+ "android_arm_armv7-a-neon_shared",
+ "android_arm_armv7-a-neon_shared_current",
+ }
+ variantsMismatch := false
+ if len(variants) != len(expectedVariants) {
+ variantsMismatch = true
+ } else {
+ for _, v := range expectedVariants {
+ if !inList(v, variants) {
+ variantsMismatch = false
+ }
+ }
+ }
+ if variantsMismatch {
+ t.Errorf("variants of libFoo expected:\n")
+ for _, v := range expectedVariants {
+ t.Errorf("%q\n", v)
+ }
+ t.Errorf(", but got:\n")
+ for _, v := range variants {
+ t.Errorf("%q\n", v)
+ }
+ }
+
+ linkAgainstFoo := []string{"libBarA1"}
+ linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
+
+ libFooPath := "out/bazel/execroot/__main__/libFoo.so"
+ for _, lib := range linkAgainstFoo {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
+ }
+ }
+
+ libFooStubPath := "out/bazel/execroot/__main__/libFoo_stub_libs-current.so"
+ for _, lib := range linkAgainstFooStubs {
+ libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
+ libFlags := libLinkRule.Args["libFlags"]
+ if !strings.Contains(libFlags, libFooStubPath) {
+ t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
+ }
+ }
+}
+
func TestVersioningMacro(t *testing.T) {
t.Parallel()
for _, tc := range []struct{ moduleName, expected string }{
@@ -5101,3 +5230,256 @@
expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
}
+
+func TestDisableSanitizerVariantsInMixedBuilds(t *testing.T) {
+ t.Parallel()
+ bp := `
+ cc_library_static {
+ name: "foo_ubsan_minimal",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_ubsan_minimal" },
+ sanitize: {
+ all_undefined: true,
+ integer_overflow: true,
+ },
+ }
+ cc_library_static {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo" },
+ sanitize: {
+ address: true,
+ hwaddress: true,
+ fuzzer: true,
+ integer_overflow: true,
+ scs: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_tsan",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_tsan" },
+ sanitize: {
+ thread: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_cfi",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_cfi" },
+ sanitize: {
+ cfi: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_memtag_stack",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_memtag_stack" },
+ sanitize: {
+ memtag_stack: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_memtag_heap",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_memtag_heap" },
+ sanitize: {
+ memtag_heap: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_safestack",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_safestack" },
+ sanitize: {
+ safestack: true,
+ },
+ }
+ cc_library_static {
+ name: "foo_scudo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo_scudo" },
+ sanitize: {
+ scudo: true,
+ },
+ }
+ `
+ testcases := []struct {
+ name string
+ variant string
+ expectedOutputPaths []string
+ }{
+ {
+ name: "foo_ubsan_minimal",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "outputbase/execroot/__main__/foo_ubsan_minimal.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "outputbase/execroot/__main__/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm_armv7-a-neon_static_asan_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm_armv7-a-neon_static_asan_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_hwasan_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_fuzzer_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_fuzzer_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm_armv7-a-neon_static_asan_fuzzer_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm_armv7-a-neon_static_asan_fuzzer_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_hwasan_fuzzer_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_fuzzer_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_scs_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_scs_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_hwasan_scs_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_scs_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo",
+ variant: "android_arm64_armv8-a_static_hwasan_scs_fuzzer_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_scs_fuzzer_apex28/foo.a",
+ },
+ },
+ {
+ name: "foo_tsan",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "outputbase/execroot/__main__/foo_tsan.a",
+ },
+ },
+ {
+ name: "foo_tsan",
+ variant: "android_arm64_armv8-a_static_tsan_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_tsan/android_arm64_armv8-a_static_tsan_apex28/foo_tsan.a",
+ },
+ },
+ {
+ name: "foo_cfi",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "outputbase/execroot/__main__/foo_cfi.a",
+ },
+ },
+ {
+ name: "foo_cfi",
+ variant: "android_arm64_armv8-a_static_cfi_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_cfi/android_arm64_armv8-a_static_cfi_apex28/foo_cfi.a",
+ },
+ },
+ {
+ name: "foo_memtag_stack",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_memtag_stack/android_arm64_armv8-a_static_apex28/foo_memtag_stack.a",
+ },
+ },
+ {
+ name: "foo_memtag_heap",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_memtag_heap/android_arm64_armv8-a_static_apex28/foo_memtag_heap.a",
+ },
+ },
+ {
+ name: "foo_safestack",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_safestack/android_arm64_armv8-a_static_apex28/foo_safestack.a",
+ },
+ },
+ {
+ name: "foo_scudo",
+ variant: "android_arm64_armv8-a_static_apex28",
+ expectedOutputPaths: []string{
+ "out/soong/.intermediates/foo_scudo/android_arm64_armv8-a_static_apex28/foo_scudo.a",
+ },
+ },
+ }
+
+ ctx := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForAsanTest,
+ android.FixtureRegisterWithContext(registerTestMutators),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo_ubsan_minimal": {
+ RootStaticArchives: []string{"foo_ubsan_minimal.a"},
+ },
+ "//foo": {
+ RootStaticArchives: []string{"foo.a"},
+ },
+ "//foo_tsan": {
+ RootStaticArchives: []string{"foo_tsan.a"},
+ },
+ "//foo_cfi": {
+ RootStaticArchives: []string{"foo_cfi.a"},
+ },
+ "//foo_memtag_stack": {
+ RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+ },
+ "//foo_memtag_heap": {
+ RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+ },
+ "//foo_safestack": {
+ RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+ },
+ "//foo_scudo": {
+ RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+ },
+ },
+ }
+ }),
+ ).RunTestWithBp(t, bp).TestContext
+
+ for _, tc := range testcases {
+ fooMod := ctx.ModuleForTests(tc.name, tc.variant).Module()
+ outputFiles, err := fooMod.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ android.AssertPathsRelativeToTopEquals(t, "output files", tc.expectedOutputPaths, outputFiles)
+ }
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index a2d5cf4..0c6e66d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -311,7 +311,7 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r487747"
+ ClangDefaultVersion = "clang-r487747b"
ClangDefaultShortVersion = "17"
// Directories with warnings from Android.bp files.
diff --git a/cc/library.go b/cc/library.go
index 172ca64..ee09389 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -311,6 +311,11 @@
asFlags = bazel.MakeStringListAttribute(nil)
}
+ sharedFeatures := baseAttributes.features.Clone().Append(sharedAttrs.Features)
+ sharedFeatures.DeduplicateAxesFromBase()
+ staticFeatures := baseAttributes.features.Clone().Append(staticAttrs.Features)
+ staticFeatures.DeduplicateAxesFromBase()
+
staticCommonAttrs := staticOrSharedAttributes{
Srcs: *srcs.Clone().Append(staticAttrs.Srcs),
Srcs_c: *compilerAttrs.cSrcs.Clone().Append(staticAttrs.Srcs_c),
@@ -366,7 +371,7 @@
Cpp_std: compilerAttrs.cppStd,
C_std: compilerAttrs.cStd,
- Features: baseAttributes.features,
+ Features: *staticFeatures,
}
sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
@@ -390,7 +395,7 @@
Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
- Features: baseAttributes.features,
+ Features: *sharedFeatures,
bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, m),
Fdo_profile: compilerAttrs.fdoProfile,
@@ -931,9 +936,17 @@
func (handler *ccLibraryBazelHandler) QueueBazelCall(ctx android.BaseModuleContext, label string) {
bazelCtx := ctx.Config().BazelContext
bazelCtx.QueueBazelRequest(label, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ if v := handler.module.library.stubsVersion(); v != "" {
+ stubsLabel := label + "_stub_libs-" + v
+ bazelCtx.QueueBazelRequest(stubsLabel, cquery.GetCcInfo, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
+ }
}
func (handler *ccLibraryBazelHandler) ProcessBazelQueryResponse(ctx android.ModuleContext, label string) {
+ if v := handler.module.library.stubsVersion(); v != "" {
+ // if we are a stubs variant, just use the Bazel stubs target
+ label = label + "_stub_libs-" + v
+ }
bazelCtx := ctx.Config().BazelContext
ccInfo, err := bazelCtx.GetCcInfo(label, android.GetConfigKeyApexVariant(ctx, GetApexConfigKey(ctx)))
if err != nil {
@@ -962,6 +975,9 @@
}
handler.module.setAndroidMkVariablesFromCquery(ccInfo.CcAndroidMkInfo)
+
+ cctx := moduleContextFromAndroidModuleContext(ctx, handler.module)
+ addStubDependencyProviders(cctx)
}
func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
@@ -1787,6 +1803,12 @@
Target: ctx.Target(),
})
+ addStubDependencyProviders(ctx)
+
+ return unstrippedOutputFile
+}
+
+func addStubDependencyProviders(ctx ModuleContext) {
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
if len(stubs) > 0 {
var stubsInfo []SharedStubLibrary
@@ -1801,12 +1823,9 @@
}
ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
SharedStubLibraries: stubsInfo,
-
- IsLLNDK: ctx.IsLlndk(),
+ IsLLNDK: ctx.IsLlndk(),
})
}
-
- return unstrippedOutputFile
}
func (library *libraryDecorator) unstrippedOutputFilePath() android.Path {
@@ -2392,7 +2411,10 @@
}
// Future API level is implicitly added if there isn't
- vers := library.Properties.Stubs.Versions
+ return addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+}
+
+func addCurrentVersionIfNotPresent(vers []string) []string {
if inList(android.FutureApiLevel.String(), vers) {
return vers
}
@@ -2657,7 +2679,7 @@
// normalizeVersions modifies `versions` in place, so that each raw version
// string becomes its normalized canonical form.
// Validates that the versions in `versions` are specified in least to greatest order.
-func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
+func normalizeVersions(ctx android.BazelConversionPathContext, versions []string) {
var previous android.ApiLevel
for i, v := range versions {
ver, err := android.ApiLevelFromUser(ctx, v)
@@ -2881,6 +2903,9 @@
asFlags = bazel.MakeStringListAttribute(nil)
}
+ features := baseAttributes.features.Clone().Append(libSharedOrStaticAttrs.Features)
+ features.DeduplicateAxesFromBase()
+
commonAttrs := staticOrSharedAttributes{
Srcs: compilerAttrs.srcs,
Srcs_c: compilerAttrs.cSrcs,
@@ -2922,7 +2947,7 @@
Conlyflags: compilerAttrs.conlyFlags,
Asflags: asFlags,
- Features: baseAttributes.features,
+ Features: *features,
}
} else {
commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
@@ -2951,7 +2976,7 @@
Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
- Features: baseAttributes.features,
+ Features: *features,
Suffix: compilerAttrs.suffix,
diff --git a/cc/util.go b/cc/util.go
index aa0f6b5..6d8ac43 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -100,17 +100,6 @@
"ln -sf " + target + " " + filepath.Join(dir, linkName)
}
-func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cat,
- Inputs: paths,
- Output: outPath,
- Description: "combine notices for " + out,
- })
- return outPath
-}
-
// Dump a map to a list file as:
//
// {key1} {value1}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index aed3775..e6e5660 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -160,7 +160,7 @@
MinSdkVersion string `json:",omitempty"`
}
-var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
/*
Vendor snapshot zipped artifacts directory structure for cc modules:
{SNAPSHOT_ARCH}/
@@ -195,10 +195,10 @@
*/
var snapshotOutputs android.Paths
+ var snapshotNotices android.Paths
includeDir := filepath.Join(snapshotArchDir, "include")
configsDir := filepath.Join(snapshotArchDir, "configs")
- noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
installedNotices := make(map[string]bool)
installedConfigs := make(map[string]bool)
@@ -228,7 +228,7 @@
prop := snapshotJsonFlags{}
// Common properties among snapshots.
- prop.ModuleName = ctx.ModuleName(m)
+ prop.InitBaseSnapshotPropsWithName(m, ctx.ModuleName(m))
if supportsVndkExt(s.Image) && m.IsVndkExt() {
// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
if m.IsVndkSp() {
@@ -406,13 +406,10 @@
headers = append(headers, m.SnapshotHeaders()...)
}
- if len(m.EffectiveLicenseFiles()) > 0 {
- noticeName := ctx.ModuleName(m) + ".txt"
- noticeOut := filepath.Join(noticeDir, noticeName)
- // skip already copied notice file
- if !installedNotices[noticeOut] {
- installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
+ for _, notice := range m.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotNotices = append(snapshotNotices, notice)
}
}
})
@@ -422,7 +419,7 @@
snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
}
- return snapshotOutputs
+ return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
}
func init() {
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 79a5ce4..53e0e55 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -85,7 +85,7 @@
flag.BoolVar(&cmdlineArgs.BazelModeDev, "bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
flag.BoolVar(&cmdlineArgs.UseBazelProxy, "use-bazel-proxy", false, "communicate with bazel using unix socket proxy instead of spawning subprocesses")
flag.BoolVar(&cmdlineArgs.BuildFromTextStub, "build-from-text-stub", false, "build Java stubs from API text files instead of source files")
-
+ flag.BoolVar(&cmdlineArgs.EnsureAllowlistIntegrity, "ensure-allowlist-integrity", false, "verify that allowlisted modules are mixed-built")
// Flags that probably shouldn't be flags of soong_build, but we haven't found
// the time to remove them yet
flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
@@ -288,6 +288,46 @@
maybeQuit(err, "error writing soong_build metrics %s", metricsFile)
}
+// Errors out if any modules expected to be mixed_built were not, unless
+// there is a platform incompatibility.
+func checkForAllowlistIntegrityError(configuration android.Config, isStagingMode bool) error {
+ modules := findModulesNotMixedBuiltForAnyVariant(configuration, isStagingMode)
+ if len(modules) == 0 {
+ return nil
+ }
+
+ return fmt.Errorf("Error: expected the following modules to be mixed_built: %s", modules)
+}
+
+// Returns the list of modules that should have been mixed_built (per the
+// allowlists and cmdline flags) but were not.
+func findModulesNotMixedBuiltForAnyVariant(configuration android.Config, isStagingMode bool) []string {
+ retval := []string{}
+ forceEnabledModules := configuration.BazelModulesForceEnabledByFlag()
+
+ mixedBuildsEnabled := configuration.GetMixedBuildsEnabledModules()
+ for _, module := range allowlists.ProdMixedBuildsEnabledList {
+ if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+ retval = append(retval, module)
+ }
+ }
+
+ if isStagingMode {
+ for _, module := range allowlists.StagingMixedBuildsEnabledList {
+ if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+ retval = append(retval, module)
+ }
+ }
+ }
+
+ for module, _ := range forceEnabledModules {
+ if _, ok := mixedBuildsEnabled[module]; !ok && module != "" {
+ retval = append(retval, module)
+ }
+ }
+ return retval
+}
+
func writeJsonModuleGraphAndActions(ctx *android.Context, cmdArgs android.CmdArgs) {
graphFile, graphErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleGraphFile))
maybeQuit(graphErr, "graph err")
@@ -433,8 +473,14 @@
writeMetrics(configuration, ctx.EventHandler, metricsDir)
default:
ctx.Register()
- if configuration.IsMixedBuildsEnabled() {
+ isMixedBuildsEnabled := configuration.IsMixedBuildsEnabled()
+ if isMixedBuildsEnabled {
finalOutputFile = runMixedModeBuild(ctx, extraNinjaDeps)
+ if cmdlineArgs.EnsureAllowlistIntegrity {
+ if err := checkForAllowlistIntegrityError(configuration, cmdlineArgs.BazelModeStaging); err != nil {
+ maybeQuit(err, "")
+ }
+ }
} else {
finalOutputFile = runSoongOnlyBuild(ctx, extraNinjaDeps)
}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index ce32184..67cb6cf 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -15,6 +15,7 @@
package main
import (
+ "android/soong/starlark_import"
"io/fs"
"io/ioutil"
"os"
@@ -47,6 +48,14 @@
}
}
+ // Add starlark deps here, so that they apply to both queryview and apibp2build which
+ // both run this function.
+ starlarkDeps, err2 := starlark_import.GetNinjaDeps()
+ if err2 != nil {
+ return err2
+ }
+ ctx.AddNinjaFileDeps(starlarkDeps...)
+
return nil
}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index dcd7fdc..6817dce 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -617,7 +617,7 @@
return true
}
-func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
/*
Snapshot zipped artifacts directory structure for etc modules:
{SNAPSHOT_ARCH}/
@@ -631,7 +631,7 @@
(notice files)
*/
var snapshotOutputs android.Paths
- noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+ var snapshotNotices android.Paths
installedNotices := make(map[string]bool)
ctx.VisitAllModules(func(module android.Module) {
@@ -651,7 +651,7 @@
prop := snapshot.SnapshotJsonFlags{}
propOut := snapshotLibOut + ".json"
- prop.ModuleName = m.BaseModuleName()
+ prop.InitBaseSnapshotProps(m)
if m.subdirProperties.Relative_install_path != nil {
prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
}
@@ -667,27 +667,16 @@
}
snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
- if len(m.EffectiveLicenseFiles()) > 0 {
- noticeName := ctx.ModuleName(m) + ".txt"
- noticeOut := filepath.Join(noticeDir, noticeName)
- // skip already copied notice file
- if !installedNotices[noticeOut] {
- installedNotices[noticeOut] = true
-
- noticeOutPath := android.PathForOutput(ctx, noticeOut)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cat,
- Inputs: m.EffectiveLicenseFiles(),
- Output: noticeOutPath,
- Description: "combine notices for " + noticeOut,
- })
- snapshotOutputs = append(snapshotOutputs, noticeOutPath)
+ for _, notice := range m.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotNotices = append(snapshotNotices, notice)
}
}
})
- return snapshotOutputs
+ return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
}
// For Bazel / bp2build
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 79d2412..f76529d 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -339,7 +339,7 @@
// List of modules for monitoring coverage drops in directories (e.g. "libicu")
Target_modules []string `json:"target_modules,omitempty"`
// Specifies a bug assignee to replace default ISE assignment
- Assignee string `json:"assignee,omitempty"`
+ Triage_assignee string `json:"triage_assignee,omitempty"`
}
type FuzzFrameworks struct {
diff --git a/go.mod b/go.mod
index a5d9dd5..4a511c5 100644
--- a/go.mod
+++ b/go.mod
@@ -6,4 +6,5 @@
github.com/google/blueprint v0.0.0
google.golang.org/protobuf v0.0.0
prebuilts/bazel/common/proto/analysis_v2 v0.0.0
+ go.starlark.net v0.0.0
)
diff --git a/go.work b/go.work
index 737a9df..67f6549 100644
--- a/go.work
+++ b/go.work
@@ -4,6 +4,7 @@
.
../../external/go-cmp
../../external/golang-protobuf
+ ../../external/starlark-go
../../prebuilts/bazel/common/proto/analysis_v2
../../prebuilts/bazel/common/proto/build
../blueprint
@@ -16,4 +17,5 @@
google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
+ go.starlark.net v0.0.0 => ../../external/starlark-go
)
diff --git a/java/base.go b/java/base.go
index 9911323..374138c 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1923,9 +1923,12 @@
func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
switch name {
- case android.SdkCore.JavaLibraryName(ctx.Config()), "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
+ case android.SdkCore.JavaLibraryName(ctx.Config()),
+ android.JavaApiLibraryName(ctx.Config(), "legacy.core.platform.api.stubs"),
+ android.JavaApiLibraryName(ctx.Config(), "stable.core.platform.api.stubs"),
"stub-annotations", "private-stub-annotations-jar",
- "core-lambda-stubs", "core-generated-annotation-stubs":
+ android.JavaApiLibraryName(ctx.Config(), "core-lambda-stubs"),
+ "core-generated-annotation-stubs":
return javaCore, true
case android.SdkPublic.JavaLibraryName(ctx.Config()):
return javaSdk, true
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 273aca0..d383d98 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -28,8 +28,11 @@
ctx.Strict("FRAMEWORK_LIBRARIES", strings.Join(FrameworkLibraries, " "))
// These are used by make when LOCAL_PRIVATE_PLATFORM_APIS is set (equivalent to platform_apis in blueprint):
- ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES", strings.Join(LegacyCorePlatformBootclasspathLibraries, " "))
- ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES", LegacyCorePlatformSystemModules)
+ ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES",
+ strings.Join(android.JavaApiLibraryNames(ctx.Config(), LegacyCorePlatformBootclasspathLibraries), " "))
+ ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES",
+ android.JavaApiLibraryName(ctx.Config(), LegacyCorePlatformSystemModules),
+ )
ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index c6176e6..96e084a 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -241,7 +241,7 @@
testStubModules = append(testStubModules, android.SdkTest.JavaLibraryName(config))
}
// We do not have prebuilts of the core platform api yet
- corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
+ corePlatformStubModules = append(corePlatformStubModules, android.JavaApiLibraryName(config, "legacy.core.platform.api.stubs"))
// Allow products to define their own stubs for custom product jars that apps can use.
publicStubModules = append(publicStubModules, config.ProductHiddenAPIStubs()...)
diff --git a/java/java.go b/java/java.go
index 2de4ea9..fe4df75 100644
--- a/java/java.go
+++ b/java/java.go
@@ -456,7 +456,9 @@
ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
- ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
+ ctx.AddVariationDependencies(nil, proguardRaiseTag,
+ android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)...,
+ )
}
if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 6cb549e..04c6d05 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -93,16 +93,16 @@
func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
- return config.LegacyCorePlatformSystemModules
+ return android.JavaApiLibraryName(ctx.Config(), config.LegacyCorePlatformSystemModules)
} else {
- return config.StableCorePlatformSystemModules
+ return android.JavaApiLibraryName(ctx.Config(), config.StableCorePlatformSystemModules)
}
}
func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
- return config.LegacyCorePlatformBootclasspathLibraries
+ return android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)
} else {
- return config.StableCorePlatformBootclasspathLibraries
+ return android.JavaApiLibraryNames(ctx.Config(), config.StableCorePlatformBootclasspathLibraries)
}
}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 7267cf3..dee72ed 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -923,7 +923,10 @@
license_kind {
name: "SPDX-license-identifier-Linux-syscall-note",
// expanding visibility requires approval from an OSPO lawyer or pcounsel
- visibility: ["//external/libbpf:__subpackages__"],
+ visibility: [
+ "//external/libbpf:__subpackages__",
+ "//prebuilts/vsdk:__subpackages__",
+ ],
conditions: ["permissive"],
url: "https://spdx.org/licenses/Linux-syscall-note.html",
}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 9793218..edcc163 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -96,6 +96,7 @@
var jsonData []SnapshotJsonFlags
var metaPaths android.Paths
+ installedNotices := make(map[string]bool)
metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
// Create JSON file based on the direct dependencies
@@ -104,12 +105,14 @@
if desc != nil {
jsonData = append(jsonData, *desc)
}
- if len(dep.EffectiveLicenseFiles()) > 0 {
- noticeFile := android.PathForModuleOut(ctx, "NOTICE_FILES", dep.Name()+".txt").OutputPath
- android.CatFileRule(ctx, dep.EffectiveLicenseFiles(), noticeFile)
- metaPaths = append(metaPaths, noticeFile)
+ for _, notice := range dep.EffectiveLicenseFiles() {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath
+ CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut)
+ metaPaths = append(metaPaths, noticeOut)
+ }
}
-
})
// Sort notice paths and json data for repeatble build
sort.Slice(jsonData, func(i, j int) bool {
@@ -220,8 +223,7 @@
}
if path.Valid() && path.String() != "" {
- return &SnapshotJsonFlags{
- ModuleName: m.Name(),
+ props := &SnapshotJsonFlags{
ModuleStemName: moduleStem,
Filename: path.String(),
Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
@@ -229,6 +231,8 @@
RustProcMacro: procMacro,
CrateName: crateName,
}
+ props.InitBaseSnapshotProps(m)
+ return props
}
return nil
}
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
index 206ecc9..c95a537 100644
--- a/snapshot/snapshot.go
+++ b/snapshot/snapshot.go
@@ -26,6 +26,10 @@
var pctx = android.NewPackageContext("android/soong/snapshot")
+func init() {
+ pctx.Import("android/soong/android")
+}
+
type SnapshotSingleton struct {
// Name, e.g., "vendor", "recovery", "ramdisk".
name string
@@ -48,8 +52,18 @@
Fake bool
}
+// The output files to be included in the snapshot.
+type SnapshotPaths struct {
+ // All files to be included in the snapshot
+ OutputFiles android.Paths
+
+ // Notice files of the snapshot output files
+ NoticeFiles android.Paths
+}
+
// Interface of function to capture snapshot from each module
-type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths
+// Returns snapshot ouputs and notice files.
+type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) SnapshotPaths
var snapshotActionList []GenerateSnapshotAction
@@ -74,9 +88,19 @@
snapshotDir = filepath.Join("fake", snapshotDir)
}
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
+ noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+ installedNotices := make(map[string]bool)
for _, f := range snapshotActionList {
- snapshotOutputs = append(snapshotOutputs, f(*c, ctx, snapshotArchDir)...)
+ snapshotPaths := f(*c, ctx, snapshotArchDir)
+ snapshotOutputs = append(snapshotOutputs, snapshotPaths.OutputFiles...)
+ for _, notice := range snapshotPaths.NoticeFiles {
+ if _, ok := installedNotices[notice.String()]; !ok {
+ installedNotices[notice.String()] = true
+ snapshotOutputs = append(snapshotOutputs, CopyFileRule(
+ pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
+ }
+ }
}
// All artifacts are ready. Sort them to normalize ninja and then zip.
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 809ca3d..fb4ee0c 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -120,4 +120,19 @@
// dependencies
Required []string `json:",omitempty"`
Overrides []string `json:",omitempty"`
+
+ // license information
+ LicenseKinds []string `json:",omitempty"`
+ LicenseTexts []string `json:",omitempty"`
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotPropsWithName(m android.Module, name string) {
+ prop.ModuleName = name
+
+ prop.LicenseKinds = m.EffectiveLicenseKinds()
+ prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotProps(m android.Module) {
+ prop.InitBaseSnapshotPropsWithName(m, m.Name())
}
diff --git a/snapshot/util.go b/snapshot/util.go
index 806ac90..c87c508 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -21,17 +21,25 @@
return outPath
}
-func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
+type buildContext interface {
+ Build(pctx android.PackageContext, params android.BuildParams)
+}
+
+func CopyFileToOutputPathRule(pctx android.PackageContext, ctx buildContext, path android.Path, outPath android.OutputPath) {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: path,
Output: outPath,
- Description: "copy " + path.String() + " -> " + out,
+ Description: "copy " + path.String() + " -> " + outPath.String(),
Args: map[string]string{
- "cpFlags": "-f -L",
+ "cpFlags": "-L",
},
})
+}
+
+func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ outPath := android.PathForOutput(ctx, out)
+ CopyFileToOutputPathRule(pctx, ctx, path, outPath)
return outPath
}
diff --git a/starlark_import/Android.bp b/starlark_import/Android.bp
new file mode 100644
index 0000000..b43217b
--- /dev/null
+++ b/starlark_import/Android.bp
@@ -0,0 +1,36 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-starlark",
+ pkgPath: "android/soong/starlark_import",
+ srcs: [
+ "starlark_import.go",
+ "unmarshal.go",
+ ],
+ testSrcs: [
+ "starlark_import_test.go",
+ "unmarshal_test.go",
+ ],
+ deps: [
+ "go-starlark-starlark",
+ "go-starlark-starlarkstruct",
+ "go-starlark-starlarkjson",
+ "go-starlark-starlarktest",
+ ],
+}
diff --git a/starlark_import/README.md b/starlark_import/README.md
new file mode 100644
index 0000000..e444759
--- /dev/null
+++ b/starlark_import/README.md
@@ -0,0 +1,14 @@
+# starlark_import package
+
+This allows soong to read constant information from starlark files. At package initialization
+time, soong will read `build/bazel/constants_exported_to_soong.bzl`, and then make the
+variables from that file available via `starlark_import.GetStarlarkValue()`. So to import
+a new variable, it must be added to `constants_exported_to_soong.bzl` and then it can
+be accessed by name.
+
+Only constant information can be read, since this is not a full bazel execution but a
+standalone starlark interpreter. This means you can't use bazel contructs like `rule`,
+`provider`, `select`, `glob`, etc.
+
+All starlark files that were loaded must be added as ninja deps that cause soong to rerun.
+The loaded files can be retrieved via `starlark_import.GetNinjaDeps()`.
diff --git a/starlark_import/starlark_import.go b/starlark_import/starlark_import.go
new file mode 100644
index 0000000..ebe4247
--- /dev/null
+++ b/starlark_import/starlark_import.go
@@ -0,0 +1,306 @@
+// 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 starlark_import
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "go.starlark.net/starlark"
+ "go.starlark.net/starlarkjson"
+ "go.starlark.net/starlarkstruct"
+)
+
+func init() {
+ go func() {
+ startTime := time.Now()
+ v, d, err := runStarlarkFile("//build/bazel/constants_exported_to_soong.bzl")
+ endTime := time.Now()
+ //fmt.Fprintf(os.Stderr, "starlark run time: %s\n", endTime.Sub(startTime).String())
+ globalResult.Set(starlarkResult{
+ values: v,
+ ninjaDeps: d,
+ err: err,
+ startTime: startTime,
+ endTime: endTime,
+ })
+ }()
+}
+
+type starlarkResult struct {
+ values starlark.StringDict
+ ninjaDeps []string
+ err error
+ startTime time.Time
+ endTime time.Time
+}
+
+// setOnce wraps a value and exposes Set() and Get() accessors for it.
+// The Get() calls will block until a Set() has been called.
+// A second call to Set() will panic.
+// setOnce must be created using newSetOnce()
+type setOnce[T any] struct {
+ value T
+ lock sync.Mutex
+ wg sync.WaitGroup
+ isSet bool
+}
+
+func (o *setOnce[T]) Set(value T) {
+ o.lock.Lock()
+ defer o.lock.Unlock()
+ if o.isSet {
+ panic("Value already set")
+ }
+
+ o.value = value
+ o.isSet = true
+ o.wg.Done()
+}
+
+func (o *setOnce[T]) Get() T {
+ if !o.isSet {
+ o.wg.Wait()
+ }
+ return o.value
+}
+
+func newSetOnce[T any]() *setOnce[T] {
+ result := &setOnce[T]{}
+ result.wg.Add(1)
+ return result
+}
+
+var globalResult = newSetOnce[starlarkResult]()
+
+func GetStarlarkValue[T any](key string) (T, error) {
+ result := globalResult.Get()
+ if result.err != nil {
+ var zero T
+ return zero, result.err
+ }
+ if !result.values.Has(key) {
+ var zero T
+ return zero, fmt.Errorf("a starlark variable by that name wasn't found, did you update //build/bazel/constants_exported_to_soong.bzl?")
+ }
+ return Unmarshal[T](result.values[key])
+}
+
+func GetNinjaDeps() ([]string, error) {
+ result := globalResult.Get()
+ if result.err != nil {
+ return nil, result.err
+ }
+ return result.ninjaDeps, nil
+}
+
+func getTopDir() (string, error) {
+ // It's hard to communicate the top dir to this package in any other way than reading the
+ // arguments directly, because we need to know this at package initialization time. Many
+ // soong constants that we'd like to read from starlark are initialized during package
+ // initialization.
+ for i, arg := range os.Args {
+ if arg == "--top" {
+ if i < len(os.Args)-1 && os.Args[i+1] != "" {
+ return os.Args[i+1], nil
+ }
+ }
+ }
+
+ // When running tests, --top is not passed. Instead, search for the top dir manually
+ cwd, err := os.Getwd()
+ if err != nil {
+ return "", err
+ }
+ for cwd != "/" {
+ if _, err := os.Stat(filepath.Join(cwd, "build/soong/soong_ui.bash")); err == nil {
+ return cwd, nil
+ }
+ cwd = filepath.Dir(cwd)
+ }
+ return "", fmt.Errorf("could not find top dir")
+}
+
+const callerDirKey = "callerDir"
+
+type modentry struct {
+ globals starlark.StringDict
+ err error
+}
+
+func unsupportedMethod(t *starlark.Thread, fn *starlark.Builtin, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) {
+ return nil, fmt.Errorf("%sthis file is read by soong, and must therefore be pure starlark and include only constant information. %q is not allowed", t.CallStack().String(), fn.Name())
+}
+
+var builtins = starlark.StringDict{
+ "aspect": starlark.NewBuiltin("aspect", unsupportedMethod),
+ "glob": starlark.NewBuiltin("glob", unsupportedMethod),
+ "json": starlarkjson.Module,
+ "provider": starlark.NewBuiltin("provider", unsupportedMethod),
+ "rule": starlark.NewBuiltin("rule", unsupportedMethod),
+ "struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
+ "select": starlark.NewBuiltin("select", unsupportedMethod),
+ "transition": starlark.NewBuiltin("transition", unsupportedMethod),
+}
+
+// Takes a module name (the first argument to the load() function) and returns the path
+// it's trying to load, stripping out leading //, and handling leading :s.
+func cleanModuleName(moduleName string, callerDir string) (string, error) {
+ if strings.Count(moduleName, ":") > 1 {
+ return "", fmt.Errorf("at most 1 colon must be present in starlark path: %s", moduleName)
+ }
+
+ // We don't have full support for external repositories, but at least support skylib's dicts.
+ if moduleName == "@bazel_skylib//lib:dicts.bzl" {
+ return "external/bazel-skylib/lib/dicts.bzl", nil
+ }
+
+ localLoad := false
+ if strings.HasPrefix(moduleName, "@//") {
+ moduleName = moduleName[3:]
+ } else if strings.HasPrefix(moduleName, "//") {
+ moduleName = moduleName[2:]
+ } else if strings.HasPrefix(moduleName, ":") {
+ moduleName = moduleName[1:]
+ localLoad = true
+ } else {
+ return "", fmt.Errorf("load path must start with // or :")
+ }
+
+ if ix := strings.LastIndex(moduleName, ":"); ix >= 0 {
+ moduleName = moduleName[:ix] + string(os.PathSeparator) + moduleName[ix+1:]
+ }
+
+ if filepath.Clean(moduleName) != moduleName {
+ return "", fmt.Errorf("load path must be clean, found: %s, expected: %s", moduleName, filepath.Clean(moduleName))
+ }
+ if strings.HasPrefix(moduleName, "../") {
+ return "", fmt.Errorf("load path must not start with ../: %s", moduleName)
+ }
+ if strings.HasPrefix(moduleName, "/") {
+ return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName)
+ }
+
+ if localLoad {
+ return filepath.Join(callerDir, moduleName), nil
+ }
+
+ return moduleName, nil
+}
+
+// loader implements load statement. The format of the loaded module URI is
+//
+// [//path]:base
+//
+// The file path is $ROOT/path/base if path is present, <caller_dir>/base otherwise.
+func loader(thread *starlark.Thread, module string, topDir string, moduleCache map[string]*modentry, moduleCacheLock *sync.Mutex, filesystem map[string]string) (starlark.StringDict, error) {
+ modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string))
+ if err != nil {
+ return nil, err
+ }
+ moduleCacheLock.Lock()
+ e, ok := moduleCache[modulePath]
+ if e == nil {
+ if ok {
+ moduleCacheLock.Unlock()
+ return nil, fmt.Errorf("cycle in load graph")
+ }
+
+ // Add a placeholder to indicate "load in progress".
+ moduleCache[modulePath] = nil
+ moduleCacheLock.Unlock()
+
+ childThread := &starlark.Thread{Name: "exec " + module, Load: thread.Load}
+
+ // Cheating for the sake of testing:
+ // propagate starlarktest's Reporter key, otherwise testing
+ // the load function may cause panic in starlarktest code.
+ const testReporterKey = "Reporter"
+ if v := thread.Local(testReporterKey); v != nil {
+ childThread.SetLocal(testReporterKey, v)
+ }
+
+ childThread.SetLocal(callerDirKey, filepath.Dir(modulePath))
+
+ if filesystem != nil {
+ globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), filesystem[modulePath], builtins)
+ e = &modentry{globals, err}
+ } else {
+ globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), nil, builtins)
+ e = &modentry{globals, err}
+ }
+
+ // Update the cache.
+ moduleCacheLock.Lock()
+ moduleCache[modulePath] = e
+ }
+ moduleCacheLock.Unlock()
+ return e.globals, e.err
+}
+
+// Run runs the given starlark file and returns its global variables and a list of all starlark
+// files that were loaded. The top dir for starlark's // is found via getTopDir().
+func runStarlarkFile(filename string) (starlark.StringDict, []string, error) {
+ topDir, err := getTopDir()
+ if err != nil {
+ return nil, nil, err
+ }
+ return runStarlarkFileWithFilesystem(filename, topDir, nil)
+}
+
+func runStarlarkFileWithFilesystem(filename string, topDir string, filesystem map[string]string) (starlark.StringDict, []string, error) {
+ if !strings.HasPrefix(filename, "//") && !strings.HasPrefix(filename, ":") {
+ filename = "//" + filename
+ }
+ filename, err := cleanModuleName(filename, "")
+ if err != nil {
+ return nil, nil, err
+ }
+ moduleCache := make(map[string]*modentry)
+ moduleCache[filename] = nil
+ moduleCacheLock := &sync.Mutex{}
+ mainThread := &starlark.Thread{
+ Name: "main",
+ Print: func(_ *starlark.Thread, msg string) {
+ // Ignore prints
+ },
+ Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
+ return loader(thread, module, topDir, moduleCache, moduleCacheLock, filesystem)
+ },
+ }
+ mainThread.SetLocal(callerDirKey, filepath.Dir(filename))
+
+ var result starlark.StringDict
+ if filesystem != nil {
+ result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), filesystem[filename], builtins)
+ } else {
+ result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), nil, builtins)
+ }
+ return result, sortedStringKeys(moduleCache), err
+}
+
+func sortedStringKeys(m map[string]*modentry) []string {
+ s := make([]string, 0, len(m))
+ for k := range m {
+ s = append(s, k)
+ }
+ sort.Strings(s)
+ return s
+}
diff --git a/starlark_import/starlark_import_test.go b/starlark_import/starlark_import_test.go
new file mode 100644
index 0000000..8a58e3b
--- /dev/null
+++ b/starlark_import/starlark_import_test.go
@@ -0,0 +1,122 @@
+// 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 starlark_import
+
+import (
+ "strings"
+ "testing"
+
+ "go.starlark.net/starlark"
+)
+
+func TestBasic(t *testing.T) {
+ globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+ "a.bzl": `
+my_string = "hello, world!"
+`})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if globals["my_string"].(starlark.String) != "hello, world!" {
+ t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+ }
+}
+
+func TestLoad(t *testing.T) {
+ globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+ "a.bzl": `
+load("//b.bzl", _b_string = "my_string")
+my_string = "hello, " + _b_string
+`,
+ "b.bzl": `
+my_string = "world!"
+`})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if globals["my_string"].(starlark.String) != "hello, world!" {
+ t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+ }
+}
+
+func TestLoadRelative(t *testing.T) {
+ globals, ninjaDeps, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+ "a.bzl": `
+load(":b.bzl", _b_string = "my_string")
+load("//foo/c.bzl", _c_string = "my_string")
+my_string = "hello, " + _b_string
+c_string = _c_string
+`,
+ "b.bzl": `
+my_string = "world!"
+`,
+ "foo/c.bzl": `
+load(":d.bzl", _d_string = "my_string")
+my_string = "hello, " + _d_string
+`,
+ "foo/d.bzl": `
+my_string = "world!"
+`})
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if globals["my_string"].(starlark.String) != "hello, world!" {
+ t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+ }
+
+ expectedNinjaDeps := []string{
+ "a.bzl",
+ "b.bzl",
+ "foo/c.bzl",
+ "foo/d.bzl",
+ }
+ if !slicesEqual(ninjaDeps, expectedNinjaDeps) {
+ t.Errorf("Expected %v ninja deps, got %v", expectedNinjaDeps, ninjaDeps)
+ }
+}
+
+func TestLoadCycle(t *testing.T) {
+ _, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+ "a.bzl": `
+load(":b.bzl", _b_string = "my_string")
+my_string = "hello, " + _b_string
+`,
+ "b.bzl": `
+load(":a.bzl", _a_string = "my_string")
+my_string = "hello, " + _a_string
+`})
+ if err == nil || !strings.Contains(err.Error(), "cycle in load graph") {
+ t.Errorf("Expected cycle in load graph, got: %v", err)
+ return
+ }
+}
+
+func slicesEqual[T comparable](a []T, b []T) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/starlark_import/unmarshal.go b/starlark_import/unmarshal.go
new file mode 100644
index 0000000..1b54437
--- /dev/null
+++ b/starlark_import/unmarshal.go
@@ -0,0 +1,288 @@
+// 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 starlark_import
+
+import (
+ "fmt"
+ "math"
+ "reflect"
+ "unsafe"
+
+ "go.starlark.net/starlark"
+ "go.starlark.net/starlarkstruct"
+)
+
+func Unmarshal[T any](value starlark.Value) (T, error) {
+ var zero T
+ x, err := UnmarshalReflect(value, reflect.TypeOf(zero))
+ return x.Interface().(T), err
+}
+
+func UnmarshalReflect(value starlark.Value, ty reflect.Type) (reflect.Value, error) {
+ zero := reflect.Zero(ty)
+ var result reflect.Value
+ if ty.Kind() == reflect.Interface {
+ var err error
+ ty, err = typeOfStarlarkValue(value)
+ if err != nil {
+ return zero, err
+ }
+ }
+ if ty.Kind() == reflect.Map {
+ result = reflect.MakeMap(ty)
+ } else {
+ result = reflect.Indirect(reflect.New(ty))
+ }
+
+ switch v := value.(type) {
+ case starlark.String:
+ if result.Type().Kind() != reflect.String {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ result.SetString(v.GoString())
+ case starlark.Int:
+ signedValue, signedOk := v.Int64()
+ unsignedValue, unsignedOk := v.Uint64()
+ switch result.Type().Kind() {
+ case reflect.Int64:
+ if !signedOk {
+ return zero, fmt.Errorf("starlark int didn't fit in go int64")
+ }
+ result.SetInt(signedValue)
+ case reflect.Int32:
+ if !signedOk || signedValue > math.MaxInt32 || signedValue < math.MinInt32 {
+ return zero, fmt.Errorf("starlark int didn't fit in go int32")
+ }
+ result.SetInt(signedValue)
+ case reflect.Int16:
+ if !signedOk || signedValue > math.MaxInt16 || signedValue < math.MinInt16 {
+ return zero, fmt.Errorf("starlark int didn't fit in go int16")
+ }
+ result.SetInt(signedValue)
+ case reflect.Int8:
+ if !signedOk || signedValue > math.MaxInt8 || signedValue < math.MinInt8 {
+ return zero, fmt.Errorf("starlark int didn't fit in go int8")
+ }
+ result.SetInt(signedValue)
+ case reflect.Int:
+ if !signedOk || signedValue > math.MaxInt || signedValue < math.MinInt {
+ return zero, fmt.Errorf("starlark int didn't fit in go int")
+ }
+ result.SetInt(signedValue)
+ case reflect.Uint64:
+ if !unsignedOk {
+ return zero, fmt.Errorf("starlark int didn't fit in go uint64")
+ }
+ result.SetUint(unsignedValue)
+ case reflect.Uint32:
+ if !unsignedOk || unsignedValue > math.MaxUint32 {
+ return zero, fmt.Errorf("starlark int didn't fit in go uint32")
+ }
+ result.SetUint(unsignedValue)
+ case reflect.Uint16:
+ if !unsignedOk || unsignedValue > math.MaxUint16 {
+ return zero, fmt.Errorf("starlark int didn't fit in go uint16")
+ }
+ result.SetUint(unsignedValue)
+ case reflect.Uint8:
+ if !unsignedOk || unsignedValue > math.MaxUint8 {
+ return zero, fmt.Errorf("starlark int didn't fit in go uint8")
+ }
+ result.SetUint(unsignedValue)
+ case reflect.Uint:
+ if !unsignedOk || unsignedValue > math.MaxUint {
+ return zero, fmt.Errorf("starlark int didn't fit in go uint")
+ }
+ result.SetUint(unsignedValue)
+ default:
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ case starlark.Float:
+ f := float64(v)
+ switch result.Type().Kind() {
+ case reflect.Float64:
+ result.SetFloat(f)
+ case reflect.Float32:
+ if f > math.MaxFloat32 || f < -math.MaxFloat32 {
+ return zero, fmt.Errorf("starlark float didn't fit in go float32")
+ }
+ result.SetFloat(f)
+ default:
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ case starlark.Bool:
+ if result.Type().Kind() != reflect.Bool {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ result.SetBool(bool(v))
+ case starlark.Tuple:
+ if result.Type().Kind() != reflect.Slice {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ elemType := result.Type().Elem()
+ // TODO: Add this grow call when we're on go 1.20
+ //result.Grow(v.Len())
+ for i := 0; i < v.Len(); i++ {
+ elem, err := UnmarshalReflect(v.Index(i), elemType)
+ if err != nil {
+ return zero, err
+ }
+ result = reflect.Append(result, elem)
+ }
+ case *starlark.List:
+ if result.Type().Kind() != reflect.Slice {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ elemType := result.Type().Elem()
+ // TODO: Add this grow call when we're on go 1.20
+ //result.Grow(v.Len())
+ for i := 0; i < v.Len(); i++ {
+ elem, err := UnmarshalReflect(v.Index(i), elemType)
+ if err != nil {
+ return zero, err
+ }
+ result = reflect.Append(result, elem)
+ }
+ case *starlark.Dict:
+ if result.Type().Kind() != reflect.Map {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ keyType := result.Type().Key()
+ valueType := result.Type().Elem()
+ for _, pair := range v.Items() {
+ key := pair.Index(0)
+ value := pair.Index(1)
+
+ unmarshalledKey, err := UnmarshalReflect(key, keyType)
+ if err != nil {
+ return zero, err
+ }
+ unmarshalledValue, err := UnmarshalReflect(value, valueType)
+ if err != nil {
+ return zero, err
+ }
+
+ result.SetMapIndex(unmarshalledKey, unmarshalledValue)
+ }
+ case *starlarkstruct.Struct:
+ if result.Type().Kind() != reflect.Struct {
+ return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+ }
+ if result.NumField() != len(v.AttrNames()) {
+ return zero, fmt.Errorf("starlark struct and go struct have different number of fields (%d and %d)", len(v.AttrNames()), result.NumField())
+ }
+ for _, attrName := range v.AttrNames() {
+ attr, err := v.Attr(attrName)
+ if err != nil {
+ return zero, err
+ }
+
+ // TODO(b/279787235): this should probably support tags to rename the field
+ resultField := result.FieldByName(attrName)
+ if resultField == (reflect.Value{}) {
+ return zero, fmt.Errorf("starlark struct had field %s, but requested struct type did not", attrName)
+ }
+ // This hack allows us to change unexported fields
+ resultField = reflect.NewAt(resultField.Type(), unsafe.Pointer(resultField.UnsafeAddr())).Elem()
+ x, err := UnmarshalReflect(attr, resultField.Type())
+ if err != nil {
+ return zero, err
+ }
+ resultField.Set(x)
+ }
+ default:
+ return zero, fmt.Errorf("unimplemented starlark type: %s", value.Type())
+ }
+
+ return result, nil
+}
+
+func typeOfStarlarkValue(value starlark.Value) (reflect.Type, error) {
+ var err error
+ switch v := value.(type) {
+ case starlark.String:
+ return reflect.TypeOf(""), nil
+ case *starlark.List:
+ innerType := reflect.TypeOf("")
+ if v.Len() > 0 {
+ innerType, err = typeOfStarlarkValue(v.Index(0))
+ if err != nil {
+ return nil, err
+ }
+ }
+ for i := 1; i < v.Len(); i++ {
+ innerTypeI, err := typeOfStarlarkValue(v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ if innerType != innerTypeI {
+ return nil, fmt.Errorf("List must contain elements of entirely the same type, found %v and %v", innerType, innerTypeI)
+ }
+ }
+ return reflect.SliceOf(innerType), nil
+ case *starlark.Dict:
+ keyType := reflect.TypeOf("")
+ valueType := reflect.TypeOf("")
+ keys := v.Keys()
+ if v.Len() > 0 {
+ firstKey := keys[0]
+ keyType, err = typeOfStarlarkValue(firstKey)
+ if err != nil {
+ return nil, err
+ }
+ firstValue, found, err := v.Get(firstKey)
+ if !found {
+ err = fmt.Errorf("value not found")
+ }
+ if err != nil {
+ return nil, err
+ }
+ valueType, err = typeOfStarlarkValue(firstValue)
+ if err != nil {
+ return nil, err
+ }
+ }
+ for _, key := range keys {
+ keyTypeI, err := typeOfStarlarkValue(key)
+ if err != nil {
+ return nil, err
+ }
+ if keyType != keyTypeI {
+ return nil, fmt.Errorf("dict must contain elements of entirely the same type, found %v and %v", keyType, keyTypeI)
+ }
+ value, found, err := v.Get(key)
+ if !found {
+ err = fmt.Errorf("value not found")
+ }
+ if err != nil {
+ return nil, err
+ }
+ valueTypeI, err := typeOfStarlarkValue(value)
+ if valueType.Kind() != reflect.Interface && valueTypeI != valueType {
+ // If we see conflicting value types, change the result value type to an empty interface
+ valueType = reflect.TypeOf([]interface{}{}).Elem()
+ }
+ }
+ return reflect.MapOf(keyType, valueType), nil
+ case starlark.Int:
+ return reflect.TypeOf(0), nil
+ case starlark.Float:
+ return reflect.TypeOf(0.0), nil
+ case starlark.Bool:
+ return reflect.TypeOf(true), nil
+ default:
+ return nil, fmt.Errorf("unimplemented starlark type: %s", value.Type())
+ }
+}
diff --git a/starlark_import/unmarshal_test.go b/starlark_import/unmarshal_test.go
new file mode 100644
index 0000000..ee7a9e3
--- /dev/null
+++ b/starlark_import/unmarshal_test.go
@@ -0,0 +1,133 @@
+// 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 starlark_import
+
+import (
+ "reflect"
+ "testing"
+
+ "go.starlark.net/starlark"
+)
+
+func createStarlarkValue(t *testing.T, code string) starlark.Value {
+ t.Helper()
+ result, err := starlark.ExecFile(&starlark.Thread{}, "main.bzl", "x = "+code, builtins)
+ if err != nil {
+ panic(err)
+ }
+ return result["x"]
+}
+
+func TestUnmarshallConcreteType(t *testing.T) {
+ x, err := Unmarshal[string](createStarlarkValue(t, `"foo"`))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if x != "foo" {
+ t.Errorf(`Expected "foo", got %q`, x)
+ }
+}
+
+func TestUnmarshallConcreteTypeWithInterfaces(t *testing.T) {
+ x, err := Unmarshal[map[string]map[string]interface{}](createStarlarkValue(t,
+ `{"foo": {"foo2": "foo3"}, "bar": {"bar2": ["bar3"]}}`))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ expected := map[string]map[string]interface{}{
+ "foo": {"foo2": "foo3"},
+ "bar": {"bar2": []string{"bar3"}},
+ }
+ if !reflect.DeepEqual(x, expected) {
+ t.Errorf(`Expected %v, got %v`, expected, x)
+ }
+}
+
+func TestUnmarshall(t *testing.T) {
+ testCases := []struct {
+ input string
+ expected interface{}
+ }{
+ {
+ input: `"foo"`,
+ expected: "foo",
+ },
+ {
+ input: `5`,
+ expected: 5,
+ },
+ {
+ input: `["foo", "bar"]`,
+ expected: []string{"foo", "bar"},
+ },
+ {
+ input: `("foo", "bar")`,
+ expected: []string{"foo", "bar"},
+ },
+ {
+ input: `("foo",5)`,
+ expected: []interface{}{"foo", 5},
+ },
+ {
+ input: `{"foo": 5, "bar": 10}`,
+ expected: map[string]int{"foo": 5, "bar": 10},
+ },
+ {
+ input: `{"foo": ["qux"], "bar": []}`,
+ expected: map[string][]string{"foo": {"qux"}, "bar": nil},
+ },
+ {
+ input: `struct(Foo="foo", Bar=5)`,
+ expected: struct {
+ Foo string
+ Bar int
+ }{Foo: "foo", Bar: 5},
+ },
+ {
+ // Unexported fields version of the above
+ input: `struct(foo="foo", bar=5)`,
+ expected: struct {
+ foo string
+ bar int
+ }{foo: "foo", bar: 5},
+ },
+ {
+ input: `{"foo": "foo2", "bar": ["bar2"], "baz": 5, "qux": {"qux2": "qux3"}, "quux": {"quux2": "quux3", "quux4": 5}}`,
+ expected: map[string]interface{}{
+ "foo": "foo2",
+ "bar": []string{"bar2"},
+ "baz": 5,
+ "qux": map[string]string{"qux2": "qux3"},
+ "quux": map[string]interface{}{
+ "quux2": "quux3",
+ "quux4": 5,
+ },
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ x, err := UnmarshalReflect(createStarlarkValue(t, tc.input), reflect.TypeOf(tc.expected))
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if !reflect.DeepEqual(x.Interface(), tc.expected) {
+ t.Errorf(`Expected %#v, got %#v`, tc.expected, x.Interface())
+ }
+ }
+}
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 68d7f8d..71e6af0 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -53,6 +53,20 @@
if [[ "$buildfile_mtime1" != "$buildfile_mtime2" ]]; then
fail "BUILD.bazel was updated even though contents are same"
fi
+
+ # Force bp2build to rerun by updating the timestamp of the constants_exported_to_soong.bzl file.
+ touch build/bazel/constants_exported_to_soong.bzl
+
+ run_soong bp2build
+ local -r buildfile_mtime3=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+ local -r marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+ if [[ "$marker_mtime2" == "$marker_mtime3" ]]; then
+ fail "Expected bp2build marker file to change"
+ fi
+ if [[ "$buildfile_mtime2" != "$buildfile_mtime3" ]]; then
+ fail "BUILD.bazel was updated even though contents are same"
+ fi
}
# Tests that blueprint files that are deleted are not present when the
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index a1a792d..ca63fdf 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -88,7 +88,7 @@
fail "Bazel actions not found for force-enabled module"
fi
- unused=`run_soong --bazel-force-enabled-modules=unenabled-touch-file nothing >/dev/null`
+ unused=`run_soong --bazel-force-enabled-modules=unenabled-touch-file --ensure-allowlist-integrity nothing >/dev/null`
if [[ $? -ne 1 ]]; then
fail "Expected failure due to force-enabling an unenabled module "
diff --git a/tests/persistent_bazel_test.sh b/tests/persistent_bazel_test.sh
index 4e2982a..9b7b58f 100755
--- a/tests/persistent_bazel_test.sh
+++ b/tests/persistent_bazel_test.sh
@@ -73,8 +73,8 @@
USE_PERSISTENT_BAZEL=1 run_soong nothing 1>out/failurelog.txt 2>&1 && fail "Expected build failure" || true
- if ! grep -sq "'build/bazel/rules' is not a package" out/failurelog.txt ; then
- fail "Expected error to contain 'build/bazel/rules' is not a package, instead got:\n$(cat out/failurelog.txt)"
+ if ! grep -sq "cannot load //build/bazel/rules/common/api_constants.bzl" out/failurelog.txt ; then
+ fail "Expected error to contain 'cannot load //build/bazel/rules/common/api_constants.bzl', instead got:\n$(cat out/failurelog.txt)"
fi
kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
diff --git a/tradefed/suite_harness/tradefed_binary.go b/tradefed/suite_harness/tradefed_binary.go
index a421d8b..1ce94bc 100644
--- a/tradefed/suite_harness/tradefed_binary.go
+++ b/tradefed/suite_harness/tradefed_binary.go
@@ -78,7 +78,6 @@
// Add dependencies required by all tradefed_binary modules.
props.Libs = []string{
"tradefed",
- "tradefed-test-framework",
"loganalysis",
"compatibility-host-util",
}
diff --git a/ui/build/config.go b/ui/build/config.go
index bf4aec9..2dda52a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -67,28 +67,29 @@
logsPrefix string
// From the arguments
- parallel int
- keepGoing int
- verbose bool
- checkbuild bool
- dist bool
- jsonModuleGraph bool
- apiBp2build bool // Generate BUILD files for Soong modules that contribute APIs
- bp2build bool
- queryview bool
- reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
- soongDocs bool
- multitreeBuild bool // This is a multitree build.
- skipConfig bool
- skipKati bool
- skipKatiNinja bool
- skipSoong bool
- skipNinja bool
- skipSoongTests bool
- searchApiDir bool // Scan the Android.bp files generated in out/api_surfaces
- skipMetricsUpload bool
- buildStartedTime int64 // For metrics-upload-only - manually specify a build-started time
- buildFromTextStub bool
+ parallel int
+ keepGoing int
+ verbose bool
+ checkbuild bool
+ dist bool
+ jsonModuleGraph bool
+ apiBp2build bool // Generate BUILD files for Soong modules that contribute APIs
+ bp2build bool
+ queryview bool
+ reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
+ soongDocs bool
+ multitreeBuild bool // This is a multitree build.
+ skipConfig bool
+ skipKati bool
+ skipKatiNinja bool
+ skipSoong bool
+ skipNinja bool
+ skipSoongTests bool
+ searchApiDir bool // Scan the Android.bp files generated in out/api_surfaces
+ skipMetricsUpload bool
+ buildStartedTime int64 // For metrics-upload-only - manually specify a build-started time
+ buildFromTextStub bool
+ ensureAllowlistIntegrity bool // For CI builds - make sure modules are mixed-built
// From the product config
katiArgs []string
@@ -813,9 +814,6 @@
// by a previous build.
c.skipConfig = true
c.skipKati = true
- } else if arg == "--skip-kati" {
- // TODO: remove --skip-kati once module builds have been migrated to --song-only
- c.skipKati = true
} else if arg == "--soong-only" {
c.skipKati = true
c.skipKatiNinja = true
@@ -883,6 +881,8 @@
} else {
ctx.Fatalf("Error parsing build-time-started-unix-millis", err)
}
+ } else if arg == "--ensure-allowlist-integrity" {
+ c.ensureAllowlistIntegrity = true
} else if len(arg) > 0 && arg[0] == '-' {
parseArgNum := func(def int) int {
if len(arg) > 2 {
@@ -1710,6 +1710,10 @@
return c.skipMetricsUpload
}
+func (c *configImpl) EnsureAllowlistIntegrity() bool {
+ return c.ensureAllowlistIntegrity
+}
+
// Returns a Time object if one was passed via a command-line flag.
// Otherwise returns the passed default.
func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 9287731..563199b 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -288,6 +288,9 @@
if config.buildFromTextStub {
mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-text-stub")
}
+ if config.ensureAllowlistIntegrity {
+ mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--ensure-allowlist-integrity")
+ }
queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)