diff options
37 files changed, 812 insertions, 595 deletions
diff --git a/android/Android.bp b/android/Android.bp index 8f6c9ad56..f17a8a047 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -12,7 +12,6 @@ bootstrap_go_package { "soong", "soong-android-soongconfig", "soong-bazel", - "soong-env", "soong-shared", "soong-ui-metrics_proto", ], diff --git a/android/bazel_handler.go b/android/bazel_handler.go index a5c4bedc5..415f00e8e 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -36,12 +36,14 @@ type CqueryRequestType int const ( getAllFiles CqueryRequestType = iota + getCcObjectFiles ) // Map key to describe bazel cquery requests. type cqueryKey struct { label string requestType CqueryRequestType + archType ArchType } type BazelContext interface { @@ -50,7 +52,11 @@ type BazelContext interface { // has been queued to be run later. // Returns result files built by building the given bazel target label. - GetAllFiles(label string) ([]string, bool) + GetAllFiles(label string, archType ArchType) ([]string, bool) + + // Returns object files produced by compiling the given cc-related target. + // Retrieves these files from Bazel's CcInfo provider. + GetCcObjectFiles(label string, archType ArchType) ([]string, bool) // TODO(cparsons): Other cquery-related methods should be added here. // ** End cquery methods @@ -100,7 +106,12 @@ type MockBazelContext struct { AllFiles map[string][]string } -func (m MockBazelContext) GetAllFiles(label string) ([]string, bool) { +func (m MockBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) { + result, ok := m.AllFiles[label] + return result, ok +} + +func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) { result, ok := m.AllFiles[label] return result, ok } @@ -123,8 +134,18 @@ func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement { var _ BazelContext = MockBazelContext{} -func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) { - result, ok := bazelCtx.cquery(label, getAllFiles) +func (bazelCtx *bazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) { + result, ok := bazelCtx.cquery(label, getAllFiles, archType) + if ok { + bazelOutput := strings.TrimSpace(result) + return strings.Split(bazelOutput, ", "), true + } else { + return nil, false + } +} + +func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) { + result, ok := bazelCtx.cquery(label, getCcObjectFiles, archType) if ok { bazelOutput := strings.TrimSpace(result) return strings.Split(bazelOutput, ", "), true @@ -133,7 +154,11 @@ func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) { } } -func (n noopBazelContext) GetAllFiles(label string) ([]string, bool) { +func (n noopBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) { + panic("unimplemented") +} + +func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) { panic("unimplemented") } @@ -207,8 +232,9 @@ func (context *bazelContext) BazelEnabled() bool { // If the given request was already made (and the results are available), then // returns (result, true). If the request is queued but no results are available, // then returns ("", false). -func (context *bazelContext) cquery(label string, requestType CqueryRequestType) (string, bool) { - key := cqueryKey{label, requestType} +func (context *bazelContext) cquery(label string, requestType CqueryRequestType, + archType ArchType) (string, bool) { + key := cqueryKey{label, requestType, archType} if result, ok := context.results[key]; ok { return result, true } else { @@ -241,17 +267,21 @@ func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command st fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:generic_x86_64"))) cmdFlags = append(cmdFlags, fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all"))) + // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network. + cmdFlags = append(cmdFlags, "--experimental_repository_disable_download") cmdFlags = append(cmdFlags, extraFlags...) bazelCmd := exec.Command(context.bazelPath, cmdFlags...) bazelCmd.Dir = context.workspaceDir - bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix()) - + bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix(), + // Disables local host detection of gcc; toolchain information is defined + // explicitly in BUILD files. + "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1") stderr := &bytes.Buffer{} bazelCmd.Stderr = stderr if output, err := bazelCmd.Output(); err != nil { - return "", fmt.Errorf("bazel command failed. command: [%s], error [%s]", bazelCmd, stderr) + return "", fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr) } else { return string(output), nil } @@ -273,20 +303,81 @@ local_repository( } func (context *bazelContext) mainBzlFileContents() []byte { + // TODO(cparsons): Define configuration transitions programmatically based + // on available archs. contents := ` ##################################################### # This file is generated by soong_build. Do not edit. ##################################################### +def _x86_64_transition_impl(settings, attr): + return { + "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64", + } + +def _x86_transition_impl(settings, attr): + return { + "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86", + } + +def _arm64_transition_impl(settings, attr): + return { + "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm64", + } + +def _arm_transition_impl(settings, attr): + return { + "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm", + } + +x86_64_transition = transition( + implementation = _x86_64_transition_impl, + inputs = [], + outputs = [ + "//command_line_option:platforms", + ], +) + +x86_transition = transition( + implementation = _x86_transition_impl, + inputs = [], + outputs = [ + "//command_line_option:platforms", + ], +) + +arm64_transition = transition( + implementation = _arm64_transition_impl, + inputs = [], + outputs = [ + "//command_line_option:platforms", + ], +) + +arm_transition = transition( + implementation = _arm_transition_impl, + inputs = [], + outputs = [ + "//command_line_option:platforms", + ], +) + def _mixed_build_root_impl(ctx): - return [DefaultInfo(files = depset(ctx.files.deps))] + all_files = ctx.files.deps_x86_64 + ctx.files.deps_x86 + ctx.files.deps_arm64 + ctx.files.deps_arm + return [DefaultInfo(files = depset(all_files))] # Rule representing the root of the build, to depend on all Bazel targets that # are required for the build. Building this target will build the entire Bazel # build tree. mixed_build_root = rule( implementation = _mixed_build_root_impl, - attrs = {"deps" : attr.label_list()}, + attrs = { + "deps_x86_64" : attr.label_list(cfg = x86_64_transition), + "deps_x86" : attr.label_list(cfg = x86_transition), + "deps_arm64" : attr.label_list(cfg = arm64_transition), + "deps_arm" : attr.label_list(cfg = arm_transition), + "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"), + }, ) def _phony_root_impl(ctx): @@ -317,25 +408,48 @@ func canonicalizeLabel(label string) string { } func (context *bazelContext) mainBuildFileContents() []byte { + // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded + // architecture mapping. formatString := ` # This file is generated by soong_build. Do not edit. load(":main.bzl", "mixed_build_root", "phony_root") mixed_build_root(name = "buildroot", - deps = [%s], + deps_x86_64 = [%s], + deps_x86 = [%s], + deps_arm64 = [%s], + deps_arm = [%s], ) phony_root(name = "phonyroot", deps = [":buildroot"], ) ` - var buildRootDeps []string = nil + var deps_x86_64 []string = nil + var deps_x86 []string = nil + var deps_arm64 []string = nil + var deps_arm []string = nil for val, _ := range context.requests { - buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))) + labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)) + switch getArchString(val) { + case "x86_64": + deps_x86_64 = append(deps_x86_64, labelString) + case "x86": + deps_x86 = append(deps_x86, labelString) + case "arm64": + deps_arm64 = append(deps_arm64, labelString) + case "arm": + deps_arm = append(deps_arm, labelString) + default: + panic(fmt.Sprintf("unhandled architecture %s for %s", getArchString(val), val)) + } } - buildRootDepsString := strings.Join(buildRootDeps, ",\n ") - return []byte(fmt.Sprintf(formatString, buildRootDepsString)) + return []byte(fmt.Sprintf(formatString, + strings.Join(deps_x86_64, ",\n "), + strings.Join(deps_x86, ",\n "), + strings.Join(deps_arm64, ",\n "), + strings.Join(deps_arm, ",\n "))) } func (context *bazelContext) cqueryStarlarkFileContents() []byte { @@ -345,23 +459,64 @@ getAllFilesLabels = { %s } +getCcObjectFilesLabels = { + %s +} + +def get_cc_object_files(target): + result = [] + linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list() + + for linker_input in linker_inputs: + for library in linker_input.libraries: + for object in library.objects: + result += [object.path] + return result + +def get_arch(target): + buildoptions = build_options(target) + platforms = build_options(target)["//command_line_option:platforms"] + if len(platforms) != 1: + # An individual configured target should have only one platform architecture. + # Note that it's fine for there to be multiple architectures for the same label, + # but each is its own configured target. + fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms)) + platform_name = build_options(target)["//command_line_option:platforms"][0].name + if platform_name == "host": + return "HOST" + elif not platform_name.startswith("generic_"): + fail("expected platform name of the form 'generic_<arch>', but was " + str(platforms)) + return "UNKNOWN" + return platform_name[len("generic_"):] + def format(target): - if str(target.label) in getAllFilesLabels: - return str(target.label) + ">>" + ', '.join([f.path for f in target.files.to_list()]) + id_string = str(target.label) + "|" + get_arch(target) + if id_string in getAllFilesLabels: + return id_string + ">>" + ', '.join([f.path for f in target.files.to_list()]) + elif id_string in getCcObjectFilesLabels: + return id_string + ">>" + ', '.join(get_cc_object_files(target)) else: # This target was not requested via cquery, and thus must be a dependency # of a requested target. - return "" + return id_string + ">>NONE" ` - var buildRootDeps []string = nil - // TODO(cparsons): Sort by request type instead of assuming all requests - // are of GetAllFiles type. + var getAllFilesDeps []string = nil + var getCcObjectFilesDeps []string = nil + for val, _ := range context.requests { - buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label))) + labelWithArch := getCqueryId(val) + mapEntryString := fmt.Sprintf("%q : True", labelWithArch) + switch val.requestType { + case getAllFiles: + getAllFilesDeps = append(getAllFilesDeps, mapEntryString) + case getCcObjectFiles: + getCcObjectFilesDeps = append(getCcObjectFilesDeps, mapEntryString) + } } - buildRootDepsString := strings.Join(buildRootDeps, ",\n ") + getAllFilesDepsString := strings.Join(getAllFilesDeps, ",\n ") + getCcObjectFilesDepsString := strings.Join(getCcObjectFilesDeps, ",\n ") - return []byte(fmt.Sprintf(formatString, buildRootDepsString)) + return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString)) } // Returns a workspace-relative path containing build-related metadata required @@ -414,9 +569,15 @@ func (context *bazelContext) InvokeBazel() error { } buildrootLabel := "//:buildroot" cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery", - []string{fmt.Sprintf("deps(%s)", buildrootLabel)}, + []string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)}, "--output=starlark", "--starlark:file="+cqueryFileRelpath) + err = ioutil.WriteFile( + absolutePath(filepath.Join(context.intermediatesDir(), "cquery.out")), + []byte(cqueryOutput), 0666) + if err != nil { + return err + } if err != nil { return err @@ -431,10 +592,10 @@ func (context *bazelContext) InvokeBazel() error { } for val, _ := range context.requests { - if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok { + if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok { context.results[val] = string(cqueryResult) } else { - return fmt.Errorf("missing result for bazel target %s", val.label) + return fmt.Errorf("missing result for bazel target %s. query output: [%s]", getCqueryId(val), cqueryOutput) } } @@ -510,6 +671,9 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { // Register bazel-owned build statements (obtained from the aquery invocation). for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() { + if len(buildStatement.Command) < 1 { + panic(fmt.Sprintf("unhandled build statement: %s", buildStatement)) + } rule := NewRuleBuilder(pctx, ctx) cmd := rule.Command() cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s", @@ -531,3 +695,16 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic) } } + +func getCqueryId(key cqueryKey) string { + return canonicalizeLabel(key.label) + "|" + getArchString(key) +} + +func getArchString(key cqueryKey) string { + arch := key.archType.Name + if len(arch) > 0 { + return arch + } else { + return "x86_64" + } +} diff --git a/android/env.go b/android/env.go index c2a09aab8..a8c7777ad 100644 --- a/android/env.go +++ b/android/env.go @@ -21,7 +21,7 @@ import ( "strings" "syscall" - "android/soong/env" + "android/soong/shared" ) // This file supports dependencies on environment variables. During build manifest generation, @@ -113,7 +113,7 @@ func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) { return } - data, err := env.EnvFileContents(envDeps) + data, err := shared.EnvFileContents(envDeps) if err != nil { ctx.Errorf(err.Error()) } diff --git a/android/fixture.go b/android/fixture.go index 0efe329cb..ae24b914f 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -182,7 +182,13 @@ type FixtureFactory interface { // Create a Fixture. Fixture(t *testing.T, preparers ...FixturePreparer) Fixture - // Run the test, expecting no errors, returning a TestResult instance. + // Set the error handler that will be used to check any errors reported by the test. + // + // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any + // errors are reported. + SetErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory + + // Run the test, checking any errors reported and returning a TestResult instance. // // Shorthand for Fixture(t, preparers...).RunTest() RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult @@ -202,6 +208,9 @@ func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) F return &fixtureFactory{ buildDirSupplier: buildDirSupplier, preparers: dedupAndFlattenPreparers(nil, preparers), + + // Set the default error handler. + errorHandler: FixtureExpectsNoErrors, } } @@ -352,9 +361,84 @@ func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer { return &simpleFixturePreparer{function: preparer} } +// FixtureErrorHandler determines how to respond to errors reported by the code under test. +// +// Some possible responses: +// * Fail the test if any errors are reported, see FixtureExpectsNoErrors. +// * Fail the test if at least one error that matches a pattern is not reported see +// FixtureExpectsAtLeastOneErrorMatchingPattern +// * Fail the test if any unexpected errors are reported. +// +// Although at the moment all the error handlers are implemented as simply a wrapper around a +// function this is defined as an interface to allow future enhancements, e.g. provide different +// ways other than patterns to match an error and to combine handlers together. +type FixtureErrorHandler interface { + // CheckErrors checks the errors reported. + // + // The supplied result can be used to access the state of the code under test just as the main + // body of the test would but if any errors other than ones expected are reported the state may + // be indeterminate. + CheckErrors(result *TestResult, errs []error) +} + +type simpleErrorHandler struct { + function func(result *TestResult, errs []error) +} + +func (h simpleErrorHandler) CheckErrors(result *TestResult, errs []error) { + h.function(result, errs) +} + +// The default fixture error handler. +// +// Will fail the test immediately if any errors are reported. +var FixtureExpectsNoErrors = FixtureCustomErrorHandler( + func(result *TestResult, errs []error) { + FailIfErrored(result.T, errs) + }, +) + +// FixtureExpectsAtLeastOneMatchingError returns an error handler that will cause the test to fail +// if at least one error that matches the regular expression is not found. +// +// The test will be failed if: +// * No errors are reported. +// * One or more errors are reported but none match the pattern. +// +// The test will not fail if: +// * Multiple errors are reported that do not match the pattern as long as one does match. +func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler { + return FixtureCustomErrorHandler(func(result *TestResult, errs []error) { + FailIfNoMatchingErrors(result.T, pattern, errs) + }) +} + +// FixtureExpectsOneErrorToMatchPerPattern returns an error handler that will cause the test to fail +// if there are any unexpected errors. +// +// The test will be failed if: +// * The number of errors reported does not exactly match the patterns. +// * One or more of the reported errors do not match a pattern. +// * No patterns are provided and one or more errors are reported. +// +// The test will not fail if: +// * One or more of the patterns does not match an error. +func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler { + return FixtureCustomErrorHandler(func(result *TestResult, errs []error) { + CheckErrorsAgainstExpectations(result.T, errs, patterns) + }) +} + +// FixtureCustomErrorHandler creates a custom error handler +func FixtureCustomErrorHandler(function func(result *TestResult, errs []error)) FixtureErrorHandler { + return simpleErrorHandler{ + function: function, + } +} + // Fixture defines the test environment. type Fixture interface { - // Run the test, expecting no errors, returning a TestResult instance. + // Run the test, checking any errors reported and returning a TestResult instance. RunTest() *TestResult } @@ -454,25 +538,29 @@ var _ FixtureFactory = (*fixtureFactory)(nil) type fixtureFactory struct { buildDirSupplier *string preparers []*simpleFixturePreparer + errorHandler FixtureErrorHandler } func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory { all := append(f.preparers, dedupAndFlattenPreparers(f.preparers, preparers)...) - return &fixtureFactory{ - buildDirSupplier: f.buildDirSupplier, - preparers: all, - } + // Copy the existing factory. + extendedFactory := &fixtureFactory{} + *extendedFactory = *f + // Use the extended list of preparers. + extendedFactory.preparers = all + return extendedFactory } func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture { config := TestConfig(*f.buildDirSupplier, nil, "", nil) ctx := NewTestContext(config) fixture := &fixture{ - factory: f, - t: t, - config: config, - ctx: ctx, - mockFS: make(MockFS), + factory: f, + t: t, + config: config, + ctx: ctx, + mockFS: make(MockFS), + errorHandler: f.errorHandler, } for _, preparer := range f.preparers { @@ -486,6 +574,11 @@ func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fix return fixture } +func (f *fixtureFactory) SetErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory { + f.errorHandler = errorHandler + return f +} + func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult { t.Helper() fixture := f.Fixture(t, preparers...) @@ -498,11 +591,23 @@ func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult { } type fixture struct { + // The factory used to create this fixture. factory *fixtureFactory - t *testing.T - config Config - ctx *TestContext - mockFS MockFS + + // The gotest state of the go test within which this was created. + t *testing.T + + // The configuration prepared for this fixture. + config Config + + // The test context prepared for this fixture. + ctx *TestContext + + // The mock filesystem prepared for this fixture. + mockFS MockFS + + // The error handler used to check the errors, if any, that are reported. + errorHandler FixtureErrorHandler } func (f *fixture) RunTest() *TestResult { @@ -525,9 +630,9 @@ func (f *fixture) RunTest() *TestResult { ctx.Register() _, errs := ctx.ParseBlueprintsFiles("ignored") - FailIfErrored(f.t, errs) - _, errs = ctx.PrepareBuildActions(f.config) - FailIfErrored(f.t, errs) + if len(errs) == 0 { + _, errs = ctx.PrepareBuildActions(f.config) + } result := &TestResult{ TestHelper: TestHelper{T: f.t}, @@ -535,6 +640,9 @@ func (f *fixture) RunTest() *TestResult { fixture: f, Config: f.config, } + + f.errorHandler.CheckErrors(result, errs) + return result } diff --git a/android/module.go b/android/module.go index 8245861bd..e8fb7497e 100644 --- a/android/module.go +++ b/android/module.go @@ -443,6 +443,7 @@ type Module interface { Disable() Enabled() bool Target() Target + MultiTargets() []Target Owner() string InstallInData() bool InstallInTestcases() bool diff --git a/android/package.go b/android/package.go index 7012fc7d8..878e4c4ed 100644 --- a/android/package.go +++ b/android/package.go @@ -23,6 +23,8 @@ func init() { RegisterPackageBuildComponents(InitRegistrationContext) } +var PrepareForTestWithPackageModule = FixtureRegisterWithContext(RegisterPackageBuildComponents) + // Register the package module type. func RegisterPackageBuildComponents(ctx RegistrationContext) { ctx.RegisterModuleType("package", PackageFactory) diff --git a/android/prebuilt.go b/android/prebuilt.go index 39d30c5ec..04864a1d0 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -100,7 +100,7 @@ func (p *Prebuilt) Prefer() bool { // more modules like this. func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path { if p.srcsSupplier != nil { - srcs := p.srcsSupplier(ctx) + srcs := p.srcsSupplier(ctx, ctx.Module()) if len(srcs) == 0 { ctx.PropertyErrorf(p.srcsPropertyName, "missing prebuilt source file") @@ -128,8 +128,11 @@ func (p *Prebuilt) UsePrebuilt() bool { // Called to provide the srcs value for the prebuilt module. // +// This can be called with a context for any module not just the prebuilt one itself. It can also be +// called concurrently. +// // Return the src value or nil if it is not available. -type PrebuiltSrcsSupplier func(ctx BaseModuleContext) []string +type PrebuiltSrcsSupplier func(ctx BaseModuleContext, prebuilt Module) []string // Initialize the module as a prebuilt module that uses the provided supplier to access the // prebuilt sources of the module. @@ -163,7 +166,7 @@ func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) { panic(fmt.Errorf("srcs must not be nil")) } - srcsSupplier := func(ctx BaseModuleContext) []string { + srcsSupplier := func(ctx BaseModuleContext, _ Module) []string { return *srcs } @@ -184,7 +187,7 @@ func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface srcFieldIndex := srcStructField.Index srcPropertyName := proptools.PropertyNameForField(srcField) - srcsSupplier := func(ctx BaseModuleContext) []string { + srcsSupplier := func(ctx BaseModuleContext, _ Module) []string { if !module.Enabled() { return nil } @@ -256,12 +259,12 @@ func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) { panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it")) } if !p.properties.SourceExists { - p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil) + p.properties.UsePrebuilt = p.usePrebuilt(ctx, nil, m) } } else if s, ok := ctx.Module().(Module); ok { ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(m Module) { p := m.(PrebuiltInterface).Prebuilt() - if p.usePrebuilt(ctx, s) { + if p.usePrebuilt(ctx, s, m) { p.properties.UsePrebuilt = true s.ReplacedByPrebuilt() } @@ -296,8 +299,8 @@ func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) { // usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt // will be used if it is marked "prefer" or if the source module is disabled. -func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool { - if p.srcsSupplier != nil && len(p.srcsSupplier(ctx)) == 0 { +func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module, prebuilt Module) bool { + if p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 { return false } diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index 164b1bc59..32af5df4b 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -388,6 +388,8 @@ func registerTestPrebuiltBuildComponents(ctx RegistrationContext) { ctx.PostDepsMutators(RegisterOverridePostDepsMutators) } +var prepareForTestWithFakePrebuiltModules = FixtureRegisterWithContext(registerTestPrebuiltModules) + func registerTestPrebuiltModules(ctx RegistrationContext) { ctx.RegisterModuleType("prebuilt", newPrebuiltModule) ctx.RegisterModuleType("source", newSourceModule) diff --git a/android/visibility.go b/android/visibility.go index 7eac47166..631e88ff9 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -202,6 +202,18 @@ type ExcludeFromVisibilityEnforcementTag interface { ExcludeFromVisibilityEnforcement() } +var PrepareForTestWithVisibilityRuleChecker = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreArchMutators(RegisterVisibilityRuleChecker) +}) + +var PrepareForTestWithVisibilityRuleGatherer = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreArchMutators(RegisterVisibilityRuleGatherer) +}) + +var PrepareForTestWithVisibilityRuleEnforcer = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer) +}) + // The rule checker needs to be registered before defaults expansion to correctly check that // //visibility:xxx isn't combined with other packages in the same list in any one module. func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) { diff --git a/android/visibility_test.go b/android/visibility_test.go index 87a295e23..30cdcbf8f 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -9,13 +9,13 @@ import ( var visibilityTests = []struct { name string - fs map[string][]byte + fs MockFS expectedErrors []string effectiveVisibility map[qualifiedModuleName][]string }{ { name: "invalid visibility: empty list", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -26,7 +26,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility: empty rule", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -37,7 +37,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility: unqualified", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -48,7 +48,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility: empty namespace", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -59,7 +59,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility: empty module", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -70,7 +70,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility: empty namespace and module", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -81,7 +81,7 @@ var visibilityTests = []struct { }, { name: "//visibility:unknown", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -92,7 +92,7 @@ var visibilityTests = []struct { }, { name: "//visibility:xxx mixed", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -113,7 +113,7 @@ var visibilityTests = []struct { }, { name: "//visibility:legacy_public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -129,7 +129,7 @@ var visibilityTests = []struct { // Verify that //visibility:public will allow the module to be referenced from anywhere, e.g. // the current directory, a nested directory and a directory in a separate tree. name: "//visibility:public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -156,7 +156,7 @@ var visibilityTests = []struct { // Verify that //visibility:private allows the module to be referenced from the current // directory only. name: "//visibility:private", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -188,7 +188,7 @@ var visibilityTests = []struct { { // Verify that :__pkg__ allows the module to be referenced from the current directory only. name: ":__pkg__", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -221,7 +221,7 @@ var visibilityTests = []struct { // Verify that //top/nested allows the module to be referenced from the current directory and // the top/nested directory only, not a subdirectory of top/nested and not peak directory. name: "//top/nested", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -259,7 +259,7 @@ var visibilityTests = []struct { // Verify that :__subpackages__ allows the module to be referenced from the current directory // and sub directories but nowhere else. name: ":__subpackages__", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -290,7 +290,7 @@ var visibilityTests = []struct { // Verify that //top/nested:__subpackages__ allows the module to be referenced from the current // directory and sub directories but nowhere else. name: "//top/nested:__subpackages__", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -321,7 +321,7 @@ var visibilityTests = []struct { // Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from // the current directory, top/nested and peak and all its subpackages. name: `["//top/nested", "//peak:__subpackages__"]`, - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -347,7 +347,7 @@ var visibilityTests = []struct { { // Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__ name: `//vendor`, - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -381,7 +381,7 @@ var visibilityTests = []struct { { // Check that visibility is the union of the defaults modules. name: "defaults union, basic", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -419,7 +419,7 @@ var visibilityTests = []struct { }, { name: "defaults union, multiple defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults_1", @@ -460,7 +460,7 @@ var visibilityTests = []struct { }, { name: "//visibility:public mixed with other in defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -478,7 +478,7 @@ var visibilityTests = []struct { }, { name: "//visibility:public overriding defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -501,7 +501,7 @@ var visibilityTests = []struct { }, { name: "//visibility:public mixed with other from different defaults 1", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults_1", @@ -524,7 +524,7 @@ var visibilityTests = []struct { }, { name: "//visibility:public mixed with other from different defaults 2", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults_1", @@ -547,7 +547,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private in defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -581,7 +581,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private mixed with other in defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -599,7 +599,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private overriding defaults", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -618,7 +618,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private in defaults overridden", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -637,7 +637,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private override //visibility:public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -655,7 +655,7 @@ var visibilityTests = []struct { }, { name: "//visibility:public override //visibility:private", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -673,7 +673,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override must be first in the list", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_library { name: "libexample", @@ -686,7 +686,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override discards //visibility:private", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -707,7 +707,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override discards //visibility:public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -736,7 +736,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override discards defaults supplied rules", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -765,7 +765,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override can override //visibility:public with //visibility:private", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -788,7 +788,7 @@ var visibilityTests = []struct { }, { name: "//visibility:override can override //visibility:private with //visibility:public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults", @@ -808,7 +808,7 @@ var visibilityTests = []struct { }, { name: "//visibility:private mixed with itself", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "libexample_defaults_1", @@ -838,7 +838,7 @@ var visibilityTests = []struct { // Defaults module's defaults_visibility tests { name: "defaults_visibility invalid", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_defaults { name: "top_defaults", @@ -851,7 +851,7 @@ var visibilityTests = []struct { }, { name: "defaults_visibility overrides package default", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:private"], @@ -871,7 +871,7 @@ var visibilityTests = []struct { // Package default_visibility tests { name: "package default_visibility property is checked", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:invalid"], @@ -882,7 +882,7 @@ var visibilityTests = []struct { { // This test relies on the default visibility being legacy_public. name: "package default_visibility property used when no visibility specified", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:private"], @@ -904,7 +904,7 @@ var visibilityTests = []struct { }, { name: "package default_visibility public does not override visibility private", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:public"], @@ -927,7 +927,7 @@ var visibilityTests = []struct { }, { name: "package default_visibility private does not override visibility public", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:private"], @@ -946,7 +946,7 @@ var visibilityTests = []struct { }, { name: "package default_visibility :__subpackages__", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: [":__subpackages__"], @@ -973,7 +973,7 @@ var visibilityTests = []struct { }, { name: "package default_visibility inherited to subpackages", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//outsider"], @@ -1001,7 +1001,7 @@ var visibilityTests = []struct { }, { name: "package default_visibility inherited to subpackages", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` package { default_visibility: ["//visibility:private"], @@ -1031,7 +1031,7 @@ var visibilityTests = []struct { }, { name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)", - fs: map[string][]byte{ + fs: MockFS{ "prebuilts/Blueprints": []byte(` prebuilt { name: "module", @@ -1053,7 +1053,7 @@ var visibilityTests = []struct { }, { name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)", - fs: map[string][]byte{ + fs: MockFS{ "prebuilts/Blueprints": []byte(` prebuilt { name: "module", @@ -1076,7 +1076,7 @@ var visibilityTests = []struct { }, { name: "ensure visibility properties are checked for correctness", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_parent { name: "parent", @@ -1093,7 +1093,7 @@ var visibilityTests = []struct { }, { name: "invalid visibility added to child detected during gather phase", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_parent { name: "parent", @@ -1115,7 +1115,7 @@ var visibilityTests = []struct { }, { name: "automatic visibility inheritance enabled", - fs: map[string][]byte{ + fs: MockFS{ "top/Blueprints": []byte(` mock_parent { name: "parent", @@ -1142,53 +1142,41 @@ var visibilityTests = []struct { func TestVisibility(t *testing.T) { for _, test := range visibilityTests { t.Run(test.name, func(t *testing.T) { - ctx, errs := testVisibility(buildDir, test.fs) - - CheckErrorsAgainstExpectations(t, errs, test.expectedErrors) + result := emptyTestFixtureFactory.Extend( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("mock_library", newMockLibraryModule) + ctx.RegisterModuleType("mock_parent", newMockParentFactory) + ctx.RegisterModuleType("mock_defaults", defaultsFactory) + }), + prepareForTestWithFakePrebuiltModules, + PrepareForTestWithPackageModule, + // Order of the following method calls is significant as they register mutators. + PrepareForTestWithArchMutator, + PrepareForTestWithPrebuilts, + PrepareForTestWithOverrides, + PrepareForTestWithVisibilityRuleChecker, + PrepareForTestWithDefaults, + PrepareForTestWithVisibilityRuleGatherer, + PrepareForTestWithVisibilityRuleEnforcer, + // Add additional files to the mock filesystem + test.fs.AddToFixture(), + ). + SetErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)). + RunTest(t) if test.effectiveVisibility != nil { - checkEffectiveVisibility(t, ctx, test.effectiveVisibility) + checkEffectiveVisibility(result, test.effectiveVisibility) } }) } } -func checkEffectiveVisibility(t *testing.T, ctx *TestContext, effectiveVisibility map[qualifiedModuleName][]string) { +func checkEffectiveVisibility(result *TestResult, effectiveVisibility map[qualifiedModuleName][]string) { for moduleName, expectedRules := range effectiveVisibility { - rule := effectiveVisibilityRules(ctx.config, moduleName) + rule := effectiveVisibilityRules(result.Config, moduleName) stringRules := rule.Strings() - if !reflect.DeepEqual(expectedRules, stringRules) { - t.Errorf("effective rules mismatch: expected %q, found %q", expectedRules, stringRules) - } - } -} - -func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) { - - // Create a new config per test as visibility information is stored in the config. - config := TestArchConfig(buildDir, nil, "", fs) - - ctx := NewTestArchContext(config) - ctx.RegisterModuleType("mock_library", newMockLibraryModule) - ctx.RegisterModuleType("mock_parent", newMockParentFactory) - ctx.RegisterModuleType("mock_defaults", defaultsFactory) - - // Order of the following method calls is significant. - RegisterPackageBuildComponents(ctx) - registerTestPrebuiltBuildComponents(ctx) - ctx.PreArchMutators(RegisterVisibilityRuleChecker) - ctx.PreArchMutators(RegisterDefaultsPreArchMutators) - ctx.PreArchMutators(RegisterVisibilityRuleGatherer) - ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer) - ctx.Register() - - _, errs := ctx.ParseBlueprintsFiles(".") - if len(errs) > 0 { - return ctx, errs + result.AssertDeepEquals("effective rules mismatch", expectedRules, stringRules) } - - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs } type mockLibraryProperties struct { diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt index 47ea239be..78b84f09e 100644 --- a/apex/allowed_deps.txt +++ b/apex/allowed_deps.txt @@ -477,6 +477,7 @@ media_plugin_headers(minSdkVersion:29) MediaProvider(minSdkVersion:30) mediaswcodec(minSdkVersion:29) metrics-constants-protos(minSdkVersion:29) +modules-annotation-minsdk(minSdkVersion:29) modules-utils-build(minSdkVersion:29) modules-utils-build_system(minSdkVersion:29) modules-utils-os(minSdkVersion:30) diff --git a/apex/apex.go b/apex/apex.go index 662bbbd0b..a4bdc639b 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -54,8 +54,6 @@ func init() { func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) { ctx.TopDown("apex_vndk", apexVndkMutator).Parallel() ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel() - ctx.BottomUp("prebuilt_apex_select_source", prebuiltSelectSourceMutator).Parallel() - ctx.BottomUp("deapexer_select_source", deapexerSelectSourceMutator).Parallel() } func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { diff --git a/apex/apex_test.go b/apex/apex_test.go index 82fc95578..af5fe0653 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -144,17 +144,18 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr bp = bp + java.GatherRequiredDepsForTest() fs := map[string][]byte{ - "a.java": nil, - "PrebuiltAppFoo.apk": nil, - "PrebuiltAppFooPriv.apk": nil, - "build/make/target/product/security": nil, - "apex_manifest.json": nil, - "AndroidManifest.xml": nil, - "system/sepolicy/apex/myapex-file_contexts": nil, - "system/sepolicy/apex/myapex.updatable-file_contexts": nil, - "system/sepolicy/apex/myapex2-file_contexts": nil, - "system/sepolicy/apex/otherapex-file_contexts": nil, - "system/sepolicy/apex/com.android.vndk-file_contexts": nil, + "a.java": nil, + "PrebuiltAppFoo.apk": nil, + "PrebuiltAppFooPriv.apk": nil, + "build/make/target/product/security": nil, + "apex_manifest.json": nil, + "AndroidManifest.xml": nil, + "system/sepolicy/apex/myapex-file_contexts": nil, + "system/sepolicy/apex/myapex.updatable-file_contexts": nil, + "system/sepolicy/apex/myapex2-file_contexts": nil, + "system/sepolicy/apex/otherapex-file_contexts": nil, + "system/sepolicy/apex/com.android.vndk-file_contexts": nil, + "system/sepolicy/apex/com.android.vndk.current-file_contexts": nil, "mylib.cpp": nil, "mytest.cpp": nil, "mytest1.cpp": nil, @@ -972,8 +973,8 @@ func TestApexWithStubsWithMinSdkVersion(t *testing.T) { mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"] - // Ensure that mylib is linking with the version 29 stubs for mylib2 - ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_29/mylib2.so") + // Ensure that mylib is linking with the latest version of stub for mylib2 + ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") // ... and not linking to the non-stub (impl) variant of mylib2 ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") @@ -1057,11 +1058,11 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { config.TestProductVariables.Platform_version_active_codenames = []string{"Z"} }) - // Ensure that mylib from myapex is built against "min_sdk_version" stub ("Z"), which is non-final + // Ensure that mylib from myapex is built against the latest stub (current) mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] - ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=9000 ") + ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ") mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] - ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_Z/libstub.so ") + ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ") // Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] @@ -1359,18 +1360,18 @@ func TestApexDependsOnLLNDKTransitively(t *testing.T) { shouldNotLink []string }{ { - name: "should link to the latest", + name: "unspecified version links to the latest", minSdkVersion: "", apexVariant: "apex10000", shouldLink: "30", shouldNotLink: []string{"29"}, }, { - name: "should link to llndk#29", + name: "always use the latest", minSdkVersion: "min_sdk_version: \"29\",", apexVariant: "apex29", - shouldLink: "29", - shouldNotLink: []string{"30"}, + shouldLink: "30", + shouldNotLink: []string{"29"}, }, } for _, tc := range testcases { @@ -1532,8 +1533,8 @@ func TestApexWithSystemLibsStubs(t *testing.T) { } func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) { - // there are three links between liba --> libz - // 1) myapex -> libx -> liba -> libz : this should be #29 link, but fallback to #28 + // there are three links between liba --> libz. + // 1) myapex -> libx -> liba -> libz : this should be #30 link // 2) otherapex -> liby -> liba -> libz : this should be #30 link // 3) (platform) -> liba -> libz : this should be non-stub link ctx, _ := testApex(t, ` @@ -1607,9 +1608,9 @@ func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) } // platform liba is linked to non-stub version expectLink("liba", "shared", "libz", "shared") - // liba in myapex is linked to #28 - expectLink("liba", "shared_apex29", "libz", "shared_28") - expectNoLink("liba", "shared_apex29", "libz", "shared_30") + // liba in myapex is linked to #30 + expectLink("liba", "shared_apex29", "libz", "shared_30") + expectNoLink("liba", "shared_apex29", "libz", "shared_28") expectNoLink("liba", "shared_apex29", "libz", "shared") // liba in otherapex is linked to #30 expectLink("liba", "shared_apex30", "libz", "shared_30") @@ -1827,41 +1828,6 @@ func TestQTargetApexUsesStaticUnwinder(t *testing.T) { ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind") } -func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) { - testApexError(t, `"libz" .*: not found a version\(<=29\)`, ` - apex { - name: "myapex", - key: "myapex.key", - native_shared_libs: ["libx"], - min_sdk_version: "29", - } - - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - cc_library { - name: "libx", - shared_libs: ["libz"], - system_shared_libs: [], - stl: "none", - apex_available: [ "myapex" ], - min_sdk_version: "29", - } - - cc_library { - name: "libz", - system_shared_libs: [], - stl: "none", - stubs: { - versions: ["30"], - }, - } - `) -} - func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) { testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, ` apex { @@ -2173,7 +2139,7 @@ func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion private_key: "testkey.pem", } - // mylib in myapex will link to mylib2#29 + // mylib in myapex will link to mylib2#30 // mylib in otherapex will link to mylib2(non-stub) in otherapex as well cc_library { name: "mylib", @@ -2207,7 +2173,7 @@ func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion libFlags := ld.Args["libFlags"] ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } - expectLink("mylib", "shared_apex29", "mylib2", "shared_29") + expectLink("mylib", "shared_apex29", "mylib2", "shared_30") expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30") } @@ -2276,7 +2242,7 @@ func TestApexMinSdkVersion_WorksWithActiveCodenames(t *testing.T) { // ensure libfoo is linked with "S" version of libbar stub libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000") libFlags := libfoo.Rule("ld").Args["libFlags"] - ensureContains(t, libFlags, "android_arm64_armv8-a_shared_S/libbar.so") + ensureContains(t, libFlags, "android_arm64_armv8-a_shared_T/libbar.so") } func TestFilesInSubDir(t *testing.T) { @@ -3239,12 +3205,12 @@ func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, var func TestVndkApexCurrent(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3259,7 +3225,7 @@ func TestVndkApexCurrent(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_library { @@ -3273,11 +3239,11 @@ func TestVndkApexCurrent(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } `+vndkLibrariesTxtFiles("current")) - ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ "lib/libvndk.so", "lib/libvndksp.so", "lib/libc++.so", @@ -3295,12 +3261,12 @@ func TestVndkApexCurrent(t *testing.T) { func TestVndkApexWithPrebuilt(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3315,7 +3281,7 @@ func TestVndkApexWithPrebuilt(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_prebuilt_library_shared { @@ -3334,15 +3300,14 @@ func TestVndkApexWithPrebuilt(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } `+vndkLibrariesTxtFiles("current"), withFiles(map[string][]byte{ "libvndk.so": nil, "libvndk.arm.so": nil, })) - - ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ "lib/libvndk.so", "lib/libvndk.arm.so", "lib64/libvndk.so", @@ -3379,7 +3344,7 @@ func vndkLibrariesTxtFiles(vers ...string) (result string) { func TestVndkApexVersion(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex_v27", + name: "com.android.vndk.v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", @@ -3408,7 +3373,7 @@ func TestVndkApexVersion(t *testing.T) { srcs: ["libvndk27_arm64.so"], }, }, - apex_available: [ "myapex_v27" ], + apex_available: [ "com.android.vndk.v27" ], } vndk_prebuilt_shared { @@ -3437,70 +3402,22 @@ func TestVndkApexVersion(t *testing.T) { "libvndk27_x86_64.so": nil, })) - ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{ "lib/libvndk27_arm.so", "lib64/libvndk27_arm64.so", "etc/*", }) } -func TestVndkApexErrorWithDuplicateVersion(t *testing.T) { - testApexError(t, `module "myapex_v27.*" .*: vndk_version: 27 is already defined in "myapex_v27.*"`, ` - apex_vndk { - name: "myapex_v27", - key: "myapex.key", - file_contexts: ":myapex-file_contexts", - vndk_version: "27", - } - apex_vndk { - name: "myapex_v27_other", - key: "myapex.key", - file_contexts: ":myapex-file_contexts", - vndk_version: "27", - } - - apex_key { - name: "myapex.key", - public_key: "testkey.avbpubkey", - private_key: "testkey.pem", - } - - cc_library { - name: "libvndk", - srcs: ["mylib.cpp"], - vendor_available: true, - product_available: true, - vndk: { - enabled: true, - }, - system_shared_libs: [], - stl: "none", - } - - vndk_prebuilt_shared { - name: "libvndk", - version: "27", - vendor_available: true, - product_available: true, - vndk: { - enabled: true, - }, - srcs: ["libvndk.so"], - } - `, withFiles(map[string][]byte{ - "libvndk.so": nil, - })) -} - func TestVndkApexNameRule(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", + name: "com.android.vndk.current", key: "myapex.key", file_contexts: ":myapex-file_contexts", } apex_vndk { - name: "myapex_v28", + name: "com.android.vndk.v28", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "28", @@ -3519,20 +3436,20 @@ func TestVndkApexNameRule(t *testing.T) { } } - assertApexName("com.android.vndk.vVER", "myapex") - assertApexName("com.android.vndk.v28", "myapex_v28") + assertApexName("com.android.vndk.vVER", "com.android.vndk.current") + assertApexName("com.android.vndk.v28", "com.android.vndk.v28") } func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", file_contexts: ":myapex-file_contexts", } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3549,11 +3466,12 @@ func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } - `+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled) + `+vndkLibrariesTxtFiles("current"), + withNativeBridgeEnabled) - ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ "lib/libvndk.so", "lib64/libvndk.so", "lib/libc++.so", @@ -3563,16 +3481,16 @@ func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) { } func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) { - testApexError(t, `module "myapex" .*: native_bridge_supported: .* doesn't support native bridge binary`, ` + testApexError(t, `module "com.android.vndk.current" .*: native_bridge_supported: .* doesn't support native bridge binary`, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", file_contexts: ":myapex-file_contexts", native_bridge_supported: true, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3596,7 +3514,7 @@ func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) { func TestVndkApexWithBinder32(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex_v27", + name: "com.android.vndk.v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", @@ -3639,7 +3557,7 @@ func TestVndkApexWithBinder32(t *testing.T) { srcs: ["libvndk27binder32.so"], } }, - apex_available: [ "myapex_v27" ], + apex_available: [ "com.android.vndk.v27" ], } `+vndkLibrariesTxtFiles("27"), withFiles(map[string][]byte{ @@ -3655,7 +3573,7 @@ func TestVndkApexWithBinder32(t *testing.T) { }), ) - ensureExactContents(t, ctx, "myapex_v27", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common_image", []string{ "lib/libvndk27binder32.so", "etc/*", }) @@ -3664,13 +3582,13 @@ func TestVndkApexWithBinder32(t *testing.T) { func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", file_contexts: ":myapex-file_contexts", } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3691,7 +3609,7 @@ func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) { "libz.map.txt": nil, })) - apexManifestRule := ctx.ModuleForTests("myapex", "android_common_image").Rule("apexManifestRule") + apexManifestRule := ctx.ModuleForTests("com.android.vndk.current", "android_common_image").Rule("apexManifestRule") provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"]) ensureListEmpty(t, provideNativeLibs) } @@ -4412,6 +4330,20 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) + // Make sure that the deapexer has the correct input APEX. + deapexer := ctx.ModuleForTests("myapex.deapexer", "android_common") + rule := deapexer.Rule("deapexer") + if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) { + t.Errorf("expected: %q, found: %q", expected, actual) + } + + // Make sure that the prebuilt_apex has the correct input APEX. + prebuiltApex := ctx.ModuleForTests("myapex", "android_common") + rule = prebuiltApex.Rule("android/soong/android.Cp") + if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) { + t.Errorf("expected: %q, found: %q", expected, actual) + } + checkDexJarBuildPath(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "libbar") diff --git a/apex/deapexer.go b/apex/deapexer.go index 8f4a28569..46ce41f4d 100644 --- a/apex/deapexer.go +++ b/apex/deapexer.go @@ -65,7 +65,7 @@ func privateDeapexerFactory() android.Module { &module.properties, &module.apexFileProperties, ) - android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source") + android.InitPrebuiltModuleWithSrcSupplier(module, module.apexFileProperties.prebuiltApexSelector, "src") android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) return module } @@ -78,16 +78,6 @@ func (p *Deapexer) Name() string { return p.prebuilt.Name(p.ModuleBase.Name()) } -func deapexerSelectSourceMutator(ctx android.BottomUpMutatorContext) { - p, ok := ctx.Module().(*Deapexer) - if !ok { - return - } - if err := p.apexFileProperties.selectSource(ctx); err != nil { - ctx.ModuleErrorf("%s", err) - } -} - func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) { // Add dependencies from the java modules to which this exports files from the `.apex` file onto // this module so that they can access the `DeapexerInfo` object that this provides. diff --git a/apex/prebuilt.go b/apex/prebuilt.go index ec7f25336..3280cd8e5 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -109,8 +109,10 @@ type Prebuilt struct { type ApexFileProperties struct { // the path to the prebuilt .apex file to import. - Source string `blueprint:"mutated"` - + // + // This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated + // for android_common. That is so that it will have the same arch variant as, and so be compatible + // with, the source `apex` module type that it replaces. Src *string Arch struct { Arm struct { @@ -128,15 +130,20 @@ type ApexFileProperties struct { } } -func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error { - // This is called before prebuilt_select and prebuilt_postdeps mutators - // The mutators requires that src to be set correctly for each arch so that - // arch variants are disabled when src is not provided for the arch. - if len(ctx.MultiTargets()) != 1 { - return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex") +// prebuiltApexSelector selects the correct prebuilt APEX file for the build target. +// +// The ctx parameter can be for any module not just the prebuilt module so care must be taken not +// to use methods on it that are specific to the current module. +// +// See the ApexFileProperties.Src property. +func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string { + multiTargets := prebuilt.MultiTargets() + if len(multiTargets) != 1 { + ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex") + return nil } var src string - switch ctx.MultiTargets()[0].Arch.ArchType { + switch multiTargets[0].Arch.ArchType { case android.Arm: src = String(p.Arch.Arm.Src) case android.Arm64: @@ -146,14 +153,14 @@ func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) er case android.X86_64: src = String(p.Arch.X86_64.Src) default: - return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String()) + ctx.OtherModuleErrorf(prebuilt, "prebuilt_apex does not support %q", multiTargets[0].Arch.String()) + return nil } if src == "" { src = String(p.Src) } - p.Source = src - return nil + return []string{src} } type PrebuiltProperties struct { @@ -217,7 +224,7 @@ func (p *Prebuilt) Name() string { func PrebuiltFactory() android.Module { module := &Prebuilt{} module.AddProperties(&module.properties) - android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source") + android.InitPrebuiltModuleWithSrcSupplier(module, module.properties.prebuiltApexSelector, "src") android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.AddLoadHook(module, func(ctx android.LoadHookContext) { @@ -250,16 +257,6 @@ func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name str return name } -func prebuiltSelectSourceMutator(ctx android.BottomUpMutatorContext) { - p, ok := ctx.Module().(*Prebuilt) - if !ok { - return - } - if err := p.properties.selectSource(ctx); err != nil { - ctx.ModuleErrorf("%s", err) - } -} - type exportedDependencyTag struct { blueprint.BaseDependencyTag name string @@ -535,7 +532,7 @@ func apexSetFactory() android.Module { module := &ApexSet{} module.AddProperties(&module.properties) - srcsSupplier := func(ctx android.BaseModuleContext) []string { + srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string { return module.prebuiltSrcs(ctx) } diff --git a/apex/vndk.go b/apex/vndk.go index f4b12b564..75c0fb01b 100644 --- a/apex/vndk.go +++ b/apex/vndk.go @@ -17,7 +17,6 @@ package apex import ( "path/filepath" "strings" - "sync" "android/soong/android" "android/soong/cc" @@ -60,17 +59,6 @@ type apexVndkProperties struct { Vndk_version *string } -var ( - vndkApexListKey = android.NewOnceKey("vndkApexList") - vndkApexListMutex sync.Mutex -) - -func vndkApexList(config android.Config) map[string]string { - return config.Once(vndkApexListKey, func() interface{} { - return map[string]string{} - }).(map[string]string) -} - func apexVndkMutator(mctx android.TopDownMutatorContext) { if ab, ok := mctx.Module().(*apexBundle); ok && ab.vndkApex { if ab.IsNativeBridgeSupported() { @@ -80,15 +68,6 @@ func apexVndkMutator(mctx android.TopDownMutatorContext) { vndkVersion := ab.vndkVersion(mctx.DeviceConfig()) // Ensure VNDK APEX mount point is formatted as com.android.vndk.v### ab.properties.Apex_name = proptools.StringPtr(vndkApexNamePrefix + vndkVersion) - - // vndk_version should be unique - vndkApexListMutex.Lock() - defer vndkApexListMutex.Unlock() - vndkApexList := vndkApexList(mctx.Config()) - if other, ok := vndkApexList[vndkVersion]; ok { - mctx.PropertyErrorf("vndk_version", "%v is already defined in %q", vndkVersion, other) - } - vndkApexList[vndkVersion] = mctx.ModuleName() } } @@ -99,9 +78,16 @@ func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) { if vndkVersion == "" { vndkVersion = mctx.DeviceConfig().PlatformVndkVersion() } - vndkApexList := vndkApexList(mctx.Config()) - if vndkApex, ok := vndkApexList[vndkVersion]; ok { - mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex) + if vndkVersion == mctx.DeviceConfig().PlatformVndkVersion() { + vndkVersion = "current" + } else { + vndkVersion = "v" + vndkVersion + } + + vndkApexName := "com.android.vndk." + vndkVersion + + if mctx.OtherModuleExists(vndkApexName) { + mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApexName) } } else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex { vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current") diff --git a/apex/vndk_test.go b/apex/vndk_test.go index ccf4e57e6..74a79e1c5 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -11,12 +11,12 @@ import ( func TestVndkApexForVndkLite(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -31,7 +31,7 @@ func TestVndkApexForVndkLite(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_library { @@ -45,13 +45,13 @@ func TestVndkApexForVndkLite(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } `+vndkLibrariesTxtFiles("current"), func(fs map[string][]byte, config android.Config) { config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("") }) // VNDK-Lite contains only core variants of VNDK-Sp libraries - ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ + ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{ "lib/libvndksp.so", "lib/libc++.so", "lib64/libvndksp.so", @@ -67,7 +67,7 @@ func TestVndkApexForVndkLite(t *testing.T) { func TestVndkApexUsesVendorVariant(t *testing.T) { bp := ` apex_vndk { - name: "myapex", + name: "com.android.vndk.current", key: "mykey", } apex_key { @@ -94,7 +94,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { return } } - t.Fail() + t.Errorf("expected path %q not found", path) } t.Run("VNDK lib doesn't have an apex variant", func(t *testing.T) { @@ -106,7 +106,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { } // VNDK APEX doesn't create apex variant - files := getFiles(t, ctx, "myapex", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so") }) @@ -116,7 +116,7 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { config.TestProductVariables.ProductVndkVersion = proptools.StringPtr("current") }) - files := getFiles(t, ctx, "myapex", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so") }) @@ -126,10 +126,10 @@ func TestVndkApexUsesVendorVariant(t *testing.T) { config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) }) - files := getFiles(t, ctx, "myapex", "android_common_image") + files := getFiles(t, ctx, "com.android.vndk.current", "android_common_image") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared/libfoo.so") - files = getFiles(t, ctx, "myapex", "android_common_cov_image") + files = getFiles(t, ctx, "com.android.vndk.current", "android_common_cov_image") ensureFileSrc(t, files, "lib/libfoo.so", "libfoo/android_vendor.VER_arm_armv7-a-neon_shared_cov/libfoo.so") }) } diff --git a/bazel/aquery.go b/bazel/aquery.go index eb4bdfe99..c82b464ad 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -115,7 +115,23 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { // may be an expensive operation. depsetIdToArtifactIdsCache := map[int][]int{} + // Do a pass through all actions to identify which artifacts are middleman artifacts. + // These will be omitted from the inputs of other actions. + // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated + // headers may cause build failures. + middlemanArtifactIds := map[int]bool{} for _, actionEntry := range aqueryResult.Actions { + if actionEntry.Mnemonic == "Middleman" { + for _, outputId := range actionEntry.OutputIds { + middlemanArtifactIds[outputId] = true + } + } + } + + for _, actionEntry := range aqueryResult.Actions { + if shouldSkipAction(actionEntry) { + continue + } outputPaths := []string{} for _, outputId := range actionEntry.OutputIds { outputPath, exists := artifactIdToPath[outputId] @@ -132,6 +148,10 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { return nil, err } for _, inputId := range inputArtifacts { + if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact { + // Omit middleman artifacts. + continue + } inputPath, exists := artifactIdToPath[inputId] if !exists { return nil, fmt.Errorf("undefined input artifactId %d", inputId) @@ -145,12 +165,38 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { InputPaths: inputPaths, Env: actionEntry.EnvironmentVariables, Mnemonic: actionEntry.Mnemonic} + if len(actionEntry.Arguments) < 1 { + return nil, fmt.Errorf("received action with no command: [%s]", buildStatement) + continue + } buildStatements = append(buildStatements, buildStatement) } return buildStatements, nil } +func shouldSkipAction(a action) bool { + // TODO(b/180945121): Handle symlink actions. + if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" { + return true + } + // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated + // headers may cause build failures. + if a.Mnemonic == "Middleman" { + return true + } + // Skip "Fail" actions, which are placeholder actions designed to always fail. + if a.Mnemonic == "Fail" { + return true + } + // TODO(b/180946980): Handle FileWrite. The aquery proto currently contains no information + // about the contents that are written. + if a.Mnemonic == "FileWrite" { + return true + } + return false +} + func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles, depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) { if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists { @@ -577,6 +577,17 @@ type installer interface { makeUninstallable(mod *Module) } +// bazelHandler is the interface for a helper object related to deferring to Bazel for +// processing a module (during Bazel mixed builds). Individual module types should define +// their own bazel handler if they support deferring to Bazel. +type bazelHandler interface { + // Issue query to Bazel to retrieve information about Bazel's view of the current module. + // If Bazel returns this information, set module properties on the current module to reflect + // the returned information. + // Returns true if information was available from Bazel, false if bazel invocation still needs to occur. + generateBazelBuildActions(ctx android.ModuleContext, label string) bool +} + type xref interface { XrefCcFiles() android.Paths } @@ -779,9 +790,10 @@ type Module struct { // type-specific logic. These members may reference different objects or the same object. // Functions of these decorators will be invoked to initialize and register type-specific // build statements. - compiler compiler - linker linker - installer installer + compiler compiler + linker linker + installer installer + bazelHandler bazelHandler features []feature stl *stl @@ -1559,24 +1571,7 @@ func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string return nameSuffix } -func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { - // Handle the case of a test module split by `test_per_src` mutator. - // - // The `test_per_src` mutator adds an extra variation named "", depending on all the other - // `test_per_src` variations of the test module. Set `outputFile` to an empty path for this - // module and return early, as this module does not produce an output file per se. - if c.IsTestPerSrcAllTestsVariation() { - c.outputFile = android.OptionalPath{} - return - } - - apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo) - if !apexInfo.IsForPlatform() { - c.hideApexVariantFromMake = true - } - - c.makeLinkType = GetMakeLinkType(actx, c) - +func (c *Module) setSubnameProperty(actx android.ModuleContext) { c.Properties.SubName = "" if c.Target().NativeBridge == android.NativeBridgeEnabled { @@ -1606,6 +1601,43 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.Properties.SubName += "." + c.SdkVersion() } } +} + +// Returns true if Bazel was successfully used for the analysis of this module. +func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool { + bazelModuleLabel := c.GetBazelLabel() + bazelActionsUsed := false + if c.bazelHandler != nil && actx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 { + bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel) + } + return bazelActionsUsed +} + +func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { + // TODO(cparsons): Any logic in this method occurring prior to querying Bazel should be + // requested from Bazel instead. + + // Handle the case of a test module split by `test_per_src` mutator. + // + // The `test_per_src` mutator adds an extra variation named "", depending on all the other + // `test_per_src` variations of the test module. Set `outputFile` to an empty path for this + // module and return early, as this module does not produce an output file per se. + if c.IsTestPerSrcAllTestsVariation() { + c.outputFile = android.OptionalPath{} + return + } + + c.setSubnameProperty(actx) + apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if !apexInfo.IsForPlatform() { + c.hideApexVariantFromMake = true + } + + if c.maybeGenerateBazelActions(actx) { + return + } + + c.makeLinkType = GetMakeLinkType(actx, c) ctx := &moduleContext{ ModuleContext: actx, @@ -2420,36 +2452,6 @@ func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) { } } -// Returns the highest version which is <= maxSdkVersion. -// For example, with maxSdkVersion is 10 and versionList is [9,11] -// it returns 9 as string. The list of stubs must be in order from -// oldest to newest. -func (c *Module) chooseSdkVersion(ctx android.PathContext, stubsInfo []SharedStubLibrary, - maxSdkVersion android.ApiLevel) (SharedStubLibrary, error) { - - for i := range stubsInfo { - stubInfo := stubsInfo[len(stubsInfo)-i-1] - var ver android.ApiLevel - if stubInfo.Version == "" { - ver = android.FutureApiLevel - } else { - var err error - ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version) - if err != nil { - return SharedStubLibrary{}, err - } - } - if ver.LessThanOrEqualTo(maxSdkVersion) { - return stubInfo, nil - } - } - var versionList []string - for _, stubInfo := range stubsInfo { - versionList = append(versionList, stubInfo.Version) - } - return SharedStubLibrary{}, fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion.String(), versionList) -} - // Convert dependencies to paths. Returns a PathDeps containing paths func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { var depPaths PathDeps @@ -2633,16 +2635,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { useStubs = !android.DirectlyInAllApexes(apexInfo, depName) } - // when to use (unspecified) stubs, check min_sdk_version and choose the right one + // when to use (unspecified) stubs, use the latest one. if useStubs { - sharedLibraryStubsInfo, err := - c.chooseSdkVersion(ctx, sharedLibraryStubsInfo.SharedStubLibraries, c.apexSdkVersion) - if err != nil { - ctx.OtherModuleErrorf(dep, err.Error()) - return - } - sharedLibraryInfo = sharedLibraryStubsInfo.SharedLibraryInfo - depExporterInfo = sharedLibraryStubsInfo.FlagExporterInfo + stubs := sharedLibraryStubsInfo.SharedStubLibraries + toUse := stubs[len(stubs)-1] + sharedLibraryInfo = toUse.SharedLibraryInfo + depExporterInfo = toUse.FlagExporterInfo } } diff --git a/cc/config/tidy.go b/cc/config/tidy.go index 7c20dd543..c4563e273 100644 --- a/cc/config/tidy.go +++ b/cc/config/tidy.go @@ -31,7 +31,6 @@ func init() { } checks := strings.Join([]string{ "-*", - "abseil-*", "android-*", "bugprone-*", "cert-*", diff --git a/cc/object.go b/cc/object.go index 32347b81b..126bd657b 100644 --- a/cc/object.go +++ b/cc/object.go @@ -46,6 +46,26 @@ type objectLinker struct { Properties ObjectLinkerProperties } +type objectBazelHandler struct { + bazelHandler + + module *Module +} + +func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { + bazelCtx := ctx.Config().BazelContext + objPaths, ok := bazelCtx.GetCcObjectFiles(label, ctx.Arch().ArchType) + if ok { + if len(objPaths) != 1 { + ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths) + return false + } + + handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0])) + } + return ok +} + type ObjectLinkerProperties struct { // list of modules that should only provide headers for this module. Header_libs []string `android:"arch_variant,variant_prepend"` @@ -80,6 +100,7 @@ func ObjectFactory() android.Module { baseLinker: NewBaseLinker(module.sanitize), } module.compiler = NewBaseCompiler() + module.bazelHandler = &objectBazelHandler{module: module} // Clang's address-significance tables are incompatible with ld -r. module.compiler.appendCflags([]string{"-fno-addrsig"}) diff --git a/cc/prebuilt.go b/cc/prebuilt.go index 2cd18cb99..6b9a3d529 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -246,7 +246,7 @@ func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDec module.AddProperties(&prebuilt.properties) - srcsSupplier := func(ctx android.BaseModuleContext) []string { + srcsSupplier := func(ctx android.BaseModuleContext, _ android.Module) []string { return prebuilt.prebuiltSrcs(ctx) } diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp index 6a0a87bc6..9f0922449 100644 --- a/cmd/soong_build/Android.bp +++ b/cmd/soong_build/Android.bp @@ -25,7 +25,6 @@ bootstrap_go_binary { "soong", "soong-android", "soong-bp2build", - "soong-env", "soong-ui-metrics_proto", ], srcs: [ diff --git a/cmd/soong_env/Android.bp b/cmd/soong_env/Android.bp deleted file mode 100644 index ad717d0b1..000000000 --- a/cmd/soong_env/Android.bp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015 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_binary { - name: "soong_env", - deps: [ - "soong-env", - ], - srcs: [ - "soong_env.go", - ], - default: true, -} diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go deleted file mode 100644 index 8020b17a0..000000000 --- a/cmd/soong_env/soong_env.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015 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. - -// soong_env determines if the given soong environment file (usually ".soong.environment") is stale -// by comparing its contents to the current corresponding environment variable values. -// It fails if the file cannot be opened or corrupted, or its contents differ from the current -// values. - -package main - -import ( - "flag" - "fmt" - "os" - - "android/soong/env" -) - -func usage() { - fmt.Fprintf(os.Stderr, "usage: soong_env env_file\n") - fmt.Fprintf(os.Stderr, "exits with success if the environment varibles in env_file match\n") - fmt.Fprintf(os.Stderr, "the current environment\n") - flag.PrintDefaults() - os.Exit(2) -} - -// This is a simple executable packaging, and the real work happens in env.StaleEnvFile. -func main() { - flag.Parse() - - if flag.NArg() != 1 { - usage() - } - - stale, err := env.StaleEnvFile(flag.Arg(0)) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) - os.Exit(1) - } - - if stale { - os.Exit(1) - } - - os.Exit(0) -} diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 36a5e2aa2..888466a2f 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -85,7 +85,7 @@ type GlobalConfig struct { Dex2oatImageXmx string // max heap size for dex2oat for the boot image Dex2oatImageXms string // initial heap size for dex2oat for the boot image - // If true, downgrade the compiler filter of dexpreopt to "extract" when verify_uses_libraries + // If true, downgrade the compiler filter of dexpreopt to "verify" when verify_uses_libraries // check fails, instead of failing the build. This will disable any AOT-compilation. // // The intended use case for this flag is to have a smoother migration path for the Java diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 6e0fe01f9..737773fcb 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -369,11 +369,11 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g } if module.EnforceUsesLibraries { // If the verify_uses_libraries check failed (in this case status file contains a - // non-empty error message), then use "extract" compiler filter to avoid compiling any + // non-empty error message), then use "verify" compiler filter to avoid compiling any // code (it would be rejected on device because of a class loader context mismatch). cmd.Text("--compiler-filter=$(if test -s "). Input(module.EnforceUsesLibrariesStatusFile). - Text(" ; then echo extract ; else echo " + compilerFilter + " ; fi)") + Text(" ; then echo verify ; else echo " + compilerFilter + " ; fi)") } else { cmd.FlagWithArg("--compiler-filter=", compilerFilter) } diff --git a/env/Android.bp b/env/Android.bp deleted file mode 100644 index c6a97b1e6..000000000 --- a/env/Android.bp +++ /dev/null @@ -1,11 +0,0 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -bootstrap_go_package { - name: "soong-env", - pkgPath: "android/soong/env", - srcs: [ - "env.go", - ], -} diff --git a/genrule/genrule.go b/genrule/genrule.go index b4303a696..50c77cf9a 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -206,7 +206,7 @@ func toolDepsMutator(ctx android.BottomUpMutatorContext) { // Returns true if information was available from Bazel, false if bazel invocation still needs to occur. func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { bazelCtx := ctx.Config().BazelContext - filePaths, ok := bazelCtx.GetAllFiles(label) + filePaths, ok := bazelCtx.GetAllFiles(label, ctx.Arch().ArchType) if ok { var bazelOutputFiles android.Paths for _, bazelOutputFile := range filePaths { diff --git a/java/app.go b/java/app.go index 39f06d004..2d918e942 100755 --- a/java/app.go +++ b/java/app.go @@ -1217,6 +1217,15 @@ func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []s return optionalUsesLibs } +// Helper function to replace string in a list. +func replaceInList(list []string, oldstr, newstr string) { + for i, str := range list { + if str == oldstr { + list[i] = newstr + } + } +} + // Returns a map of module names of shared library dependencies to the paths // to their dex jars on host and on device. func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap { @@ -1227,7 +1236,16 @@ func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok { dep := ctx.OtherModuleName(m) if lib, ok := m.(UsesLibraryDependency); ok { - clcMap.AddContext(ctx, tag.sdkVersion, dep, + libName := dep + if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil { + libName = *ulib.ProvidesUsesLib() + // Replace module name with library name in `uses_libs`/`optional_uses_libs` + // in order to pass verify_uses_libraries check (which compares these + // properties against library names written in the manifest). + replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName) + replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName) + } + clcMap.AddContext(ctx, tag.sdkVersion, libName, lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts()) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{dep}) diff --git a/java/app_import.go b/java/app_import.go index 59eb10a9b..d69dd10f9 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -244,10 +244,6 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext srcApk := a.prebuilt.SingleSourcePath(ctx) - if a.usesLibrary.enforceUsesLibraries() { - srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) - } - // TODO: Install or embed JNI libraries // Uncompress JNI libraries in the apk @@ -276,6 +272,10 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) + if a.usesLibrary.enforceUsesLibraries() { + srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) + } + a.dexpreopter.dexpreopt(ctx, jnisUncompressed) if a.dexpreopter.uncompressedDex { dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk") diff --git a/java/app_test.go b/java/app_test.go index 349579e97..f41047aa6 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2290,17 +2290,33 @@ func TestUsesLibraries(t *testing.T) { sdk_version: "current", } + // A library that has to use "provides_uses_lib", because: + // - it is not an SDK library + // - its library name is different from its module name + java_library { + name: "non-sdk-lib", + provides_uses_lib: "com.non.sdk.lib", + installable: true, + srcs: ["a.java"], + } + android_app { name: "app", srcs: ["a.java"], - libs: ["qux", "quuz.stubs"], + libs: [ + "qux", + "quuz.stubs" + ], static_libs: [ "static-runtime-helper", // statically linked component libraries should not pull their SDK libraries, // so "fred" should not be added to class loader context "fred.stubs", ], - uses_libs: ["foo"], + uses_libs: [ + "foo", + "non-sdk-lib" + ], sdk_version: "current", optional_uses_libs: [ "bar", @@ -2312,7 +2328,11 @@ func TestUsesLibraries(t *testing.T) { name: "prebuilt", apk: "prebuilts/apk/app.apk", certificate: "platform", - uses_libs: ["foo", "android.test.runner"], + uses_libs: [ + "foo", + "non-sdk-lib", + "android.test.runner" + ], optional_uses_libs: [ "bar", "baz", @@ -2331,39 +2351,51 @@ func TestUsesLibraries(t *testing.T) { prebuilt := ctx.ModuleForTests("prebuilt", "android_common") // Test that implicit dependencies on java_sdk_library instances are passed to the manifest. - manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] - for _, w := range []string{"qux", "quuz", "runtime-library"} { - if !strings.Contains(manifestFixerArgs, "--uses-library "+w) { - t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs) - } - } - - // Test that all libraries are verified - cmd := app.Rule("verify_uses_libraries").RuleParams.Command - if w := "--uses-library foo"; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } - - if w := "--optional-uses-library bar --optional-uses-library baz"; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } - - cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command - - if w := `uses_library_names="foo android.test.runner"`; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) - } - - if w := `optional_uses_library_names="bar baz"`; !strings.Contains(cmd, w) { - t.Errorf("wanted %q in %q", w, cmd) + // This should not include explicit `uses_libs`/`optional_uses_libs` entries. + actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] + expectManifestFixerArgs := `--extract-native-libs=true ` + + `--uses-library qux ` + + `--uses-library quuz ` + + `--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer + `--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer + `--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer + `--uses-library runtime-library` + if actualManifestFixerArgs != expectManifestFixerArgs { + t.Errorf("unexpected manifest_fixer args:\n\texpect: %q\n\tactual: %q", + expectManifestFixerArgs, actualManifestFixerArgs) + } + + // Test that all libraries are verified (library order matters). + verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command + verifyArgs := `--uses-library foo ` + + `--uses-library com.non.sdk.lib ` + + `--uses-library qux ` + + `--uses-library quuz ` + + `--uses-library runtime-library ` + + `--optional-uses-library bar ` + + `--optional-uses-library baz ` + if !strings.Contains(verifyCmd, verifyArgs) { + t.Errorf("wanted %q in %q", verifyArgs, verifyCmd) + } + + // Test that all libraries are verified for an APK (library order matters). + verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command + verifyApkReqLibs := `uses_library_names="foo com.non.sdk.lib android.test.runner"` + verifyApkOptLibs := `optional_uses_library_names="bar baz"` + if !strings.Contains(verifyApkCmd, verifyApkReqLibs) { + t.Errorf("wanted %q in %q", verifyApkReqLibs, verifyApkCmd) + } + if !strings.Contains(verifyApkCmd, verifyApkOptLibs) { + t.Errorf("wanted %q in %q", verifyApkOptLibs, verifyApkCmd) } // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs - cmd = app.Rule("dexpreopt").RuleParams.Command + cmd := app.Rule("dexpreopt").RuleParams.Command w := `--target-context-for-sdk any ` + `PCL[/system/framework/qux.jar]#` + `PCL[/system/framework/quuz.jar]#` + `PCL[/system/framework/foo.jar]#` + + `PCL[/system/framework/non-sdk-lib.jar]#` + `PCL[/system/framework/bar.jar]#` + `PCL[/system/framework/runtime-library.jar]` if !strings.Contains(cmd, w) { @@ -2393,6 +2425,7 @@ func TestUsesLibraries(t *testing.T) { cmd = prebuilt.Rule("dexpreopt").RuleParams.Command if w := `--target-context-for-sdk any` + ` PCL[/system/framework/foo.jar]` + + `#PCL[/system/framework/non-sdk-lib.jar]` + `#PCL[/system/framework/android.test.runner.jar]` + `#PCL[/system/framework/bar.jar] `; !strings.Contains(cmd, w) { t.Errorf("wanted %q in %q", w, cmd) diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 86b189558..e94b20c55 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -210,6 +210,15 @@ import ( // apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage). // +var artApexNames = []string{ + "com.android.art", + "com.android.art.debug", + "com.android.art,testing", + "com.google.android.art", + "com.google.android.art.debug", + "com.google.android.art.testing", +} + func init() { RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext) } @@ -485,7 +494,14 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul switch image.name { case artBootImageName: - if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") { + inArtApex := false + for _, n := range artApexNames { + if apexInfo.InApexByBaseName(n) { + inArtApex = true + break + } + } + if inArtApex { // ok: found the jar in the ART apex } else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") { // exception (skip and continue): Jacoco platform variant for a coverage build diff --git a/shared/Android.bp b/shared/Android.bp index 5aa9d54f7..c79bc2b39 100644 --- a/shared/Android.bp +++ b/shared/Android.bp @@ -6,6 +6,7 @@ bootstrap_go_package { name: "soong-shared", pkgPath: "android/soong/shared", srcs: [ + "env.go", "paths.go", ], deps: [ diff --git a/env/env.go b/shared/env.go index 735a38aa4..7900daa88 100644 --- a/env/env.go +++ b/shared/env.go @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -// env implements the environment JSON file handling for the soong_env command line tool run before -// the builder and for the env writer in the builder. -package env +// Implements the environment JSON file handling for serializing the +// environment variables that were used in soong_build so that soong_ui can +// check whether they have changed +package shared import ( "encoding/json" "fmt" "io/ioutil" - "os" "sort" ) @@ -57,7 +57,7 @@ func EnvFileContents(envDeps map[string]string) ([]byte, error) { // Reads and deserializes a Soong environment file located at the given file path to determine its // staleness. If any environment variable values have changed, it prints them out and returns true. // Failing to read or parse the file also causes it to return true. -func StaleEnvFile(filepath string) (bool, error) { +func StaleEnvFile(filepath string, getenv func(string) string) (bool, error) { data, err := ioutil.ReadFile(filepath) if err != nil { return true, err @@ -74,7 +74,7 @@ func StaleEnvFile(filepath string) (bool, error) { for _, entry := range contents { key := entry.Key old := entry.Value - cur := os.Getenv(key) + cur := getenv(key) if old != cur { changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur)) } diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go index 3b1005fb5..067f3f3fe 100644 --- a/ui/build/paths/logs_test.go +++ b/ui/build/paths/logs_test.go @@ -26,6 +26,9 @@ import ( ) func TestSendLog(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode, sometimes hangs") + } t.Run("Short name", func(t *testing.T) { d, err := ioutil.TempDir("", "s") if err != nil { diff --git a/ui/build/soong.go b/ui/build/soong.go index 899ab5da5..125dbcc11 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -19,8 +19,8 @@ import ( "os" "path/filepath" "strconv" - "strings" + "android/soong/shared" soong_metrics_proto "android/soong/ui/metrics/metrics_proto" "github.com/golang/protobuf/proto" @@ -79,29 +79,12 @@ func runSoong(ctx Context, config Config) { defer ctx.EndTrace() envFile := filepath.Join(config.SoongOutDir(), ".soong.environment") - envTool := filepath.Join(config.SoongOutDir(), ".bootstrap/bin/soong_env") - if _, err := os.Stat(envFile); err == nil { - if _, err := os.Stat(envTool); err == nil { - cmd := Command(ctx, config, "soong_env", envTool, envFile) - cmd.Sandbox = soongSandbox - - var buf strings.Builder - cmd.Stdout = &buf - cmd.Stderr = &buf - if err := cmd.Run(); err != nil { - ctx.Verboseln("soong_env failed, forcing manifest regeneration") - os.Remove(envFile) - } - - if buf.Len() > 0 { - ctx.Verboseln(buf.String()) - } - } else { - ctx.Verboseln("Missing soong_env tool, forcing manifest regeneration") - os.Remove(envFile) - } - } else if !os.IsNotExist(err) { - ctx.Fatalf("Failed to stat %f: %v", envFile, err) + getenv := func(k string) string { + v, _ := config.Environment().Get(k) + return v + } + if stale, _ := shared.StaleEnvFile(envFile, getenv); stale { + os.Remove(envFile) } }() |