diff options
146 files changed, 5621 insertions, 2407 deletions
diff --git a/android/Android.bp b/android/Android.bp index 00139d8ab..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", ], @@ -22,6 +21,7 @@ bootstrap_go_package { "api_levels.go", "arch.go", "arch_list.go", + "bazel.go", "bazel_handler.go", "config.go", "csuite_config.go", @@ -33,6 +33,7 @@ bootstrap_go_package { "deptag.go", "expand.go", "filegroup.go", + "fixture.go", "hooks.go", "image.go", "license.go", @@ -86,6 +87,7 @@ bootstrap_go_package { "depset_test.go", "deptag_test.go", "expand_test.go", + "fixture_test.go", "license_kind_test.go", "license_test.go", "licenses_test.go", diff --git a/android/android_test.go b/android/android_test.go index 46b705468..68cb70543 100644 --- a/android/android_test.go +++ b/android/android_test.go @@ -44,3 +44,5 @@ func TestMain(m *testing.M) { os.Exit(run()) } + +var emptyTestFixtureFactory = NewFixtureFactory(&buildDir) diff --git a/android/apex.go b/android/apex.go index 463794245..a014592ef 100644 --- a/android/apex.go +++ b/android/apex.go @@ -739,6 +739,8 @@ func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath WriteFileRule(ctx, d.flatListPath, flatContent.String()) + + ctx.Phony(fmt.Sprintf("%s-depsinfo", ctx.ModuleName()), d.fullListPath, d.flatListPath) } // TODO(b/158059172): remove minSdkVersion allowlist @@ -847,8 +849,12 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil { toName := ctx.OtherModuleName(to) if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) { - ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s", - minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false)) + ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v."+ + "\n\nDependency path: %s\n\n"+ + "Consider adding 'min_sdk_version: %q' to %q", + minSdkVersion, ctx.ModuleName(), err.Error(), + ctx.GetPathString(false), + minSdkVersion, ctx.ModuleName()) return false } } diff --git a/android/arch.go b/android/arch.go index 6fb70c9f1..20b4ab07d 100644 --- a/android/arch.go +++ b/android/arch.go @@ -616,16 +616,8 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { osTargets = []Target{osTargets[0]} } - // Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit. - // This is used for HOST_PREFER_32_BIT=true support for Art modules. - prefer32 := false - if base.prefer32 != nil { - prefer32 = base.prefer32(mctx, base, os) - } - if os == Windows { - // Windows builds always prefer 32-bit - prefer32 = true - } + // Windows builds always prefer 32-bit + prefer32 := os == Windows // Determine the multilib selection for this module. multilib, extraMultilib := decodeMultilib(base, os.Class) @@ -1623,3 +1615,97 @@ func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([] return buildTargets, nil } + +// GetArchProperties returns a map of architectures to the values of the +// properties of the 'dst' struct that are specific to that architecture. +// +// For example, passing a struct { Foo bool, Bar string } will return an +// interface{} that can be type asserted back into the same struct, containing +// the arch specific property value specified by the module if defined. +func (m *ModuleBase) GetArchProperties(dst interface{}) map[ArchType]interface{} { + // Return value of the arch types to the prop values for that arch. + archToProp := map[ArchType]interface{}{} + + // Nothing to do for non-arch-specific modules. + if !m.ArchSpecific() { + return archToProp + } + + // archProperties has the type of [][]interface{}. Looks complicated, so let's + // explain this step by step. + // + // Loop over the outer index, which determines the property struct that + // contains a matching set of properties in dst that we're interested in. + // For example, BaseCompilerProperties or BaseLinkerProperties. + for i := range m.archProperties { + if m.archProperties[i] == nil { + // Skip over nil arch props + continue + } + + // Non-nil arch prop, let's see if the props match up. + for _, arch := range ArchTypeList() { + // e.g X86, Arm + field := arch.Field + + // If it's not nil, loop over the inner index, which determines the arch variant + // of the prop type. In an Android.bp file, this is like looping over: + // + // arch: { arm: { key: value, ... }, x86: { key: value, ... } } + for _, archProperties := range m.archProperties[i] { + archPropValues := reflect.ValueOf(archProperties).Elem() + + // This is the archPropRoot struct. Traverse into the Arch nested struct. + src := archPropValues.FieldByName("Arch").Elem() + + // Step into non-nil pointers to structs in the src value. + if src.Kind() == reflect.Ptr { + if src.IsNil() { + // Ignore nil pointers. + continue + } + src = src.Elem() + } + + // Find the requested field (e.g. x86, x86_64) in the src struct. + src = src.FieldByName(field) + if !src.IsValid() { + continue + } + + // We only care about structs. These are not the droids you are looking for. + if src.Kind() != reflect.Struct { + continue + } + + // If the value of the field is a struct then step into the + // BlueprintEmbed field. The special "BlueprintEmbed" name is + // used by createArchPropTypeDesc to embed the arch properties + // in the parent struct, so the src arch prop should be in this + // field. + // + // See createArchPropTypeDesc for more details on how Arch-specific + // module properties are processed from the nested props and written + // into the module's archProperties. + src = src.FieldByName("BlueprintEmbed") + + // Clone the destination prop, since we want a unique prop struct per arch. + dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface() + + // Copy the located property struct into the cloned destination property struct. + err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace) + if err != nil { + // This is fine, it just means the src struct doesn't match. + continue + } + + // Found the prop for the arch, you have. + archToProp[arch] = dstClone + + // Go to the next prop. + break + } + } + } + return archToProp +} diff --git a/android/bazel.go b/android/bazel.go new file mode 100644 index 000000000..9939bd5e2 --- /dev/null +++ b/android/bazel.go @@ -0,0 +1,57 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import "android/soong/bazel" + +// BazelModuleBase contains the property structs with metadata for modules which can be converted to +// Bazel. +type BazelModuleBase struct { + bazelProperties bazel.Properties +} + +// Bazelable is specifies the interface for modules that can be converted to Bazel. +type Bazelable interface { + bazelProps() *bazel.Properties + GetBazelLabel() string + ConvertWithBp2build() bool +} + +// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules. +type BazelModule interface { + Module + Bazelable +} + +// InitBazelModule is a wrapper function that decorates a BazelModule with Bazel-conversion +// properties. +func InitBazelModule(module BazelModule) { + module.AddProperties(module.bazelProps()) +} + +// bazelProps returns the Bazel properties for the given BazelModuleBase. +func (b *BazelModuleBase) bazelProps() *bazel.Properties { + return &b.bazelProperties +} + +// GetBazelLabel returns the Bazel label for the given BazelModuleBase. +func (b *BazelModuleBase) GetBazelLabel() string { + return b.bazelProperties.Bazel_module.Label +} + +// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build. +func (b *BazelModuleBase) ConvertWithBp2build() bool { + return b.bazelProperties.Bazel_module.Bp2build_available +} diff --git a/android/bazel_handler.go b/android/bazel_handler.go index a5c4bedc5..9cd9fadfe 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 %v", 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/config.go b/android/config.go index 6a093fc7a..949119d5a 100644 --- a/android/config.go +++ b/android/config.go @@ -232,7 +232,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string // Copy the real PATH value to the test environment, it's needed by // NonHermeticHostSystemTool() used in x86_darwin_host.go - envCopy["PATH"] = originalEnv["PATH"] + envCopy["PATH"] = os.Getenv("PATH") config := &config{ productVariables: productVariables{ @@ -305,10 +305,7 @@ func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs return testConfig } -// TestArchConfig returns a Config object suitable for using for tests that -// need to run the arch mutator. -func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { - testConfig := TestConfig(buildDir, env, bp, fs) +func modifyTestConfigToSupportArchMutator(testConfig Config) { config := testConfig.config config.Targets = map[OsType][]Target{ @@ -334,7 +331,13 @@ func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[st config.TestProductVariables.DeviceArchVariant = proptools.StringPtr("armv8-a") config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm") config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon") +} +// TestArchConfig returns a Config object suitable for using for tests that +// need to run the arch mutator. +func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { + testConfig := TestConfig(buildDir, env, bp, fs) + modifyTestConfigToSupportArchMutator(testConfig) return testConfig } @@ -913,6 +916,25 @@ func (c *config) XrefCuEncoding() string { return "json" } +// XrefCuJavaSourceMax returns the maximum number of the Java source files +// in a single compilation unit +const xrefJavaSourceFileMaxDefault = "1000" + +func (c Config) XrefCuJavaSourceMax() string { + v := c.Getenv("KYTHE_JAVA_SOURCE_BATCH_SIZE") + if v == "" { + return xrefJavaSourceFileMaxDefault + } + if _, err := strconv.ParseUint(v, 0, 0); err != nil { + fmt.Fprintf(os.Stderr, + "bad KYTHE_JAVA_SOURCE_BATCH_SIZE value: %s, will use %s", + err, xrefJavaSourceFileMaxDefault) + return xrefJavaSourceFileMaxDefault + } + return v + +} + func (c *config) EmitXrefRules() bool { return c.XrefCorpusName() != "" } diff --git a/android/defs.go b/android/defs.go index 38ecb0505..1a767214c 100644 --- a/android/defs.go +++ b/android/defs.go @@ -57,6 +57,15 @@ var ( }, "cpFlags") + // A copy rule that only updates the output if it changed. + CpIfChanged = pctx.AndroidStaticRule("CpIfChanged", + blueprint.RuleParams{ + Command: "if ! cmp -s $in $out; then cp $in $out; fi", + Description: "cp if changed $out", + Restat: true, + }, + "cpFlags") + CpExecutable = pctx.AndroidStaticRule("CpExecutable", blueprint.RuleParams{ Command: "rm -f $out && cp $cpPreserveSymlinks $cpFlags $in $out && chmod +x $out", diff --git a/android/env.go b/android/env.go index 57617e71c..16de86769 100644 --- a/android/env.go +++ b/android/env.go @@ -15,13 +15,10 @@ package android import ( - "fmt" "os" - "os/exec" "strings" - "syscall" - "android/soong/env" + "android/soong/shared" ) // This file supports dependencies on environment variables. During build manifest generation, @@ -32,75 +29,25 @@ import ( // a manifest regeneration. var originalEnv map[string]string -var soongDelveListen string -var soongDelvePath string -var soongDelveEnv []string var SdclangEnv map[string]string var QiifaBuildConfig string -func init() { - // Delve support needs to read this environment variable very early, before NewConfig has created a way to - // access originalEnv with dependencies. Store the value where soong_build can find it, it will manually - // ensure the dependencies are created. - soongDelveListen = os.Getenv("SOONG_DELVE") - soongDelvePath = os.Getenv("SOONG_DELVE_PATH") - if soongDelvePath == "" { - soongDelvePath, _ = exec.LookPath("dlv") - } - originalEnv = make(map[string]string) - soongDelveEnv = []string{} +func InitEnvironment(envFile string) { + var err error + originalEnv, err = shared.EnvFromFile(envFile) + if err != nil { + panic(err) + } + // TODO(b/182662723) move functionality as necessary SdclangEnv = make(map[string]string) for _, env := range os.Environ() { idx := strings.IndexRune(env, '=') if idx != -1 { - originalEnv[env[:idx]] = env[idx+1:] - if env[:idx] != "SOONG_DELVE" && env[:idx] != "SOONG_DELVE_PATH" { - soongDelveEnv = append(soongDelveEnv, env) - } SdclangEnv[env[:idx]] = env[idx+1:] } } QiifaBuildConfig = originalEnv["QIIFA_BUILD_CONFIG"] - // Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment - // variable values. The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc. - os.Clearenv() -} - -func ReexecWithDelveMaybe() { - if soongDelveListen == "" { - return - } - - if soongDelvePath == "" { - fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv") - os.Exit(1) - } - dlvArgv := []string{ - soongDelvePath, - "--listen=:" + soongDelveListen, - "--headless=true", - "--api-version=2", - "exec", - os.Args[0], - "--", - } - dlvArgv = append(dlvArgv, os.Args[1:]...) - os.Chdir(absSrcDir) - syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv) - fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve") - os.Exit(1) -} - -// getenv checks either os.Getenv or originalEnv so that it works before or after the init() -// function above. It doesn't add any dependencies on the environment variable, so it should -// only be used for values that won't change. For values that might change use ctx.Config().Getenv. -func getenv(key string) string { - if originalEnv == nil { - return os.Getenv(key) - } else { - return originalEnv[key] - } } func EnvSingleton() Singleton { @@ -112,12 +59,12 @@ type envSingleton struct{} func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) { envDeps := ctx.Config().EnvDeps() - envFile := PathForOutput(ctx, ".soong.environment") + envFile := PathForOutput(ctx, "soong.environment.used") if ctx.Failed() { return } - data, err := env.EnvFileContents(envDeps) + data, err := shared.EnvFileContents(envDeps) if err != nil { ctx.Errorf(err.Error()) } diff --git a/android/filegroup.go b/android/filegroup.go index 674a196cb..2eb474187 100644 --- a/android/filegroup.go +++ b/android/filegroup.go @@ -24,6 +24,10 @@ func init() { RegisterBp2BuildMutator("filegroup", FilegroupBp2Build) } +var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("filegroup", FileGroupFactory) +}) + // https://docs.bazel.build/versions/master/be/general.html#filegroup type bazelFilegroupAttributes struct { Srcs bazel.LabelList @@ -49,7 +53,7 @@ func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {} func FilegroupBp2Build(ctx TopDownMutatorContext) { fg, ok := ctx.Module().(*fileGroup) - if !ok || !fg.properties.Bazel_module.Bp2build_available { + if !ok || !fg.ConvertWithBp2build() { return } @@ -57,9 +61,9 @@ func FilegroupBp2Build(ctx TopDownMutatorContext) { Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs), } - props := bazel.NewBazelTargetModuleProperties(fg.Name(), "filegroup", "") + props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"} - ctx.CreateBazelTargetModule(BazelFileGroupFactory, props, attrs) + ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs) } type fileGroupProperties struct { @@ -77,13 +81,11 @@ type fileGroupProperties struct { // Create a make variable with the specified name that contains the list of files in the // filegroup, relative to the root of the source tree. Export_to_make_var *string - - // Properties for Bazel migration purposes. - bazel.Properties } type fileGroup struct { ModuleBase + BazelModuleBase properties fileGroupProperties srcs Paths } @@ -97,6 +99,7 @@ func FileGroupFactory() Module { module := &fileGroup{} module.AddProperties(&module.properties) InitAndroidModule(module) + InitBazelModule(module) return module } diff --git a/android/fixture.go b/android/fixture.go new file mode 100644 index 000000000..485ca2321 --- /dev/null +++ b/android/fixture.go @@ -0,0 +1,810 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "fmt" + "reflect" + "strings" + "testing" +) + +// Provides support for creating test fixtures on which tests can be run. Reduces duplication +// of test setup by allow tests to easily reuse setup code. +// +// Fixture +// ======= +// These determine the environment within which a test can be run. Fixtures are mutable and are +// created by FixtureFactory instances and mutated by FixturePreparer instances. They are created by +// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer +// instances to it to modify the environment. +// +// FixtureFactory +// ============== +// These are responsible for creating fixtures. Factories are immutable and are intended to be +// initialized once and reused to create multiple fixtures. Each factory has a list of fixture +// preparers that prepare a fixture for running a test. Factories can also be used to create other +// factories by extending them with additional fixture preparers. +// +// FixturePreparer +// =============== +// These are responsible for modifying a Fixture in preparation for it to run a test. Preparers are +// intended to be immutable and able to prepare multiple Fixture objects simultaneously without +// them sharing any data. +// +// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of +// FixturePreparers are flattened and deduped while preserving the order they first appear in the +// list. This makes it easy to reuse, group and combine FixturePreparers together. +// +// Each small self contained piece of test setup should be their own FixturePreparer. e.g. +// * A group of related modules. +// * A group of related mutators. +// * A combination of both. +// * Configuration. +// +// They should not overlap, e.g. the same module type should not be registered by different +// FixturePreparers as using them both would cause a build error. In that case the preparer should +// be split into separate parts and combined together using FixturePreparers(...). +// +// e.g. attempting to use AllPreparers in preparing a Fixture would break as it would attempt to +// register module bar twice: +// var Preparer1 = FixtureRegisterWithContext(RegisterModuleFooAndBar) +// var Preparer2 = FixtureRegisterWithContext(RegisterModuleBarAndBaz) +// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2) +// +// However, when restructured like this it would work fine: +// var PreparerFoo = FixtureRegisterWithContext(RegisterModuleFoo) +// var PreparerBar = FixtureRegisterWithContext(RegisterModuleBar) +// var PreparerBaz = FixtureRegisterWithContext(RegisterModuleBaz) +// var Preparer1 = GroupFixturePreparers(RegisterModuleFoo, RegisterModuleBar) +// var Preparer2 = GroupFixturePreparers(RegisterModuleBar, RegisterModuleBaz) +// var AllPreparers = GroupFixturePreparers(Preparer1, Preparer2) +// +// As after deduping and flattening AllPreparers would result in the following preparers being +// applied: +// 1. PreparerFoo +// 2. PreparerBar +// 3. PreparerBaz +// +// Preparers can be used for both integration and unit tests. +// +// Integration tests typically use all the module types, mutators and singletons that are available +// for that package to try and replicate the behavior of the runtime build as closely as possible. +// However, that realism comes at a cost of increased fragility (as they can be broken by changes in +// many different parts of the build) and also increased runtime, especially if they use lots of +// singletons and mutators. +// +// Unit tests on the other hand try and minimize the amount of code being tested which makes them +// less susceptible to changes elsewhere in the build and quick to run but at a cost of potentially +// not testing realistic scenarios. +// +// Supporting unit tests effectively require that preparers are available at the lowest granularity +// possible. Supporting integration tests effectively require that the preparers are organized into +// groups that provide all the functionality available. +// +// At least in terms of tests that check the behavior of build components via processing +// `Android.bp` there is no clear separation between a unit test and an integration test. Instead +// they vary from one end that tests a single module (e.g. filegroup) to the other end that tests a +// whole system of modules, mutators and singletons (e.g. apex + hiddenapi). +// +// TestResult +// ========== +// These are created by running tests in a Fixture and provide access to the Config and TestContext +// in which the tests were run. +// +// Example +// ======= +// +// An exported preparer for use by other packages that need to use java modules. +// +// package java +// var PrepareForIntegrationTestWithJava = GroupFixturePreparers( +// android.PrepareForIntegrationTestWithAndroid, +// FixtureRegisterWithContext(RegisterAGroupOfRelatedModulesMutatorsAndSingletons), +// FixtureRegisterWithContext(RegisterAnotherGroupOfRelatedModulesMutatorsAndSingletons), +// ... +// ) +// +// Some files to use in tests in the java package. +// +// var javaMockFS = android.MockFS{ +// "api/current.txt": nil, +// "api/removed.txt": nil, +// ... +// } +// +// A package private factory for use for testing java within the java package. +// +// var javaFixtureFactory = NewFixtureFactory( +// PrepareForIntegrationTestWithJava, +// FixtureRegisterWithContext(func(ctx android.RegistrationContext) { +// ctx.RegisterModuleType("test_module", testModule) +// }), +// javaMockFS.AddToFixture(), +// ... +// } +// +// func TestJavaStuff(t *testing.T) { +// result := javaFixtureFactory.RunTest(t, +// android.FixtureWithRootAndroidBp(`java_library {....}`), +// android.MockFS{...}.AddToFixture(), +// ) +// ... test result ... +// } +// +// package cc +// var PrepareForTestWithCC = GroupFixturePreparers( +// android.PrepareForArchMutator, +// android.prepareForPrebuilts, +// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest), +// ... +// ) +// +// package apex +// +// var PrepareForApex = GroupFixturePreparers( +// ... +// ) +// +// Use modules and mutators from java, cc and apex. Any duplicate preparers (like +// android.PrepareForArchMutator) will be automatically deduped. +// +// var apexFixtureFactory = android.NewFixtureFactory( +// PrepareForJava, +// PrepareForCC, +// PrepareForApex, +// ) + +// Factory for Fixture objects. +// +// This is configured with a set of FixturePreparer objects that are used to +// initialize each Fixture instance this creates. +type FixtureFactory interface { + + // Creates a copy of this instance and adds some additional preparers. + // + // Before the preparers are used they are combined with the preparers provided when the factory + // was created, any groups of preparers are flattened, and the list is deduped so that each + // preparer is only used once. See the file documentation in android/fixture.go for more details. + Extend(preparers ...FixturePreparer) FixtureFactory + + // Create a Fixture. + Fixture(t *testing.T, preparers ...FixturePreparer) Fixture + + // ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler + // to check the errors (may be 0) reported by the test. + // + // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any + // errors are reported. + ExtendWithErrorHandler(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 + + // Run the test with the supplied Android.bp file. + // + // Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp)) + RunTestWithBp(t *testing.T, bp string) *TestResult +} + +// Create a new FixtureFactory that will apply the supplied preparers. +// +// The buildDirSupplier is a pointer to the package level buildDir variable that is initialized by +// the package level setUp method. It has to be a pointer to the variable as the variable will not +// have been initialized at the time the factory is created. +func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory { + return &fixtureFactory{ + buildDirSupplier: buildDirSupplier, + preparers: dedupAndFlattenPreparers(nil, preparers), + + // Set the default error handler. + errorHandler: FixtureExpectsNoErrors, + } +} + +// A set of mock files to add to the mock file system. +type MockFS map[string][]byte + +func (fs MockFS) Merge(extra map[string][]byte) { + for p, c := range extra { + fs[p] = c + } +} + +func (fs MockFS) AddToFixture() FixturePreparer { + return FixtureMergeMockFs(fs) +} + +// Modify the config +func FixtureModifyConfig(mutator func(config Config)) FixturePreparer { + return newSimpleFixturePreparer(func(f *fixture) { + mutator(f.config) + }) +} + +// Modify the config and context +func FixtureModifyConfigAndContext(mutator func(config Config, ctx *TestContext)) FixturePreparer { + return newSimpleFixturePreparer(func(f *fixture) { + mutator(f.config, f.ctx) + }) +} + +// Modify the context +func FixtureModifyContext(mutator func(ctx *TestContext)) FixturePreparer { + return newSimpleFixturePreparer(func(f *fixture) { + mutator(f.ctx) + }) +} + +func FixtureRegisterWithContext(registeringFunc func(ctx RegistrationContext)) FixturePreparer { + return FixtureModifyContext(func(ctx *TestContext) { registeringFunc(ctx) }) +} + +// Modify the mock filesystem +func FixtureModifyMockFS(mutator func(fs MockFS)) FixturePreparer { + return newSimpleFixturePreparer(func(f *fixture) { + mutator(f.mockFS) + }) +} + +// Merge the supplied file system into the mock filesystem. +// +// Paths that already exist in the mock file system are overridden. +func FixtureMergeMockFs(mockFS MockFS) FixturePreparer { + return FixtureModifyMockFS(func(fs MockFS) { + fs.Merge(mockFS) + }) +} + +// Add a file to the mock filesystem +func FixtureAddFile(path string, contents []byte) FixturePreparer { + return FixtureModifyMockFS(func(fs MockFS) { + fs[path] = contents + }) +} + +// Add a text file to the mock filesystem +func FixtureAddTextFile(path string, contents string) FixturePreparer { + return FixtureAddFile(path, []byte(contents)) +} + +// Add the root Android.bp file with the supplied contents. +func FixtureWithRootAndroidBp(contents string) FixturePreparer { + return FixtureAddTextFile("Android.bp", contents) +} + +// Merge some environment variables into the fixture. +func FixtureMergeEnv(env map[string]string) FixturePreparer { + return FixtureModifyConfig(func(config Config) { + for k, v := range env { + if k == "PATH" { + panic("Cannot set PATH environment variable") + } + config.env[k] = v + } + }) +} + +// Modify the env. +// +// Will panic if the mutator changes the PATH environment variable. +func FixtureModifyEnv(mutator func(env map[string]string)) FixturePreparer { + return FixtureModifyConfig(func(config Config) { + oldPath := config.env["PATH"] + mutator(config.env) + newPath := config.env["PATH"] + if newPath != oldPath { + panic(fmt.Errorf("Cannot change PATH environment variable from %q to %q", oldPath, newPath)) + } + }) +} + +// Allow access to the product variables when preparing the fixture. +type FixtureProductVariables struct { + *productVariables +} + +// Modify product variables. +func FixtureModifyProductVariables(mutator func(variables FixtureProductVariables)) FixturePreparer { + return FixtureModifyConfig(func(config Config) { + productVariables := FixtureProductVariables{&config.productVariables} + mutator(productVariables) + }) +} + +// GroupFixturePreparers creates a composite FixturePreparer that is equivalent to applying each of +// the supplied FixturePreparer instances in order. +// +// Before preparing the fixture the list of preparers is flattened by replacing each +// instance of GroupFixturePreparers with its contents. +func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer { + return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)} +} + +type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer) + +// FixturePreparer is an opaque interface that can change a fixture. +type FixturePreparer interface { + // visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer, + visit(simpleFixturePreparerVisitor) +} + +type fixturePreparers []FixturePreparer + +func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) { + for _, p := range f { + p.visit(visitor) + } +} + +// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer +// instances. +// +// base - a list of already flattened and deduped preparers that will be applied first before +// the list of additional preparers. Any duplicates of these in the additional preparers +// will be ignored. +// +// preparers - a list of additional unflattened, undeduped preparers that will be applied after the +// base preparers. +// +// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers. +func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer { + var list []*simpleFixturePreparer + visited := make(map[*simpleFixturePreparer]struct{}) + + // Mark the already flattened and deduped preparers, if any, as having been seen so that + // duplicates of these in the additional preparers will be discarded. + for _, s := range base { + visited[s] = struct{}{} + } + + preparers.visit(func(preparer *simpleFixturePreparer) { + if _, seen := visited[preparer]; !seen { + visited[preparer] = struct{}{} + list = append(list, preparer) + } + }) + return list +} + +// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers. +type compositeFixturePreparer struct { + preparers []*simpleFixturePreparer +} + +func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) { + for _, p := range c.preparers { + p.visit(visitor) + } +} + +// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture. +type simpleFixturePreparer struct { + function func(fixture *fixture) +} + +func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) { + visitor(s) +} + +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) +} + +type simpleErrorHandler struct { + function func(result *TestResult) +} + +func (h simpleErrorHandler) CheckErrors(result *TestResult) { + result.Helper() + h.function(result) +} + +// The default fixture error handler. +// +// Will fail the test immediately if any errors are reported. +// +// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within +// which the test is being run which means that the RunTest() method will not return. +var FixtureExpectsNoErrors = FixtureCustomErrorHandler( + func(result *TestResult) { + result.Helper() + FailIfErrored(result.T, result.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. +// +// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within +// which the test is being run which means that the RunTest() method will not return. +func FixtureExpectsAtLeastOneErrorMatchingPattern(pattern string) FixtureErrorHandler { + return FixtureCustomErrorHandler(func(result *TestResult) { + result.Helper() + if !FailIfNoMatchingErrors(result.T, pattern, result.Errs) { + result.FailNow() + } + }) +} + +// 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. +// +// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within +// which the test is being run which means that the RunTest() method will not return. +func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandler { + return FixtureCustomErrorHandler(func(result *TestResult) { + result.Helper() + CheckErrorsAgainstExpectations(result.T, result.Errs, patterns) + }) +} + +// FixtureCustomErrorHandler creates a custom error handler +func FixtureCustomErrorHandler(function func(result *TestResult)) FixtureErrorHandler { + return simpleErrorHandler{ + function: function, + } +} + +// Fixture defines the test environment. +type Fixture interface { + // Run the test, checking any errors reported and returning a TestResult instance. + RunTest() *TestResult +} + +// Provides general test support. +type TestHelper struct { + *testing.T +} + +// AssertBoolEquals checks if the expected and actual values are equal and if they are not then it +// reports an error prefixed with the supplied message and including a reason for why it failed. +func (h *TestHelper) AssertBoolEquals(message string, expected bool, actual bool) { + h.Helper() + if actual != expected { + h.Errorf("%s: expected %t, actual %t", message, expected, actual) + } +} + +// AssertStringEquals checks if the expected and actual values are equal and if they are not then +// it reports an error prefixed with the supplied message and including a reason for why it failed. +func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) { + h.Helper() + if actual != expected { + h.Errorf("%s: expected %s, actual %s", message, expected, actual) + } +} + +// AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does +// not then this reports an error prefixed with the supplied message and including a reason for why +// it failed. +func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) { + h.Helper() + if actual == nil { + h.Errorf("Expected error but was nil") + } else if actual.Error() != expected { + h.Errorf("%s: expected %s, actual %s", message, expected, actual.Error()) + } +} + +// AssertTrimmedStringEquals checks if the expected and actual values are the same after trimming +// leading and trailing spaces from them both. If they are not then it reports an error prefixed +// with the supplied message and including a reason for why it failed. +func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) { + h.Helper() + h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual)) +} + +// AssertStringDoesContain checks if the string contains the expected substring. If it does not +// then it reports an error prefixed with the supplied message and including a reason for why it +// failed. +func (h *TestHelper) AssertStringDoesContain(message string, s string, expectedSubstring string) { + h.Helper() + if !strings.Contains(s, expectedSubstring) { + h.Errorf("%s: could not find %q within %q", message, expectedSubstring, s) + } +} + +// AssertStringDoesNotContain checks if the string contains the expected substring. If it does then +// it reports an error prefixed with the supplied message and including a reason for why it failed. +func (h *TestHelper) AssertStringDoesNotContain(message string, s string, unexpectedSubstring string) { + h.Helper() + if strings.Contains(s, unexpectedSubstring) { + h.Errorf("%s: unexpectedly found %q within %q", message, unexpectedSubstring, s) + } +} + +// AssertArrayString checks if the expected and actual values are equal and if they are not then it +// reports an error prefixed with the supplied message and including a reason for why it failed. +func (h *TestHelper) AssertArrayString(message string, expected, actual []string) { + h.Helper() + if len(actual) != len(expected) { + h.Errorf("%s: expected %d (%q), actual (%d) %q", message, len(expected), expected, len(actual), actual) + return + } + for i := range actual { + if actual[i] != expected[i] { + h.Errorf("%s: expected %d-th, %q (%q), actual %q (%q)", + message, i, expected[i], expected, actual[i], actual) + return + } + } +} + +// AssertArrayString checks if the expected and actual values are equal using reflect.DeepEqual and +// if they are not then it reports an error prefixed with the supplied message and including a +// reason for why it failed. +func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) { + h.Helper() + if !reflect.DeepEqual(actual, expected) { + h.Errorf("%s: expected:\n %#v\n got:\n %#v", message, expected, actual) + } +} + +// AssertPanic checks that the supplied function panics as expected. +func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) { + h.Helper() + panicked := false + func() { + defer func() { + if x := recover(); x != nil { + panicked = true + } + }() + funcThatShouldPanic() + }() + if !panicked { + h.Error(message) + } +} + +// Struct to allow TestResult to embed a *TestContext and allow call forwarding to its methods. +type testContext struct { + *TestContext +} + +// The result of running a test. +type TestResult struct { + TestHelper + testContext + + fixture *fixture + Config Config + + // The errors that were reported during the test. + Errs []error +} + +var _ FixtureFactory = (*fixtureFactory)(nil) + +type fixtureFactory struct { + buildDirSupplier *string + preparers []*simpleFixturePreparer + errorHandler FixtureErrorHandler +} + +func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory { + // Create a new slice to avoid accidentally sharing the preparers slice from this factory with + // the extending factories. + var all []*simpleFixturePreparer + all = append(all, f.preparers...) + all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...) + // 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), + errorHandler: f.errorHandler, + } + + for _, preparer := range f.preparers { + preparer.function(fixture) + } + + for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) { + preparer.function(fixture) + } + + return fixture +} + +func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory { + newFactory := &fixtureFactory{} + *newFactory = *f + newFactory.errorHandler = errorHandler + return newFactory +} + +func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult { + t.Helper() + fixture := f.Fixture(t, preparers...) + return fixture.RunTest() +} + +func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult { + t.Helper() + return f.RunTest(t, FixtureWithRootAndroidBp(bp)) +} + +type fixture struct { + // The factory used to create this fixture. + factory *fixtureFactory + + // 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 { + f.t.Helper() + + ctx := f.ctx + + // The TestConfig() method assumes that the mock filesystem is available when creating so creates + // the mock file system immediately. Similarly, the NewTestContext(Config) method assumes that the + // supplied Config's FileSystem has been properly initialized before it is called and so it takes + // its own reference to the filesystem. However, fixtures create the Config and TestContext early + // so they can be modified by preparers at which time the mockFS has not been populated (because + // it too is modified by preparers). So, this reinitializes the Config and TestContext's + // FileSystem using the now populated mockFS. + f.config.mockFileSystem("", f.mockFS) + ctx.SetFs(ctx.config.fs) + if ctx.config.mockBpList != "" { + ctx.SetModuleListFile(ctx.config.mockBpList) + } + + ctx.Register() + _, errs := ctx.ParseBlueprintsFiles("ignored") + if len(errs) == 0 { + _, errs = ctx.PrepareBuildActions(f.config) + } + + result := &TestResult{ + TestHelper: TestHelper{T: f.t}, + testContext: testContext{ctx}, + fixture: f, + Config: f.config, + Errs: errs, + } + + f.errorHandler.CheckErrors(result) + + return result +} + +// NormalizePathForTesting removes the test invocation specific build directory from the supplied +// path. +// +// If the path is within the build directory (e.g. an OutputPath) then this returns the relative +// path to avoid tests having to deal with the dynamically generated build directory. +// +// Otherwise, this returns the supplied path as it is almost certainly a source path that is +// relative to the root of the source tree. +// +// Even though some information is removed from some paths and not others it should be possible to +// differentiate between them by the paths themselves, e.g. output paths will likely include +// ".intermediates" but source paths won't. +func (r *TestResult) NormalizePathForTesting(path Path) string { + pathContext := PathContextForTesting(r.Config) + pathAsString := path.String() + if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel { + return rel + } + return pathAsString +} + +// NormalizePathsForTesting normalizes each path in the supplied list and returns their normalized +// forms. +func (r *TestResult) NormalizePathsForTesting(paths Paths) []string { + var result []string + for _, path := range paths { + result = append(result, r.NormalizePathForTesting(path)) + } + return result +} + +// NewFixture creates a new test fixture that is based on the one that created this result. It is +// intended to test the output of module types that generate content to be processed by the build, +// e.g. sdk snapshots. +func (r *TestResult) NewFixture(preparers ...FixturePreparer) Fixture { + return r.fixture.factory.Fixture(r.T, preparers...) +} + +// RunTest is shorthand for NewFixture(preparers...).RunTest(). +func (r *TestResult) RunTest(preparers ...FixturePreparer) *TestResult { + r.Helper() + return r.fixture.factory.Fixture(r.T, preparers...).RunTest() +} + +// Module returns the module with the specific name and of the specified variant. +func (r *TestResult) Module(name string, variant string) Module { + return r.ModuleForTests(name, variant).Module() +} + +// Create a *TestResult object suitable for use within a subtest. +// +// This ensures that any errors reported by the TestResult, e.g. from within one of its +// Assert... methods, will be associated with the sub test and not the main test. +// +// result := ....RunTest() +// t.Run("subtest", func(t *testing.T) { +// subResult := result.ResultForSubTest(t) +// subResult.AssertStringEquals("something", ....) +// }) +func (r *TestResult) ResultForSubTest(t *testing.T) *TestResult { + subTestResult := *r + r.T = t + return &subTestResult +} diff --git a/android/fixture_test.go b/android/fixture_test.go new file mode 100644 index 000000000..5a7bf3b6e --- /dev/null +++ b/android/fixture_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import "testing" + +// Make sure that FixturePreparer instances are only called once per fixture and in the order in +// which they were added. +func TestFixtureDedup(t *testing.T) { + list := []string{} + + appendToList := func(s string) FixturePreparer { + return FixtureModifyConfig(func(_ Config) { + list = append(list, s) + }) + } + + preparer1 := appendToList("preparer1") + preparer2 := appendToList("preparer2") + preparer3 := appendToList("preparer3") + preparer4 := appendToList("preparer4") + + preparer1Then2 := GroupFixturePreparers(preparer1, preparer2) + + preparer2Then1 := GroupFixturePreparers(preparer2, preparer1) + + buildDir := "build" + factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2) + + extension := factory.Extend(preparer4, preparer2) + + extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3) + + h := TestHelper{t} + h.AssertDeepEquals("preparers called in wrong order", + []string{"preparer1", "preparer2", "preparer4", "preparer3"}, list) +} diff --git a/android/license.go b/android/license.go index b140b55b1..3bc6199b2 100644 --- a/android/license.go +++ b/android/license.go @@ -19,7 +19,7 @@ import ( ) type licenseKindDependencyTag struct { - blueprint.BaseDependencyTag + blueprint.BaseDependencyTag } var ( diff --git a/android/license_test.go b/android/license_test.go index 552bbaee0..9f6871374 100644 --- a/android/license_test.go +++ b/android/license_test.go @@ -49,9 +49,9 @@ var licenseTests = []struct { }`), }, expectedErrors: []string{ - `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice `+ + `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice ` + `which is not visible to this module`, - `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice `+ + `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice ` + `which is not visible to this module`, }, }, @@ -70,7 +70,7 @@ var licenseTests = []struct { }`), }, expectedErrors: []string{ - `top/Blueprints:6:5: module "top_proprietary": license_kinds property `+ + `top/Blueprints:6:5: module "top_proprietary": license_kinds property ` + `"top_by_exception_only" is not a license_kind module`, }, }, diff --git a/android/licenses.go b/android/licenses.go index 100042911..2838f5d28 100644 --- a/android/licenses.go +++ b/android/licenses.go @@ -51,7 +51,7 @@ type applicableLicensesPropertyImpl struct { func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty { return applicableLicensesPropertyImpl{ - name: name, + name: name, licensesProperty: licensesProperty, } } diff --git a/android/licenses_test.go b/android/licenses_test.go index b94add7cf..c04379108 100644 --- a/android/licenses_test.go +++ b/android/licenses_test.go @@ -7,15 +7,15 @@ import ( ) var licensesTests = []struct { - name string - fs map[string][]byte - expectedErrors []string - effectiveLicenses map[string][]string - effectiveInheritedLicenses map[string][]string - effectivePackage map[string]string - effectiveNotices map[string][]string - effectiveKinds map[string][]string - effectiveConditions map[string][]string + name string + fs map[string][]byte + expectedErrors []string + effectiveLicenses map[string][]string + effectiveInheritedLicenses map[string][]string + effectivePackage map[string]string + effectiveNotices map[string][]string + effectiveKinds map[string][]string + effectiveConditions map[string][]string }{ { name: "invalid module type without licenses property", @@ -71,28 +71,28 @@ var licensesTests = []struct { }, effectiveLicenses: map[string][]string{ "libexample1": []string{"top_Apache2"}, - "libnested": []string{"top_Apache2"}, - "libother": []string{"top_Apache2"}, + "libnested": []string{"top_Apache2"}, + "libother": []string{"top_Apache2"}, }, effectiveKinds: map[string][]string{ "libexample1": []string{"notice"}, - "libnested": []string{"notice"}, - "libother": []string{"notice"}, + "libnested": []string{"notice"}, + "libother": []string{"notice"}, }, effectivePackage: map[string]string{ "libexample1": "topDog", - "libnested": "topDog", - "libother": "topDog", + "libnested": "topDog", + "libother": "topDog", }, effectiveConditions: map[string][]string{ "libexample1": []string{"shownotice"}, - "libnested": []string{"shownotice"}, - "libother": []string{"shownotice"}, + "libnested": []string{"shownotice"}, + "libother": []string{"shownotice"}, }, effectiveNotices: map[string][]string{ "libexample1": []string{"top/LICENSE", "top/NOTICE"}, - "libnested": []string{"top/LICENSE", "top/NOTICE"}, - "libother": []string{"top/LICENSE", "top/NOTICE"}, + "libnested": []string{"top/LICENSE", "top/NOTICE"}, + "libother": []string{"top/LICENSE", "top/NOTICE"}, }, }, @@ -147,28 +147,28 @@ var licensesTests = []struct { }`), }, effectiveLicenses: map[string][]string{ - "libexample": []string{"nested_other", "top_other"}, + "libexample": []string{"nested_other", "top_other"}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, + "libnested": []string{}, + "libother": []string{}, }, effectiveInheritedLicenses: map[string][]string{ - "libexample": []string{"nested_other", "top_other"}, + "libexample": []string{"nested_other", "top_other"}, "libsamepackage": []string{"nested_other", "top_other"}, - "libnested": []string{"nested_other", "top_other"}, - "libother": []string{"nested_other", "top_other"}, + "libnested": []string{"nested_other", "top_other"}, + "libother": []string{"nested_other", "top_other"}, }, effectiveKinds: map[string][]string{ - "libexample": []string{"nested_notice", "top_notice"}, + "libexample": []string{"nested_notice", "top_notice"}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, + "libnested": []string{}, + "libother": []string{}, }, effectiveConditions: map[string][]string{ - "libexample": []string{"notice"}, + "libexample": []string{"notice"}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, + "libnested": []string{}, + "libother": []string{}, }, }, { @@ -218,32 +218,32 @@ var licensesTests = []struct { }`), }, effectiveLicenses: map[string][]string{ - "libexample": []string{"other", "top_nested"}, + "libexample": []string{"other", "top_nested"}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, - "liboutsider": []string{}, + "libnested": []string{}, + "libother": []string{}, + "liboutsider": []string{}, }, effectiveInheritedLicenses: map[string][]string{ - "libexample": []string{"other", "top_nested"}, + "libexample": []string{"other", "top_nested"}, "libsamepackage": []string{"other", "top_nested"}, - "libnested": []string{"other", "top_nested"}, - "libother": []string{"other", "top_nested"}, - "liboutsider": []string{"other", "top_nested"}, + "libnested": []string{"other", "top_nested"}, + "libother": []string{"other", "top_nested"}, + "liboutsider": []string{"other", "top_nested"}, }, effectiveKinds: map[string][]string{ - "libexample": []string{}, + "libexample": []string{}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, - "liboutsider": []string{}, + "libnested": []string{}, + "libother": []string{}, + "liboutsider": []string{}, }, effectiveNotices: map[string][]string{ - "libexample": []string{"top/nested/LICENSE.txt"}, + "libexample": []string{"top/nested/LICENSE.txt"}, "libsamepackage": []string{}, - "libnested": []string{}, - "libother": []string{}, - "liboutsider": []string{}, + "libnested": []string{}, + "libother": []string{}, + "liboutsider": []string{}, }, }, @@ -285,11 +285,11 @@ var licensesTests = []struct { }`), }, effectiveLicenses: map[string][]string{ - "libexample": []string{"by_exception_only"}, + "libexample": []string{"by_exception_only"}, "libdefaults": []string{"notice"}, }, effectiveInheritedLicenses: map[string][]string{ - "libexample": []string{"by_exception_only"}, + "libexample": []string{"by_exception_only"}, "libdefaults": []string{"notice"}, }, }, @@ -327,11 +327,11 @@ var licensesTests = []struct { }`), }, effectiveLicenses: map[string][]string{ - "libexample": []string{"top_notice"}, + "libexample": []string{"top_notice"}, "liboutsider": []string{}, }, effectiveInheritedLicenses: map[string][]string{ - "libexample": []string{"top_notice"}, + "libexample": []string{"top_notice"}, "liboutsider": []string{"top_notice"}, }, }, @@ -370,15 +370,15 @@ var licensesTests = []struct { }`), }, effectiveLicenses: map[string][]string{ - "libexample": []string{"top_notice"}, - "libnested": []string{"outsider"}, - "libother": []string{}, + "libexample": []string{"top_notice"}, + "libnested": []string{"outsider"}, + "libother": []string{}, "liboutsider": []string{}, }, effectiveInheritedLicenses: map[string][]string{ - "libexample": []string{"top_notice"}, - "libnested": []string{"outsider"}, - "libother": []string{}, + "libexample": []string{"top_notice"}, + "libnested": []string{"outsider"}, + "libother": []string{}, "liboutsider": []string{"top_notice", "outsider"}, }, }, @@ -449,7 +449,7 @@ var licensesTests = []struct { }, effectiveInheritedLicenses: map[string][]string{ "module": []string{"prebuilt", "top_sources"}, - "other": []string{"prebuilt", "top_sources"}, + "other": []string{"prebuilt", "top_sources"}, }, }, } diff --git a/android/module.go b/android/module.go index bf74cad07..9f923e2d0 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 @@ -507,13 +508,17 @@ type Module interface { type BazelTargetModule interface { Module - BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties + bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties + SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties) + + RuleClass() string + BzlLoadLocation() string } // InitBazelTargetModule is a wrapper function that decorates BazelTargetModule // with property structs containing metadata for bp2build conversion. func InitBazelTargetModule(module BazelTargetModule) { - module.AddProperties(module.BazelTargetModuleProperties()) + module.AddProperties(module.bazelTargetModuleProperties()) InitAndroidModule(module) } @@ -524,11 +529,26 @@ type BazelTargetModuleBase struct { Properties bazel.BazelTargetModuleProperties } -// BazelTargetModuleProperties getter. -func (btmb *BazelTargetModuleBase) BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties { +// bazelTargetModuleProperties getter. +func (btmb *BazelTargetModuleBase) bazelTargetModuleProperties() *bazel.BazelTargetModuleProperties { return &btmb.Properties } +// SetBazelTargetModuleProperties setter for BazelTargetModuleProperties +func (btmb *BazelTargetModuleBase) SetBazelTargetModuleProperties(props bazel.BazelTargetModuleProperties) { + btmb.Properties = props +} + +// RuleClass returns the rule class for this Bazel target +func (b *BazelTargetModuleBase) RuleClass() string { + return b.bazelTargetModuleProperties().Rule_class +} + +// BzlLoadLocation returns the rule class for this Bazel target +func (b *BazelTargetModuleBase) BzlLoadLocation() string { + return b.bazelTargetModuleProperties().Bzl_load_location +} + // Qualified id for a module type qualifiedModuleName struct { // The package (i.e. directory) in which the module is defined, without trailing / @@ -733,7 +753,7 @@ type commonProperties struct { // Whether this module is installed to vendor ramdisk Vendor_ramdisk *bool - // Whether this module is built for non-native architecures (also known as native bridge binary) + // Whether this module is built for non-native architectures (also known as native bridge binary) Native_bridge_supported *bool `android:"arch_variant"` // init.rc files to be installed if this module is installed @@ -1104,8 +1124,15 @@ type ModuleBase struct { variableProperties interface{} hostAndDeviceProperties hostAndDeviceProperties generalProperties []interface{} - archProperties [][]interface{} - customizableProperties []interface{} + + // Arch specific versions of structs in generalProperties. The outer index + // has the same order as generalProperties as initialized in + // InitAndroidArchModule, and the inner index chooses the props specific to + // the architecture. The interface{} value is an archPropRoot that is + // filled with arch specific values by the arch mutator. + archProperties [][]interface{} + + customizableProperties []interface{} // Properties specific to the Blueprint to BUILD migration. bazelTargetModuleProperties bazel.BazelTargetModuleProperties @@ -1149,8 +1176,6 @@ type ModuleBase struct { initRcPaths Paths vintfFragmentsPaths Paths - - prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool } func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {} @@ -1177,10 +1202,6 @@ func (m *ModuleBase) VariablesForTests() map[string]string { return m.variables } -func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, os OsType) bool) { - m.prefer32 = prefer32 -} - // Name returns the name of the module. It may be overridden by individual module types, for // example prebuilts will prepend prebuilt_ to the name. func (m *ModuleBase) Name() string { @@ -1811,6 +1832,18 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) return } + m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) + rcDir := PathForModuleInstall(ctx, "etc", "init") + for _, src := range m.initRcPaths { + ctx.PackageFile(rcDir, filepath.Base(src.String()), src) + } + + m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) + vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest") + for _, src := range m.vintfFragmentsPaths { + ctx.PackageFile(vintfDir, filepath.Base(src.String()), src) + } + // Create the set of tagged dist files after calling GenerateAndroidBuildActions // as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the // output paths being set which must be done before or during @@ -1823,8 +1856,6 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) m.installFiles = append(m.installFiles, ctx.installFiles...) m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...) m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...) - m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) - m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) for k, v := range ctx.phonies { m.phonies[k] = append(m.phonies[k], v...) } diff --git a/android/mutator.go b/android/mutator.go index c38719334..9552aa15c 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -33,22 +33,8 @@ import ( // run FinalDeps mutators (CreateVariations disallowed in this phase) // continue on to GenerateAndroidBuildActions -func registerMutatorsToContext(ctx *blueprint.Context, mutators []*mutator) { - for _, t := range mutators { - var handle blueprint.MutatorHandle - if t.bottomUpMutator != nil { - handle = ctx.RegisterBottomUpMutator(t.name, t.bottomUpMutator) - } else if t.topDownMutator != nil { - handle = ctx.RegisterTopDownMutator(t.name, t.topDownMutator) - } - if t.parallel { - handle.Parallel() - } - } -} - // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing. -func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) { +func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) { mctx := ®isterMutatorsContext{ bazelConversionMode: true, } @@ -80,10 +66,17 @@ func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, preArchMutators, f(mctx) } - registerMutatorsToContext(ctx, mctx.mutators) + mctx.mutators.registerAll(ctx) +} + +// collateGloballyRegisteredMutators constructs the list of mutators that have been registered +// with the InitRegistrationContext and will be used at runtime. +func collateGloballyRegisteredMutators() sortableComponents { + return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps) } -func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) { +// collateRegisteredMutators constructs a single list of mutators from the separate lists. +func collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) sortableComponents { mctx := ®isterMutatorsContext{} register := func(funcs []RegisterMutatorFunc) { @@ -103,11 +96,11 @@ func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalD mctx.finalPhase = true register(finalDeps) - registerMutatorsToContext(ctx, mctx.mutators) + return mctx.mutators } type registerMutatorsContext struct { - mutators []*mutator + mutators sortableComponents finalPhase bool bazelConversionMode bool } @@ -283,7 +276,7 @@ type TopDownMutatorContext interface { // factory method, just like in CreateModule, but also requires // BazelTargetModuleProperties containing additional metadata for the // bp2build codegenerator. - CreateBazelTargetModule(ModuleFactory, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule + CreateBazelTargetModule(ModuleFactory, string, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule } type topDownMutatorContext struct { @@ -473,6 +466,23 @@ func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) Mutator return mutator } +func (mutator *mutator) componentName() string { + return mutator.name +} + +func (mutator *mutator) register(ctx *Context) { + blueprintCtx := ctx.Context + var handle blueprint.MutatorHandle + if mutator.bottomUpMutator != nil { + handle = blueprintCtx.RegisterBottomUpMutator(mutator.name, mutator.bottomUpMutator) + } else if mutator.topDownMutator != nil { + handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator) + } + if mutator.parallel { + handle.Parallel() + } +} + type MutatorHandle interface { Parallel() MutatorHandle } @@ -513,17 +523,25 @@ func registerDepsMutatorBp2Build(ctx RegisterMutatorsContext) { func (t *topDownMutatorContext) CreateBazelTargetModule( factory ModuleFactory, + name string, bazelProps bazel.BazelTargetModuleProperties, attrs interface{}) BazelTargetModule { - if !strings.HasPrefix(*bazelProps.Name, bazel.BazelTargetModuleNamePrefix) { + if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) { panic(fmt.Errorf( - "bp2build error: the bazel target module name must start with '%s': %s", + "The %s name prefix is added automatically, do not set it manually: %s", bazel.BazelTargetModuleNamePrefix, - *bazelProps.Name, - )) + name)) + } + name = bazel.BazelTargetModuleNamePrefix + name + nameProp := struct { + Name *string + }{ + Name: &name, } - return t.CreateModule(factory, &bazelProps, attrs).(BazelTargetModule) + b := t.CreateModule(factory, &nameProp, attrs).(BazelTargetModule) + b.SetBazelTargetModuleProperties(bazelProps) + return b } func (t *topDownMutatorContext) AppendProperties(props ...interface{}) { diff --git a/android/override_module.go b/android/override_module.go index fa0856631..97acc5c36 100644 --- a/android/override_module.go +++ b/android/override_module.go @@ -215,7 +215,14 @@ func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) { ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel() ctx.TopDown("register_override", registerOverrideMutator).Parallel() ctx.BottomUp("perform_override", performOverrideMutator).Parallel() + // overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can + // add deps from overridable properties. ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel() + // Because overridableModuleDepsMutator is run after PrebuiltPostDepsMutator, + // prebuilt's ReplaceDependencies doesn't affect to those deps added by overridable properties. + // By running PrebuiltPostDepsMutator again after overridableModuleDepsMutator, deps via overridable properties + // can be replaced with prebuilts. + ctx.BottomUp("replace_deps_on_prebuilts_for_overridable_deps_again", PrebuiltPostDepsMutator).Parallel() ctx.BottomUp("replace_deps_on_override", replaceDepsOnOverridingModuleMutator).Parallel() } 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/path_properties.go b/android/path_properties.go index 4bb706aa8..853e5a9a7 100644 --- a/android/path_properties.go +++ b/android/path_properties.go @@ -76,52 +76,73 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { var ret []string for _, i := range pathPropertyIndexes { - // Turn an index into a field. - sv := fieldByIndex(v, i) - if !sv.IsValid() { - // Skip properties inside a nil pointer. - continue - } - - // If the field is a non-nil pointer step into it. - if sv.Kind() == reflect.Ptr { - if sv.IsNil() { + var values []reflect.Value + fieldsByIndex(v, i, &values) + for _, sv := range values { + if !sv.IsValid() { + // Skip properties inside a nil pointer. continue } - sv = sv.Elem() - } - // Collect paths from all strings and slices of strings. - switch sv.Kind() { - case reflect.String: - ret = append(ret, sv.String()) - case reflect.Slice: - ret = append(ret, sv.Interface().([]string)...) - default: - panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`, - v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) + // If the field is a non-nil pointer step into it. + if sv.Kind() == reflect.Ptr { + if sv.IsNil() { + continue + } + sv = sv.Elem() + } + + // Collect paths from all strings and slices of strings. + switch sv.Kind() { + case reflect.String: + ret = append(ret, sv.String()) + case reflect.Slice: + ret = append(ret, sv.Interface().([]string)...) + default: + panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`, + v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) + } } } return ret } -// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when -// traversing a nil pointer to a struct. -func fieldByIndex(v reflect.Value, index []int) reflect.Value { +// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track +// nil pointers and it returns multiple values when there's slice of struct. +func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) { + // leaf case if len(index) == 1 { - return v.Field(index[0]) - } - for _, x := range index { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return reflect.Value{} + if isSliceOfStruct(v) { + for i := 0; i < v.Len(); i++ { + *values = append(*values, v.Index(i).Field(index[0])) } - v = v.Elem() + } else { + *values = append(*values, v.Field(index[0])) } - v = v.Field(x) + return } - return v + + // recursion + if v.Kind() == reflect.Ptr { + // don't track nil pointer + if v.IsNil() { + return + } + v = v.Elem() + } else if isSliceOfStruct(v) { + // do the recursion for all elements + for i := 0; i < v.Len(); i++ { + fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values) + } + return + } + fieldsByIndex(v.Field(index[0]), index[1:], values) + return +} + +func isSliceOfStruct(v reflect.Value) bool { + return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct } var pathPropertyIndexesCache OncePer diff --git a/android/path_properties_test.go b/android/path_properties_test.go index f964d9f1e..2aab74835 100644 --- a/android/path_properties_test.go +++ b/android/path_properties_test.go @@ -33,12 +33,21 @@ type pathDepsMutatorTestModule struct { Foo string `android:"path"` } + // nested slices of struct + props3 struct { + X []struct { + Y []struct { + Z []string `android:"path"` + } + } + } + sourceDeps []string } func pathDepsMutatorTestModuleFactory() Module { module := &pathDepsMutatorTestModule{} - module.AddProperties(&module.props, &module.props2) + module.AddProperties(&module.props, &module.props2, &module.props3) InitAndroidArchModule(module, DeviceSupported, MultilibBoth) return module } @@ -73,8 +82,20 @@ func TestPathDepsMutator(t *testing.T) { bar: [":b"], baz: ":c{.bar}", qux: ":d", + x: [ + { + y: [ + { + z: [":x", ":y"], + }, + { + z: [":z"], + }, + ], + }, + ], }`, - deps: []string{"a", "b", "c"}, + deps: []string{"a", "b", "c", "x", "y", "z"}, }, { name: "arch variant", @@ -113,6 +134,18 @@ func TestPathDepsMutator(t *testing.T) { filegroup { name: "d", } + + filegroup { + name: "x", + } + + filegroup { + name: "y", + } + + filegroup { + name: "z", + } ` config := TestArchConfig(buildDir, nil, bp, nil) diff --git a/android/paths.go b/android/paths.go index ada4da692..3f4d3f281 100644 --- a/android/paths.go +++ b/android/paths.go @@ -1662,9 +1662,9 @@ func modulePartition(ctx ModuleInstallPathContext, os OsType) string { // on a device without a dedicated recovery partition, install the // recovery variant. if ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() { - partition = "vendor-ramdisk/first_stage_ramdisk" + partition = "vendor_ramdisk/first_stage_ramdisk" } else { - partition = "vendor-ramdisk" + partition = "vendor_ramdisk" } if !ctx.InstallInRoot() { partition += "/system" 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 9ac38750c..32af5df4b 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -262,7 +262,7 @@ var prebuiltsTests = []struct { } func TestPrebuilts(t *testing.T) { - fs := map[string][]byte{ + fs := MockFS{ "prebuilt_file": nil, "source_file": nil, } @@ -277,32 +277,33 @@ func TestPrebuilts(t *testing.T) { deps: [":bar"], }` } - config := TestArchConfig(buildDir, nil, bp, fs) // Add windows to the target list to test the logic when a variant is // disabled by default. if !Windows.DefaultDisabled { t.Errorf("windows is assumed to be disabled by default") } - config.config.Targets[Windows] = []Target{ - {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true}, - } - - ctx := NewTestArchContext(config) - registerTestPrebuiltBuildComponents(ctx) - ctx.RegisterModuleType("filegroup", FileGroupFactory) - ctx.Register() - _, errs := ctx.ParseBlueprintsFiles("Android.bp") - FailIfErrored(t, errs) - _, errs = ctx.PrepareBuildActions(config) - FailIfErrored(t, errs) + result := emptyTestFixtureFactory.Extend( + PrepareForTestWithArchMutator, + PrepareForTestWithPrebuilts, + PrepareForTestWithOverrides, + PrepareForTestWithFilegroup, + // Add a Windows target to the configuration. + FixtureModifyConfig(func(config Config) { + config.Targets[Windows] = []Target{ + {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true}, + } + }), + fs.AddToFixture(), + FixtureRegisterWithContext(registerTestPrebuiltModules), + ).RunTestWithBp(t, bp) - for _, variant := range ctx.ModuleVariantsForTests("foo") { - foo := ctx.ModuleForTests("foo", variant) + for _, variant := range result.ModuleVariantsForTests("foo") { + foo := result.ModuleForTests("foo", variant) t.Run(foo.Module().Target().Os.String(), func(t *testing.T) { var dependsOnSourceModule, dependsOnPrebuiltModule bool - ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) { + result.VisitDirectDeps(foo.Module(), func(m blueprint.Module) { if _, ok := m.(*sourceModule); ok { dependsOnSourceModule = true } @@ -381,14 +382,20 @@ func TestPrebuilts(t *testing.T) { } func registerTestPrebuiltBuildComponents(ctx RegistrationContext) { - ctx.RegisterModuleType("prebuilt", newPrebuiltModule) - ctx.RegisterModuleType("source", newSourceModule) - ctx.RegisterModuleType("override_source", newOverrideSourceModule) + registerTestPrebuiltModules(ctx) RegisterPrebuiltMutators(ctx) ctx.PostDepsMutators(RegisterOverridePostDepsMutators) } +var prepareForTestWithFakePrebuiltModules = FixtureRegisterWithContext(registerTestPrebuiltModules) + +func registerTestPrebuiltModules(ctx RegistrationContext) { + ctx.RegisterModuleType("prebuilt", newPrebuiltModule) + ctx.RegisterModuleType("source", newSourceModule) + ctx.RegisterModuleType("override_source", newOverrideSourceModule) +} + type prebuiltModule struct { ModuleBase prebuilt Prebuilt diff --git a/android/queryview.go b/android/queryview.go index 9e3e45a32..12d14dfb6 100644 --- a/android/queryview.go +++ b/android/queryview.go @@ -66,12 +66,20 @@ func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bazelQueryView := ctx.Rule(pctx, "bazelQueryView", blueprint.RuleParams{ Command: fmt.Sprintf( - "rm -rf ${outDir}/* && "+ - "%s --bazel_queryview_dir ${outDir} %s && "+ - "echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d", - primaryBuilder.String(), - strings.Join(os.Args[1:], " "), + `rm -rf "${outDir}/"* && `+ + `mkdir -p "${outDir}" && `+ + `echo WORKSPACE: cat "%s" > "${outDir}/.queryview-depfile.d" && `+ + `BUILDER="%s" && `+ + `echo BUILDER=$$BUILDER && `+ + `cd "$$(dirname "$$BUILDER")" && `+ + `echo PWD=$$PWD && `+ + `ABSBUILDER="$$PWD/$$(basename "$$BUILDER")" && `+ + `echo ABSBUILDER=$$ABSBUILDER && `+ + `cd / && `+ + `env -i "$$ABSBUILDER" --bazel_queryview_dir "${outDir}" "%s"`, moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile. + primaryBuilder.String(), + strings.Join(os.Args[1:], "\" \""), ), CommandDeps: []string{primaryBuilder.String()}, Description: fmt.Sprintf( diff --git a/android/register.go b/android/register.go index 18c743f19..c9e66e966 100644 --- a/android/register.go +++ b/android/register.go @@ -21,21 +21,78 @@ import ( "github.com/google/blueprint" ) +// A sortable component is one whose registration order affects the order in which it is executed +// and so affects the behavior of the build system. As a result it is important for the order in +// which they are registered during tests to match the order used at runtime and so the test +// infrastructure will sort them to match. +// +// The sortable components are mutators, singletons and pre-singletons. Module types are not +// sortable because their order of registration does not affect the runtime behavior. +type sortableComponent interface { + // componentName returns the name of the component. + // + // Uniquely identifies the components within the set of components used at runtimr and during + // tests. + componentName() string + + // register registers this component in the supplied context. + register(ctx *Context) +} + +type sortableComponents []sortableComponent + +// registerAll registers all components in this slice with the supplied context. +func (r sortableComponents) registerAll(ctx *Context) { + for _, c := range r { + c.register(ctx) + } +} + type moduleType struct { name string factory ModuleFactory } +func (t moduleType) register(ctx *Context) { + ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory)) +} + var moduleTypes []moduleType var moduleTypesForDocs = map[string]reflect.Value{} type singleton struct { + // True if this should be registered as a pre-singleton, false otherwise. + pre bool + name string factory SingletonFactory } -var singletons []singleton -var preSingletons []singleton +func newSingleton(name string, factory SingletonFactory) singleton { + return singleton{false, name, factory} +} + +func newPreSingleton(name string, factory SingletonFactory) singleton { + return singleton{true, name, factory} +} + +func (s singleton) componentName() string { + return s.name +} + +func (s singleton) register(ctx *Context) { + adaptor := SingletonFactoryAdaptor(ctx, s.factory) + if s.pre { + ctx.RegisterPreSingletonType(s.name, adaptor) + } else { + ctx.RegisterSingletonType(s.name, adaptor) + } +} + +var _ sortableComponent = singleton{} + +var singletons sortableComponents +var preSingletons sortableComponents type mutator struct { name string @@ -44,6 +101,8 @@ type mutator struct { parallel bool } +var _ sortableComponent = &mutator{} + type ModuleFactory func() Module // ModuleFactoryAdaptor wraps a ModuleFactory into a blueprint.ModuleFactory by converting a Module @@ -84,11 +143,11 @@ func RegisterModuleTypeForDocs(name string, factory reflect.Value) { } func RegisterSingletonType(name string, factory SingletonFactory) { - singletons = append(singletons, singleton{name, factory}) + singletons = append(singletons, newSingleton(name, factory)) } func RegisterPreSingletonType(name string, factory SingletonFactory) { - preSingletons = append(preSingletons, singleton{name, factory}) + preSingletons = append(preSingletons, newPreSingleton(name, factory)) } type Context struct { @@ -107,46 +166,51 @@ func NewContext(config Config) *Context { // files to semantically equivalent BUILD files. func (ctx *Context) RegisterForBazelConversion() { for _, t := range moduleTypes { - ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory)) + t.register(ctx) } // Required for SingletonModule types, even though we are not using them. for _, t := range singletons { - ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory)) + t.register(ctx) } - RegisterMutatorsForBazelConversion(ctx.Context, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators) + RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators) } // Register the pipeline of singletons, module types, and mutators for // generating build.ninja and other files for Kati, from Android.bp files. func (ctx *Context) Register() { - for _, t := range preSingletons { - ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory)) - } + preSingletons.registerAll(ctx) for _, t := range moduleTypes { - ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory)) + t.register(ctx) } - for _, t := range singletons { - ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory)) - } + mutators := collateGloballyRegisteredMutators() + mutators.registerAll(ctx) + + singletons := collateGloballyRegisteredSingletons() + singletons.registerAll(ctx) +} - registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps) +func collateGloballyRegisteredSingletons() sortableComponents { + allSingletons := append(sortableComponents(nil), singletons...) + allSingletons = append(allSingletons, + singleton{false, "bazeldeps", BazelSingleton}, - ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton)) + // Register phony just before makevars so it can write out its phony rules as Make rules + singleton{false, "phony", phonySingletonFactory}, - // Register phony just before makevars so it can write out its phony rules as Make rules - ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory)) + // Register makevars after other singletons so they can export values through makevars + singleton{false, "makevars", makeVarsSingletonFunc}, - // Register makevars after other singletons so they can export values through makevars - ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc)) + // Register env and ninjadeps last so that they can track all used environment variables and + // Ninja file dependencies stored in the config. + singleton{false, "env", EnvSingleton}, + singleton{false, "ninjadeps", ninjaDepsSingletonFactory}, + ) - // Register env and ninjadeps last so that they can track all used environment variables and - // Ninja file dependencies stored in the config. - ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton)) - ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory)) + return allSingletons } func ModuleTypeFactories() map[string]ModuleFactory { @@ -168,6 +232,7 @@ func ModuleTypeFactoriesForDocs() map[string]reflect.Value { type RegistrationContext interface { RegisterModuleType(name string, factory ModuleFactory) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) + RegisterPreSingletonType(name string, factory SingletonFactory) RegisterSingletonType(name string, factory SingletonFactory) PreArchMutators(f RegisterMutatorFunc) @@ -208,6 +273,7 @@ var _ RegistrationContext = (*TestContext)(nil) type initRegistrationContext struct { moduleTypes map[string]ModuleFactory singletonTypes map[string]SingletonFactory + preSingletonTypes map[string]SingletonFactory moduleTypesForDocs map[string]reflect.Value } @@ -238,6 +304,14 @@ func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory S RegisterSingletonType(name, factory) } +func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) { + if _, present := ctx.preSingletonTypes[name]; present { + panic(fmt.Sprintf("pre singleton type %q is already registered", name)) + } + ctx.preSingletonTypes[name] = factory + RegisterPreSingletonType(name, factory) +} + func (ctx *initRegistrationContext) PreArchMutators(f RegisterMutatorFunc) { PreArchMutators(f) } diff --git a/android/sandbox.go b/android/sandbox.go index ed022fb87..28e903ad4 100644 --- a/android/sandbox.go +++ b/android/sandbox.go @@ -14,29 +14,8 @@ package android -import ( - "fmt" - "os" -) - -func init() { - // Stash the working directory in a private variable and then change the working directory - // to "/", which will prevent untracked accesses to files by Go Soong plugins. The - // SOONG_SANDBOX_SOONG_BUILD environment variable is set by soong_ui, and is not - // overrideable on the command line. - - orig, err := os.Getwd() - if err != nil { - panic(fmt.Errorf("failed to get working directory: %s", err)) - } - absSrcDir = orig - - if getenv("SOONG_SANDBOX_SOONG_BUILD") == "true" { - err = os.Chdir("/") - if err != nil { - panic(fmt.Errorf("failed to change working directory to '/': %s", err)) - } - } +func InitSandbox(topDir string) { + absSrcDir = topDir } // DO NOT USE THIS FUNCTION IN NEW CODE. diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go index c62e76dd6..34b180d7d 100644 --- a/android/soongconfig/modules.go +++ b/android/soongconfig/modules.go @@ -295,18 +295,25 @@ func createAffectablePropertiesType(affectableProperties []string, factoryProps recurse = func(prefix string, aps []string) ([]string, reflect.Type) { var fields []reflect.StructField + // Iterate while the list is non-empty so it can be modified in the loop. for len(affectableProperties) > 0 { p := affectableProperties[0] if !strings.HasPrefix(affectableProperties[0], prefix) { + // The properties are sorted and recurse is always called with a prefix that matches + // the first property in the list, so if we've reached one that doesn't match the + // prefix we are done with this prefix. break } - affectableProperties = affectableProperties[1:] nestedProperty := strings.TrimPrefix(p, prefix) if i := strings.IndexRune(nestedProperty, '.'); i >= 0 { var nestedType reflect.Type nestedPrefix := nestedProperty[:i+1] + // Recurse to handle the properties with the found prefix. This will return + // an updated affectableProperties with the handled entries removed from the front + // of the list, and the type that contains the handled entries. The type may be + // nil if none of the entries matched factoryProps. affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties) if nestedType != nil { @@ -325,6 +332,8 @@ func createAffectablePropertiesType(affectableProperties []string, factoryProps Type: typ, }) } + // The first element in the list has been handled, remove it from the list. + affectableProperties = affectableProperties[1:] } } diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go index b824c7898..48cdfe776 100644 --- a/android/soongconfig/modules_test.go +++ b/android/soongconfig/modules_test.go @@ -235,6 +235,44 @@ func Test_createAffectablePropertiesType(t *testing.T) { }{}, want: "", }, + { + name: "nested", + affectableProperties: []string{"multilib.lib32.cflags"}, + factoryProps: struct { + Multilib struct { + Lib32 struct { + Cflags string + } + } + }{}, + want: "*struct { Multilib struct { Lib32 struct { Cflags string } } }", + }, + { + name: "complex", + affectableProperties: []string{ + "cflags", + "multilib.lib32.cflags", + "multilib.lib32.ldflags", + "multilib.lib64.cflags", + "multilib.lib64.ldflags", + "zflags", + }, + factoryProps: struct { + Cflags string + Multilib struct { + Lib32 struct { + Cflags string + Ldflags string + } + Lib64 struct { + Cflags string + Ldflags string + } + } + Zflags string + }{}, + want: "*struct { Cflags string; Multilib struct { Lib32 struct { Cflags string; Ldflags string }; Lib64 struct { Cflags string; Ldflags string } }; Zflags string }", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/android/testing.go b/android/testing.go index 7852b604b..03d7bd5a6 100644 --- a/android/testing.go +++ b/android/testing.go @@ -20,6 +20,7 @@ import ( "regexp" "sort" "strings" + "sync" "testing" "github.com/google/blueprint" @@ -48,6 +49,62 @@ func NewTestContext(config Config) *TestContext { return ctx } +var PrepareForTestWithArchMutator = GroupFixturePreparers( + // Configure architecture targets in the fixture config. + FixtureModifyConfig(modifyTestConfigToSupportArchMutator), + + // Add the arch mutator to the context. + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreDepsMutators(registerArchMutator) + }), +) + +var PrepareForTestWithDefaults = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreArchMutators(RegisterDefaultsPreArchMutators) +}) + +var PrepareForTestWithComponentsMutator = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PreArchMutators(RegisterComponentsMutator) +}) + +var PrepareForTestWithPrebuilts = FixtureRegisterWithContext(RegisterPrebuiltMutators) + +var PrepareForTestWithOverrides = FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.PostDepsMutators(RegisterOverridePostDepsMutators) +}) + +// Test fixture preparer that will register most java build components. +// +// Singletons and mutators should only be added here if they are needed for a majority of java +// module types, otherwise they should be added under a separate preparer to allow them to be +// selected only when needed to reduce test execution time. +// +// Module types do not have much of an overhead unless they are used so this should include as many +// module types as possible. The exceptions are those module types that require mutators and/or +// singletons in order to function in which case they should be kept together in a separate +// preparer. +// +// The mutators in this group were chosen because they are needed by the vast majority of tests. +var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers( + // Sorted alphabetically as the actual order does not matter as tests automatically enforce the + // correct order. + PrepareForTestWithArchMutator, + PrepareForTestWithComponentsMutator, + PrepareForTestWithDefaults, + PrepareForTestWithFilegroup, + PrepareForTestWithOverrides, + PrepareForTestWithPackageModule, + PrepareForTestWithPrebuilts, + PrepareForTestWithVisibility, +) + +// Prepares an integration test with all build components from the android package. +// +// This should only be used by tests that want to run with as much of the build enabled as possible. +var PrepareForIntegrationTestWithAndroid = GroupFixturePreparers( + PrepareForTestWithAndroidBuildComponents, +) + func NewTestArchContext(config Config) *TestContext { ctx := NewTestContext(config) ctx.preDeps = append(ctx.preDeps, registerArchMutator) @@ -59,6 +116,13 @@ type TestContext struct { preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc NameResolver *NameResolver + + // The list of pre-singletons and singletons registered for the test. + preSingletons, singletons sortableComponents + + // The order in which the pre-singletons, mutators and singletons will be run in this test + // context; for debugging. + preSingletonOrder, mutatorOrder, singletonOrder []string } func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) { @@ -103,15 +167,244 @@ func (ctx *TestContext) DepsBp2BuildMutators(f RegisterMutatorFunc) { ctx.bp2buildDeps = append(ctx.bp2buildDeps, f) } +// registeredComponentOrder defines the order in which a sortableComponent type is registered at +// runtime and provides support for reordering the components registered for a test in the same +// way. +type registeredComponentOrder struct { + // The name of the component type, used for error messages. + componentType string + + // The names of the registered components in the order in which they were registered. + namesInOrder []string + + // Maps from the component name to its position in the runtime ordering. + namesToIndex map[string]int + + // A function that defines the order between two named components that can be used to sort a slice + // of component names into the same order as they appear in namesInOrder. + less func(string, string) bool +} + +// registeredComponentOrderFromExistingOrder takes an existing slice of sortableComponents and +// creates a registeredComponentOrder that contains a less function that can be used to sort a +// subset of that list of names so it is in the same order as the original sortableComponents. +func registeredComponentOrderFromExistingOrder(componentType string, existingOrder sortableComponents) registeredComponentOrder { + // Only the names from the existing order are needed for this so create a list of component names + // in the correct order. + namesInOrder := componentsToNames(existingOrder) + + // Populate the map from name to position in the list. + nameToIndex := make(map[string]int) + for i, n := range namesInOrder { + nameToIndex[n] = i + } + + // A function to use to map from a name to an index in the original order. + indexOf := func(name string) int { + index, ok := nameToIndex[name] + if !ok { + // Should never happen as tests that use components that are not known at runtime do not sort + // so should never use this function. + panic(fmt.Errorf("internal error: unknown %s %q should be one of %s", componentType, name, strings.Join(namesInOrder, ", "))) + } + return index + } + + // The less function. + less := func(n1, n2 string) bool { + i1 := indexOf(n1) + i2 := indexOf(n2) + return i1 < i2 + } + + return registeredComponentOrder{ + componentType: componentType, + namesInOrder: namesInOrder, + namesToIndex: nameToIndex, + less: less, + } +} + +// componentsToNames maps from the slice of components to a slice of their names. +func componentsToNames(components sortableComponents) []string { + names := make([]string, len(components)) + for i, c := range components { + names[i] = c.componentName() + } + return names +} + +// enforceOrdering enforces the supplied components are in the same order as is defined in this +// object. +// +// If the supplied components contains any components that are not registered at runtime, i.e. test +// specific components, then it is impossible to sort them into an order that both matches the +// runtime and also preserves the implicit ordering defined in the test. In that case it will not +// sort the components, instead it will just check that the components are in the correct order. +// +// Otherwise, this will sort the supplied components in place. +func (o *registeredComponentOrder) enforceOrdering(components sortableComponents) { + // Check to see if the list of components contains any components that are + // not registered at runtime. + var unknownComponents []string + testOrder := componentsToNames(components) + for _, name := range testOrder { + if _, ok := o.namesToIndex[name]; !ok { + unknownComponents = append(unknownComponents, name) + break + } + } + + // If the slice contains some unknown components then it is not possible to + // sort them into an order that matches the runtime while also preserving the + // order expected from the test, so in that case don't sort just check that + // the order of the known mutators does match. + if len(unknownComponents) > 0 { + // Check order. + o.checkTestOrder(testOrder, unknownComponents) + } else { + // Sort the components. + sort.Slice(components, func(i, j int) bool { + n1 := components[i].componentName() + n2 := components[j].componentName() + return o.less(n1, n2) + }) + } +} + +// checkTestOrder checks that the supplied testOrder matches the one defined by this object, +// panicking if it does not. +func (o *registeredComponentOrder) checkTestOrder(testOrder []string, unknownComponents []string) { + lastMatchingTest := -1 + matchCount := 0 + // Take a copy of the runtime order as it is modified during the comparison. + runtimeOrder := append([]string(nil), o.namesInOrder...) + componentType := o.componentType + for i, j := 0, 0; i < len(testOrder) && j < len(runtimeOrder); { + test := testOrder[i] + runtime := runtimeOrder[j] + + if test == runtime { + testOrder[i] = test + fmt.Sprintf(" <-- matched with runtime %s %d", componentType, j) + runtimeOrder[j] = runtime + fmt.Sprintf(" <-- matched with test %s %d", componentType, i) + lastMatchingTest = i + i += 1 + j += 1 + matchCount += 1 + } else if _, ok := o.namesToIndex[test]; !ok { + // The test component is not registered globally so assume it is the correct place, treat it + // as having matched and skip it. + i += 1 + matchCount += 1 + } else { + // Assume that the test list is in the same order as the runtime list but the runtime list + // contains some components that are not present in the tests. So, skip the runtime component + // to try and find the next one that matches the current test component. + j += 1 + } + } + + // If every item in the test order was either test specific or matched one in the runtime then + // it is in the correct order. Otherwise, it was not so fail. + if matchCount != len(testOrder) { + // The test component names were not all matched with a runtime component name so there must + // either be a component present in the test that is not present in the runtime or they must be + // in the wrong order. + testOrder[lastMatchingTest+1] = testOrder[lastMatchingTest+1] + " <--- unmatched" + panic(fmt.Errorf("the tests uses test specific components %q and so cannot be automatically sorted."+ + " Unfortunately it uses %s components in the wrong order.\n"+ + "test order:\n %s\n"+ + "runtime order\n %s\n", + SortedUniqueStrings(unknownComponents), + componentType, + strings.Join(testOrder, "\n "), + strings.Join(runtimeOrder, "\n "))) + } +} + +// registrationSorter encapsulates the information needed to ensure that the test mutators are +// registered, and thereby executed, in the same order as they are at runtime. +// +// It MUST be populated lazily AFTER all package initialization has been done otherwise it will +// only define the order for a subset of all the registered build components that are available for +// the packages being tested. +// +// e.g if this is initialized during say the cc package initialization then any tests run in the +// java package will not sort build components registered by the java package's init() functions. +type registrationSorter struct { + // Used to ensure that this is only created once. + once sync.Once + + // The order of pre-singletons + preSingletonOrder registeredComponentOrder + + // The order of mutators + mutatorOrder registeredComponentOrder + + // The order of singletons + singletonOrder registeredComponentOrder +} + +// populate initializes this structure from globally registered build components. +// +// Only the first call has any effect. +func (s *registrationSorter) populate() { + s.once.Do(func() { + // Create an ordering from the globally registered pre-singletons. + s.preSingletonOrder = registeredComponentOrderFromExistingOrder("pre-singleton", preSingletons) + + // Created an ordering from the globally registered mutators. + globallyRegisteredMutators := collateGloballyRegisteredMutators() + s.mutatorOrder = registeredComponentOrderFromExistingOrder("mutator", globallyRegisteredMutators) + + // Create an ordering from the globally registered singletons. + globallyRegisteredSingletons := collateGloballyRegisteredSingletons() + s.singletonOrder = registeredComponentOrderFromExistingOrder("singleton", globallyRegisteredSingletons) + }) +} + +// Provides support for enforcing the same order in which build components are registered globally +// to the order in which they are registered during tests. +// +// MUST only be accessed via the globallyRegisteredComponentsOrder func. +var globalRegistrationSorter registrationSorter + +// globallyRegisteredComponentsOrder returns the globalRegistrationSorter after ensuring it is +// correctly populated. +func globallyRegisteredComponentsOrder() *registrationSorter { + globalRegistrationSorter.populate() + return &globalRegistrationSorter +} + func (ctx *TestContext) Register() { - registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps) + globalOrder := globallyRegisteredComponentsOrder() + + // Ensure that the pre-singletons used in the test are in the same order as they are used at + // runtime. + globalOrder.preSingletonOrder.enforceOrdering(ctx.preSingletons) + ctx.preSingletons.registerAll(ctx.Context) + mutators := collateRegisteredMutators(ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps) + // Ensure that the mutators used in the test are in the same order as they are used at runtime. + globalOrder.mutatorOrder.enforceOrdering(mutators) + mutators.registerAll(ctx.Context) + + // Register the env singleton with this context before sorting. ctx.RegisterSingletonType("env", EnvSingleton) + + // Ensure that the singletons used in the test are in the same order as they are used at runtime. + globalOrder.singletonOrder.enforceOrdering(ctx.singletons) + ctx.singletons.registerAll(ctx.Context) + + // Save the sorted components order away to make them easy to access while debugging. + ctx.preSingletonOrder = componentsToNames(preSingletons) + ctx.mutatorOrder = componentsToNames(mutators) + ctx.singletonOrder = componentsToNames(singletons) } // RegisterForBazelConversion prepares a test context for bp2build conversion. func (ctx *TestContext) RegisterForBazelConversion() { - RegisterMutatorsForBazelConversion(ctx.Context.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators) + RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators) } func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) { @@ -137,7 +430,11 @@ func (ctx *TestContext) RegisterSingletonModuleType(name string, factory Singlet } func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) { - ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory)) + ctx.singletons = append(ctx.singletons, newSingleton(name, factory)) +} + +func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) { + ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory)) } func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule { @@ -415,12 +712,15 @@ func FailIfErrored(t *testing.T, errs []error) { } } -func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) { +// Fail if no errors that matched the regular expression were found. +// +// Returns true if a matching error was found, false otherwise. +func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) bool { t.Helper() matcher, err := regexp.Compile(pattern) if err != nil { - t.Errorf("failed to compile regular expression %q because %s", pattern, err) + t.Fatalf("failed to compile regular expression %q because %s", pattern, err) } found := false @@ -436,6 +736,8 @@ func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) { t.Errorf("errs[%d] = %q", i, err) } } + + return found } func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPatterns []string) { @@ -456,9 +758,9 @@ func CheckErrorsAgainstExpectations(t *testing.T, errs []error, expectedErrorPat for i, err := range errs { t.Errorf("errs[%d] = %s", i, err) } + t.FailNow() } } - } func SetKatiEnabledForTests(config Config) { @@ -504,10 +806,6 @@ func NormalizePathForTesting(path Path) string { return "<nil path>" } p := path.String() - // Allow absolute paths to /dev/ - if strings.HasPrefix(p, "/dev/") { - return p - } if w, ok := path.(WritablePath); ok { rel, err := filepath.Rel(w.buildDir(), p) if err != nil { diff --git a/android/variable.go b/android/variable.go index 060069257..3a9738d25 100644 --- a/android/variable.go +++ b/android/variable.go @@ -124,6 +124,7 @@ type variableProperties struct { Arc struct { Cflags []string `android:"arch_variant"` Exclude_srcs []string `android:"arch_variant"` + Header_libs []string `android:"arch_variant"` Include_dirs []string `android:"arch_variant"` Shared_libs []string `android:"arch_variant"` Static_libs []string `android:"arch_variant"` diff --git a/android/visibility.go b/android/visibility.go index 7eac47166..5d1be6b47 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -202,6 +202,15 @@ type ExcludeFromVisibilityEnforcementTag interface { ExcludeFromVisibilityEnforcement() } +// The visibility mutators. +var PrepareForTestWithVisibility = FixtureRegisterWithContext(registerVisibilityMutators) + +func registerVisibilityMutators(ctx RegistrationContext) { + ctx.PreArchMutators(RegisterVisibilityRuleChecker) + ctx.PreArchMutators(RegisterVisibilityRuleGatherer) + 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..eb4071eb7 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,55 +1142,44 @@ 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) + result := emptyTestFixtureFactory.Extend( + // General preparers in alphabetical order as test infrastructure will enforce correct + // registration order. + PrepareForTestWithArchMutator, + PrepareForTestWithDefaults, + PrepareForTestWithOverrides, + PrepareForTestWithPackageModule, + PrepareForTestWithPrebuilts, + PrepareForTestWithVisibility, - CheckErrorsAgainstExpectations(t, errs, test.expectedErrors) + // Additional test specific preparers. + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("mock_library", newMockLibraryModule) + ctx.RegisterModuleType("mock_parent", newMockParentFactory) + ctx.RegisterModuleType("mock_defaults", defaultsFactory) + }), + prepareForTestWithFakePrebuiltModules, + // Add additional files to the mock filesystem + test.fs.AddToFixture(), + ). + ExtendWithErrorHandler(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) - } + result.AssertDeepEquals("effective rules mismatch", 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 - } - - _, errs = ctx.PrepareBuildActions(config) - return ctx, errs -} - type mockLibraryProperties struct { Deps []string } diff --git a/android/writedocs.go b/android/writedocs.go index 91c2318f1..6cb2f1024 100644 --- a/android/writedocs.go +++ b/android/writedocs.go @@ -34,7 +34,8 @@ func DocsSingleton() Singleton { type docsSingleton struct{} func primaryBuilderPath(ctx SingletonContext) Path { - primaryBuilder, err := filepath.Rel(ctx.Config().BuildDir(), os.Args[0]) + buildDir := absolutePath(ctx.Config().BuildDir()) + primaryBuilder, err := filepath.Rel(buildDir, os.Args[0]) if err != nil { ctx.Errorf("path to primary builder %q is not in build dir %q", os.Args[0], ctx.Config().BuildDir()) @@ -65,7 +66,9 @@ func (c *docsSingleton) GenerateBuildActions(ctx SingletonContext) { soongDocs := ctx.Rule(pctx, "soongDocs", blueprint.RuleParams{ Command: fmt.Sprintf("rm -f ${outDir}/* && %s --soong_docs %s %s", - primaryBuilder.String(), docsFile.String(), strings.Join(os.Args[1:], " ")), + primaryBuilder.String(), + docsFile.String(), + "\""+strings.Join(os.Args[1:], "\" \"")+"\""), CommandDeps: []string{primaryBuilder.String()}, Description: fmt.Sprintf("%s docs $out", primaryBuilder.Base()), }, diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go index 2d1bbb4a8..b8316a361 100644 --- a/androidmk/androidmk/androidmk.go +++ b/androidmk/androidmk/androidmk.go @@ -48,6 +48,22 @@ var invalidVariableStringToReplacement = map[string]string{ "-": "_dash_", } +// Fix steps that should only run in the androidmk tool, i.e. should only be applied to +// newly-converted Android.bp files. +var fixSteps = bpfix.FixStepsExtension{ + Name: "androidmk", + Steps: []bpfix.FixStep{ + { + Name: "RewriteRuntimeResourceOverlay", + Fix: bpfix.RewriteRuntimeResourceOverlay, + }, + }, +} + +func init() { + bpfix.RegisterFixStepExtension(&fixSteps) +} + func (f *bpFile) insertComment(s string) { f.comments = append(f.comments, &bpparser.CommentGroup{ Comments: []*bpparser.Comment{ diff --git a/apex/OWNERS b/apex/OWNERS index 793f3ed19..fee739b56 100644 --- a/apex/OWNERS +++ b/apex/OWNERS @@ -1,4 +1,4 @@ per-file * = jiyong@google.com per-file allowed_deps.txt = set noparent -per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,omakoto@google.com,jham@google.com +per-file allowed_deps.txt = dariofreni@google.com,hansson@google.com,harpin@google.com,jiyong@google.com,narayan@google.com,jham@google.com diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt index 321a428e8..d9d8f4309 100644 --- a/apex/allowed_deps.txt +++ b/apex/allowed_deps.txt @@ -193,11 +193,14 @@ crtend_android(minSdkVersion:apex_inherit) crtend_so(minSdkVersion:16) crtend_so(minSdkVersion:apex_inherit) datastallprotosnano(minSdkVersion:29) +derive_classpath(minSdkVersion:30) derive_sdk(minSdkVersion:30) derive_sdk(minSdkVersion:current) derive_sdk_prefer32(minSdkVersion:30) derive_sdk_prefer32(minSdkVersion:current) +dnsresolver_aidl_interface-lateststable-ndk_platform(minSdkVersion:29) dnsresolver_aidl_interface-unstable-ndk_platform(minSdkVersion:29) +dnsresolver_aidl_interface-V7-ndk_platform(minSdkVersion:29) dnsresolver_aidl_interface-V8-ndk_platform(minSdkVersion:29) DocumentsUI-res-lib(minSdkVersion:29) exoplayer2-extractor(minSdkVersion:16) @@ -356,6 +359,7 @@ libdmabufheap(minSdkVersion:29) libeigen(minSdkVersion:(no version)) libextservices(minSdkVersion:30) libextservices_jni(minSdkVersion:30) +libfft2d(minSdkVersion:30) libfifo(minSdkVersion:29) libFLAC(minSdkVersion:29) libFLAC-config(minSdkVersion:29) @@ -401,6 +405,7 @@ liblua(minSdkVersion:30) liblz4(minSdkVersion:(no version)) libm(minSdkVersion:(no version)) libmath(minSdkVersion:29) +libmath_headers(minSdkVersion:apex_inherit) libmdnssd(minSdkVersion:(no version)) libmedia_codecserviceregistrant(minSdkVersion:29) libmedia_datasource_headers(minSdkVersion:29) @@ -460,6 +465,7 @@ libquiche_ffi_headers(minSdkVersion:29) libring(minSdkVersion:(no version)) libring-core(minSdkVersion:29) librustc_demangle.rust_sysroot(minSdkVersion:(no version)) +libruy_static(minSdkVersion:30) libsdk_proto(minSdkVersion:30) libsfplugin_ccodec_utils(minSdkVersion:29) libshmemcompat(minSdkVersion:29) @@ -493,6 +499,7 @@ libstagefright_mpeg2extractor(minSdkVersion:29) libstagefright_mpeg2support_nocrypto(minSdkVersion:29) libstats_jni(minSdkVersion:(no version)) libstats_jni(minSdkVersion:30) +libstatslog_media(minSdkVersion:29) libstatslog_resolv(minSdkVersion:29) libstatslog_statsd(minSdkVersion:(no version)) libstatslog_statsd(minSdkVersion:30) @@ -549,7 +556,9 @@ mediatranscoding(minSdkVersion:29) mediatranscoding_aidl_interface-java(minSdkVersion:29) mediatranscoding_aidl_interface-ndk_platform(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) ndk_crtbegin_so.19(minSdkVersion:(no version)) ndk_crtbegin_so.21(minSdkVersion:(no version)) @@ -573,8 +582,10 @@ netd_aidl_interface-V3-java(minSdkVersion:29) netd_aidl_interface-V5-java(minSdkVersion:29) netd_aidl_interface-V6-java(minSdkVersion:29) netd_event_listener_interface-java(minSdkVersion:29) +netd_event_listener_interface-lateststable-ndk_platform(minSdkVersion:29) netd_event_listener_interface-ndk_platform(minSdkVersion:29) netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29) +netd_event_listener_interface-V1-ndk_platform(minSdkVersion:29) netd_event_listener_interface-V2-ndk_platform(minSdkVersion:29) netlink-client(minSdkVersion:29) networkstack-aidl-interfaces-java(minSdkVersion:29) @@ -582,9 +593,13 @@ networkstack-aidl-interfaces-unstable-java(minSdkVersion:29) networkstack-aidl-interfaces-V10-java(minSdkVersion:29) networkstack-aidl-interfaces-V9-java(minSdkVersion:29) networkstack-client(minSdkVersion:29) +NetworkStackApi29Shims(minSdkVersion:29) +NetworkStackApi30Shims(minSdkVersion:29) NetworkStackApiStableDependencies(minSdkVersion:29) NetworkStackApiStableLib(minSdkVersion:29) +NetworkStackApiStableShims(minSdkVersion:29) networkstackprotos(minSdkVersion:29) +NetworkStackShimsCommon(minSdkVersion:29) neuralnetworks_types(minSdkVersion:30) neuralnetworks_utils_hal_1_0(minSdkVersion:30) neuralnetworks_utils_hal_1_1(minSdkVersion:30) diff --git a/apex/apex.go b/apex/apex.go index 74df28ea9..d37bd483e 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -39,23 +39,25 @@ import ( ) func init() { - android.RegisterModuleType("apex", BundleFactory) - android.RegisterModuleType("apex_test", testApexBundleFactory) - android.RegisterModuleType("apex_vndk", vndkApexBundleFactory) - android.RegisterModuleType("apex_defaults", defaultsFactory) - android.RegisterModuleType("prebuilt_apex", PrebuiltFactory) - android.RegisterModuleType("override_apex", overrideApexFactory) - android.RegisterModuleType("apex_set", apexSetFactory) + registerApexBuildComponents(android.InitRegistrationContext) +} + +func registerApexBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("apex", BundleFactory) + ctx.RegisterModuleType("apex_test", testApexBundleFactory) + ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory) + ctx.RegisterModuleType("apex_defaults", defaultsFactory) + ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory) + ctx.RegisterModuleType("override_apex", overrideApexFactory) + ctx.RegisterModuleType("apex_set", apexSetFactory) - android.PreDepsMutators(RegisterPreDepsMutators) - android.PostDepsMutators(RegisterPostDepsMutators) + ctx.PreDepsMutators(RegisterPreDepsMutators) + ctx.PostDepsMutators(RegisterPostDepsMutators) } 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) { @@ -123,7 +125,7 @@ type apexBundleProperties struct { // Whether this APEX is considered updatable or not. When set to true, this will enforce // additional rules for making sure that the APEX is truly updatable. To be updatable, // min_sdk_version should be set as well. This will also disable the size optimizations like - // symlinking to the system libs. Default is false. + // symlinking to the system libs. Default is true. Updatable *bool // Whether this APEX is installable to one of the partitions like system, vendor, etc. @@ -1234,7 +1236,7 @@ var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil) // Implements android.ApexBudleDepsInfoIntf func (a *apexBundle) Updatable() bool { - return proptools.Bool(a.properties.Updatable) + return proptools.BoolDefault(a.properties.Updatable, true) } // getCertString returns the name of the cert that should be used to sign this APEX. This is @@ -2246,8 +2248,10 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) { if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) { return true } - ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s", - fromName, toName, ctx.GetPathString(true)) + ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+ + "\n\nDependency path:%s\n\n"+ + "Consider adding %q to 'apex_available' property of %q", + fromName, toName, ctx.GetPathString(true), apexName, toName) // Visit this module's dependencies to check and report any issues with their availability. return true }) @@ -2853,14 +2857,6 @@ func makeApexAvailableBaseline() map[string][]string { // // Module separator // - m["com.android.sdkext"] = []string{ - "fmtlib_ndk", - "libbase_ndk", - "libprotobuf-cpp-lite-ndk", - } - // - // Module separator - // m["com.android.os.statsd"] = []string{ "libstatssocket", } diff --git a/apex/apex_test.go b/apex/apex_test.go index 3f5604741..08f54f724 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "os" "path" + "path/filepath" "reflect" "regexp" "sort" @@ -66,14 +67,14 @@ func testApexError(t *testing.T, pattern, bp string, handlers ...testCustomizer) t.Fatalf("missing expected error %q (0 errors are returned)", pattern) } -func testApex(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) { +func testApex(t *testing.T, bp string, handlers ...testCustomizer) *android.TestContext { t.Helper() ctx, config := testApexContext(t, bp, handlers...) _, errs := ctx.ParseBlueprintsFiles(".") android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) - return ctx, config + return ctx } type testCustomizer func(fs map[string][]byte, config android.Config) @@ -143,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, @@ -191,6 +193,7 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr "AppSet.apks": nil, "foo.rs": nil, "libfoo.jar": nil, + "libbar.jar": nil, } cc.GatherRequiredFilesForTest(fs) @@ -226,18 +229,11 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr android.RegisterPackageBuildComponents(ctx) ctx.PreArchMutators(android.RegisterVisibilityRuleChecker) - ctx.RegisterModuleType("apex", BundleFactory) - ctx.RegisterModuleType("apex_test", testApexBundleFactory) - ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory) - ctx.RegisterModuleType("apex_key", ApexKeyFactory) - ctx.RegisterModuleType("apex_defaults", defaultsFactory) - ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory) - ctx.RegisterModuleType("override_apex", overrideApexFactory) - ctx.RegisterModuleType("apex_set", apexSetFactory) + registerApexBuildComponents(ctx) + registerApexKeyBuildComponents(ctx) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) ctx.PreArchMutators(android.RegisterComponentsMutator) - ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) android.RegisterPrebuiltMutators(ctx) @@ -246,6 +242,10 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer) ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer) + // These must come after prebuilts and visibility rules to match runtime. + ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) + + // These must come after override rules to match the runtime. cc.RegisterRequiredBuildComponentsForTest(ctx) rust.RegisterRequiredBuildComponentsForTest(ctx) java.RegisterRequiredBuildComponentsForTest(ctx) @@ -257,12 +257,8 @@ func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*andr ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory) ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory) ctx.RegisterModuleType("bpf", bpf.BpfFactory) - ctx.PreDepsMutators(RegisterPreDepsMutators) - ctx.PostDepsMutators(RegisterPostDepsMutators) - ctx.Register() return ctx, config @@ -354,7 +350,7 @@ func ensureListNotEmpty(t *testing.T, result []string) { // Minimal test func TestBasicApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_defaults { name: "myapex-defaults", manifest: ":myapex.manifest", @@ -375,6 +371,7 @@ func TestBasicApex(t *testing.T) { "myjar", "myjar_dex", ], + updatable: false, } apex { @@ -475,6 +472,7 @@ func TestBasicApex(t *testing.T) { binaries: ["foo"], key: "myapex.key", file_contexts: ":myapex-file_contexts", + updatable: false, } cc_library_shared { @@ -658,7 +656,7 @@ func TestBasicApex(t *testing.T) { } func TestDefaults(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_defaults { name: "myapex-defaults", key: "myapex.key", @@ -668,6 +666,7 @@ func TestDefaults(t *testing.T) { apps: ["AppFoo"], rros: ["rro"], bpfs: ["bpf"], + updatable: false, } prebuilt_etc { @@ -732,10 +731,11 @@ func TestDefaults(t *testing.T) { } func TestApexManifest(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { @@ -753,12 +753,13 @@ func TestApexManifest(t *testing.T) { } func TestBasicZipApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", payload_type: "zip", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -803,11 +804,12 @@ func TestBasicZipApex(t *testing.T) { } func TestApexWithStubs(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib3"], + updatable: false, } apex_key { @@ -897,7 +899,7 @@ func TestApexWithStubs(t *testing.T) { func TestApexWithStubsWithMinSdkVersion(t *testing.T) { t.Parallel() - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -970,8 +972,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") @@ -1004,7 +1006,7 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { // | // <platform> | // libplatform ----------------' - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -1055,11 +1057,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"] @@ -1069,11 +1071,12 @@ func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { } func TestApexWithExplicitStubsDependency(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex2", key: "myapex2.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -1164,11 +1167,12 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { | `------> libbar */ - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -1225,12 +1229,13 @@ func TestApexWithRuntimeLibsDependency(t *testing.T) { } func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) { - ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) { + ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) { bp := ` apex { name: "com.android.runtime", key: "com.android.runtime.key", native_shared_libs: ["libc"], + updatable: false, } apex_key { @@ -1288,12 +1293,13 @@ func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) { } func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) { - ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) { + ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) { bp := ` apex { name: "com.android.runtime", key: "com.android.runtime.key", native_shared_libs: ["libc"], + updatable: false, } apex_key { @@ -1357,28 +1363,29 @@ 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 { t.Run(tc.name, func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, native_shared_libs: ["mylib"], + updatable: false, `+tc.minSdkVersion+` } @@ -1439,11 +1446,12 @@ func TestApexDependsOnLLNDKTransitively(t *testing.T) { } func TestApexWithSystemLibsStubs(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"], + updatable: false, } apex_key { @@ -1530,11 +1538,11 @@ 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, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -1605,9 +1613,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") @@ -1616,7 +1624,7 @@ func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) } func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -1665,11 +1673,12 @@ func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) { } func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], + updatable: false, } apex_key { @@ -1710,11 +1719,12 @@ func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) { } func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], + updatable: false, } apex_key { @@ -1757,7 +1767,7 @@ func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { } func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -1796,7 +1806,7 @@ func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) { } func TestQTargetApexUsesStaticUnwinder(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -1825,41 +1835,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 { @@ -1976,6 +1951,7 @@ func TestJavaStableSdkVersion(t *testing.T) { name: "myapex", java_libs: ["myjar"], key: "myapex.key", + updatable: false, } apex_key { name: "myapex.key", @@ -2157,7 +2133,7 @@ func TestApexMinSdkVersion_ErrorIfDepIsNewer_Java(t *testing.T) { } func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -2171,7 +2147,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", @@ -2205,7 +2181,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") } @@ -2244,7 +2220,7 @@ func TestApexMinSdkVersion_WorksWithActiveCodenames(t *testing.T) { config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("S") config.TestProductVariables.Platform_version_active_codenames = []string{"S", "T"} } - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -2274,11 +2250,11 @@ 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) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -2286,6 +2262,7 @@ func TestFilesInSubDir(t *testing.T) { binaries: ["mybin"], prebuilts: ["myetc"], compile_multilib: "both", + updatable: false, } apex_key { @@ -2340,7 +2317,7 @@ func TestFilesInSubDir(t *testing.T) { } func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -2352,6 +2329,7 @@ func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { }, compile_multilib: "both", native_bridge_supported: true, + updatable: false, } apex_key { @@ -2398,12 +2376,13 @@ func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { } func TestUseVendor(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], use_vendor: true, + updatable: false, } apex_key { @@ -2472,6 +2451,7 @@ func TestUseVendorNotAllowedForSystemApexes(t *testing.T) { name: "myapex", key: "myapex.key", use_vendor: true, + updatable: false, } apex_key { name: "myapex.key", @@ -2490,6 +2470,7 @@ func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) { key: "myapex.key", native_shared_libs: ["mylib"], use_vendor: true, + updatable: false, } apex_key { @@ -2508,12 +2489,13 @@ func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) { } func TestVendorApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["mybin"], vendor: true, + updatable: false, } apex_key { name: "myapex.key", @@ -2545,7 +2527,8 @@ func TestVendorApex(t *testing.T) { var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() - ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`) + installPath := path.Join(buildDir, "../target/product/test_device/vendor/apex") + ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath) apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) @@ -2553,13 +2536,14 @@ func TestVendorApex(t *testing.T) { } func TestVendorApex_use_vndk_as_stable(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["mybin"], vendor: true, use_vndk_as_stable: true, + updatable: false, } apex_key { name: "myapex.key", @@ -2616,11 +2600,12 @@ func TestApex_withPrebuiltFirmware(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", prebuilts: ["myfirmware"], + updatable: false, `+tc.additionalProp+` } apex_key { @@ -2643,12 +2628,13 @@ func TestApex_withPrebuiltFirmware(t *testing.T) { } func TestAndroidMk_UseVendorRequired(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -2677,12 +2663,13 @@ func TestAndroidMk_UseVendorRequired(t *testing.T) { } func TestAndroidMk_VendorApexRequired(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", vendor: true, native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -2708,12 +2695,13 @@ func TestAndroidMk_VendorApexRequired(t *testing.T) { } func TestAndroidMkWritesCommonProperties(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", vintf_fragments: ["fragment.xml"], init_rc: ["init.rc"], + updatable: false, } apex_key { name: "myapex.key", @@ -2737,11 +2725,12 @@ func TestAndroidMkWritesCommonProperties(t *testing.T) { } func TestStaticLinking(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -2781,13 +2770,14 @@ func TestStaticLinking(t *testing.T) { } func TestKeys(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", certificate: ":myapex.certificate", native_shared_libs: ["mylib"], file_contexts: ":myapex-file_contexts", + updatable: false, } cc_library { @@ -2838,10 +2828,11 @@ func TestKeys(t *testing.T) { func TestCertificate(t *testing.T) { t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { name: "myapex.key", @@ -2855,11 +2846,12 @@ func TestCertificate(t *testing.T) { } }) t.Run("override when unspecified", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", + updatable: false, } apex_key { name: "myapex.key", @@ -2877,11 +2869,12 @@ func TestCertificate(t *testing.T) { } }) t.Run("if specified as :module, it respects the prop", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", certificate: ":myapex.certificate", + updatable: false, } apex_key { name: "myapex.key", @@ -2899,12 +2892,13 @@ func TestCertificate(t *testing.T) { } }) t.Run("override when specifiec as <:module>", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", certificate: ":myapex.certificate", + updatable: false, } apex_key { name: "myapex.key", @@ -2922,11 +2916,12 @@ func TestCertificate(t *testing.T) { } }) t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", certificate: "testkey", + updatable: false, } apex_key { name: "myapex.key", @@ -2940,12 +2935,13 @@ func TestCertificate(t *testing.T) { } }) t.Run("override when specified as <name>", func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", certificate: "testkey", + updatable: false, } apex_key { name: "myapex.key", @@ -2965,11 +2961,12 @@ func TestCertificate(t *testing.T) { } func TestMacro(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib2"], + updatable: false, } apex { @@ -3092,11 +3089,12 @@ func TestMacro(t *testing.T) { } func TestHeaderLibsDependency(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -3235,14 +3233,15 @@ func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, var } func TestVndkApexCurrent(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", + updatable: false, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3257,7 +3256,7 @@ func TestVndkApexCurrent(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_library { @@ -3271,11 +3270,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", @@ -3291,14 +3290,15 @@ func TestVndkApexCurrent(t *testing.T) { } func TestVndkApexWithPrebuilt(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", + updatable: false, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3313,7 +3313,7 @@ func TestVndkApexWithPrebuilt(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_prebuilt_library_shared { @@ -3332,15 +3332,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", @@ -3375,12 +3374,13 @@ func vndkLibrariesTxtFiles(vers ...string) (result string) { } func TestVndkApexVersion(t *testing.T) { - ctx, _ := testApex(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", + updatable: false, } apex_key { @@ -3406,7 +3406,7 @@ func TestVndkApexVersion(t *testing.T) { srcs: ["libvndk27_arm64.so"], }, }, - apex_available: [ "myapex_v27" ], + apex_available: [ "com.android.vndk.v27" ], } vndk_prebuilt_shared { @@ -3435,73 +3435,27 @@ 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, ` + ctx := testApex(t, ` apex_vndk { - name: "myapex", + name: "com.android.vndk.current", key: "myapex.key", file_contexts: ":myapex-file_contexts", + updatable: false, } apex_vndk { - name: "myapex_v28", + name: "com.android.vndk.v28", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "28", + updatable: false, } apex_key { name: "myapex.key", @@ -3517,20 +3471,21 @@ 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, ` + 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", + updatable: false, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3547,11 +3502,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", @@ -3561,16 +3517,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", } @@ -3592,12 +3548,13 @@ func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) { } func TestVndkApexWithBinder32(t *testing.T) { - ctx, _ := testApex(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", + updatable: false, } apex_key { @@ -3637,7 +3594,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{ @@ -3653,22 +3610,23 @@ 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/*", }) } func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) { - ctx, _ := testApex(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", + updatable: false, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -3689,19 +3647,20 @@ 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) } func TestDependenciesInApexManifest(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex_nodep", key: "myapex.key", native_shared_libs: ["lib_nodep"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", + updatable: false, } apex { @@ -3710,6 +3669,7 @@ func TestDependenciesInApexManifest(t *testing.T) { native_shared_libs: ["lib_dep"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", + updatable: false, } apex { @@ -3718,6 +3678,7 @@ func TestDependenciesInApexManifest(t *testing.T) { native_shared_libs: ["libfoo"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", + updatable: false, } apex { @@ -3726,6 +3687,7 @@ func TestDependenciesInApexManifest(t *testing.T) { native_shared_libs: ["lib_dep", "libfoo"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", + updatable: false, } apex_key { @@ -3799,12 +3761,13 @@ func TestDependenciesInApexManifest(t *testing.T) { } func TestApexName(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apex_name: "com.android.myapex", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -3843,11 +3806,12 @@ func TestApexName(t *testing.T) { } func TestNonTestApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common"], + updatable: false, } apex_key { @@ -3895,11 +3859,12 @@ func TestNonTestApex(t *testing.T) { } func TestTestApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common_test"], + updatable: false, } apex_key { @@ -3943,10 +3908,11 @@ func TestTestApex(t *testing.T) { } func TestApexWithTarget(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, multilib: { first: { native_shared_libs: ["mylib_common"], @@ -4033,10 +3999,11 @@ func TestApexWithTarget(t *testing.T) { } func TestApexWithArch(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, arch: { arm64: { native_shared_libs: ["mylib.arm64"], @@ -4091,11 +4058,12 @@ func TestApexWithArch(t *testing.T) { } func TestApexWithShBinary(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["myscript"], + updatable: false, } apex_key { @@ -4131,10 +4099,11 @@ func TestApexInVariousPartition(t *testing.T) { } for _, tc := range testcases { t.Run(tc.propName+":"+tc.parition, func(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, `+tc.propName+` } @@ -4163,10 +4132,11 @@ func TestApexInVariousPartition(t *testing.T) { } func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { @@ -4186,6 +4156,7 @@ func TestFileContexts_ShouldBeUnderSystemSepolicyForSystemApexes(t *testing.T) { name: "myapex", key: "myapex.key", file_contexts: "my_own_file_contexts", + updatable: false, } apex_key { @@ -4205,6 +4176,7 @@ func TestFileContexts_ProductSpecificApexes(t *testing.T) { key: "myapex.key", product_specific: true, file_contexts: "product_specific_file_contexts", + updatable: false, } apex_key { @@ -4214,12 +4186,13 @@ func TestFileContexts_ProductSpecificApexes(t *testing.T) { } `) - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", product_specific: true, file_contexts: "product_specific_file_contexts", + updatable: false, } apex_key { @@ -4236,12 +4209,13 @@ func TestFileContexts_ProductSpecificApexes(t *testing.T) { } func TestFileContexts_SetViaFileGroup(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", product_specific: true, file_contexts: ":my-file-contexts", + updatable: false, } apex_key { @@ -4263,7 +4237,7 @@ func TestFileContexts_SetViaFileGroup(t *testing.T) { } func TestApexKeyFromOtherModule(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_key { name: "myapex.key", public_key: ":my.avbpubkey", @@ -4296,7 +4270,7 @@ func TestApexKeyFromOtherModule(t *testing.T) { } func TestPrebuilt(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` prebuilt_apex { name: "myapex", arch: { @@ -4319,7 +4293,7 @@ func TestPrebuilt(t *testing.T) { } func TestPrebuiltFilenameOverride(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` prebuilt_apex { name: "myapex", src: "myapex-arm.apex", @@ -4336,7 +4310,7 @@ func TestPrebuiltFilenameOverride(t *testing.T) { } func TestPrebuiltOverrides(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` prebuilt_apex { name: "myapex.prebuilt", src: "myapex-arm.apex", @@ -4366,14 +4340,15 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { // Make sure the import has been given the correct path to the dex jar. p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) dexJarBuildPath := p.DexJarBuildPath() - if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected { + stem := android.RemoveOptionalPrebuiltPrefix(name) + if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected { t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected) } } - ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext) { + ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) { // Make sure that an apex variant is not created for the source module. - if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) { + if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) { t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual) } } @@ -4390,19 +4365,42 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { name: "libfoo", jars: ["libfoo.jar"], } + + java_sdk_library_import { + name: "libbar", + public: { + jars: ["libbar.jar"], + }, + } ` // 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") }) t.Run("prebuilt with source preferred", func(t *testing.T) { @@ -4418,7 +4416,7 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4429,13 +4427,29 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { java_library { name: "libfoo", } + + java_sdk_library_import { + name: "libbar", + public: { + jars: ["libbar.jar"], + }, + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + } ` // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") - ensureNoSourceVariant(t, ctx) + ensureNoSourceVariant(t, ctx, "libfoo") + + checkDexJarBuildPath(t, ctx, "prebuilt_libbar") + ensureNoSourceVariant(t, ctx, "libbar") }) t.Run("prebuilt preferred with source", func(t *testing.T) { @@ -4450,7 +4464,7 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4462,26 +4476,45 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { java_library { name: "libfoo", } + + java_sdk_library_import { + name: "libbar", + prefer: true, + public: { + jars: ["libbar.jar"], + }, + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + } ` // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") - ensureNoSourceVariant(t, ctx) + ensureNoSourceVariant(t, ctx, "libfoo") + + checkDexJarBuildPath(t, ctx, "prebuilt_libbar") + ensureNoSourceVariant(t, ctx, "libbar") }) } func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { transform := func(config *dexpreopt.GlobalConfig) { - config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"}) + config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo", "myapex:libbar"}) } - checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, bootDexJarPath string) { + checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { + t.Helper() s := ctx.SingletonForTests("dex_bootjars") foundLibfooJar := false + base := stem + ".jar" for _, output := range s.AllOutputs() { - if strings.HasSuffix(output, "/libfoo.jar") { + if filepath.Base(output) == base { foundLibfooJar = true buildRule := s.Output(output) actual := android.NormalizePathForTesting(buildRule.Input) @@ -4496,6 +4529,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { } checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) { + t.Helper() hiddenAPIIndex := ctx.SingletonForTests("hiddenapi_index") indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index") java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule) @@ -4513,7 +4547,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4521,13 +4555,23 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { jars: ["libfoo.jar"], apex_available: ["myapex"], } + + java_sdk_library_import { + name: "libbar", + public: { + jars: ["libbar.jar"], + }, + apex_available: ["myapex"], + } ` ctx := testDexpreoptWithApexes(t, bp, "", transform) - checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` +.intermediates/libbar/android_common_myapex/hiddenapi/index.csv .intermediates/libfoo/android_common_myapex/hiddenapi/index.csv `) }) @@ -4544,7 +4588,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4558,6 +4602,21 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], } + + java_sdk_library_import { + name: "libbar", + public: { + jars: ["libbar.jar"], + }, + apex_available: ["myapex"], + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + apex_available: ["myapex"], + } ` // In this test the source (java_library) libfoo is active since the @@ -4580,7 +4639,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4595,13 +4654,31 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], } + + java_sdk_library_import { + name: "libbar", + prefer: true, + public: { + jars: ["libbar.jar"], + }, + apex_available: ["myapex"], + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + apex_available: ["myapex"], + } ` ctx := testDexpreoptWithApexes(t, bp, "", transform) - checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` +.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv .intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv `) }) @@ -4611,7 +4688,8 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { apex { name: "myapex", key: "myapex.key", - java_libs: ["libfoo"], + java_libs: ["libfoo", "libbar"], + updatable: false, } apex_key { @@ -4630,7 +4708,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4644,13 +4722,30 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], } + + java_sdk_library_import { + name: "libbar", + public: { + jars: ["libbar.jar"], + }, + apex_available: ["myapex"], + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + apex_available: ["myapex"], + } ` ctx := testDexpreoptWithApexes(t, bp, "", transform) - checkBootDexJarPath(t, ctx, ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar") + checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar") + checkBootDexJarPath(t, ctx, "libbar", ".intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar") // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` +.intermediates/libbar/android_common_myapex/hiddenapi/index.csv .intermediates/libfoo/android_common_apex10000/hiddenapi/index.csv `) }) @@ -4680,7 +4775,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { src: "myapex-arm.apex", }, }, - exported_java_libs: ["libfoo"], + exported_java_libs: ["libfoo", "libbar"], } java_import { @@ -4695,23 +4790,42 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], } + + java_sdk_library_import { + name: "libbar", + prefer: true, + public: { + jars: ["libbar.jar"], + }, + apex_available: ["myapex"], + } + + java_sdk_library { + name: "libbar", + srcs: ["foo/bar/MyClass.java"], + unsafe_ignore_missing_latest_api: true, + apex_available: ["myapex"], + } ` ctx := testDexpreoptWithApexes(t, bp, "", transform) - checkBootDexJarPath(t, ctx, ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") + checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` +.intermediates/prebuilt_libbar/android_common_prebuilt_myapex/hiddenapi/index.csv .intermediates/prebuilt_libfoo/android_common_prebuilt_myapex/hiddenapi/index.csv `) }) } func TestApexWithTests(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", + updatable: false, tests: [ "mytest", "mytests", @@ -4819,10 +4933,11 @@ func TestApexWithTests(t *testing.T) { } func TestInstallExtraFlattenedApexes(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { name: "myapex.key", @@ -4888,11 +5003,12 @@ func TestErrorsIfDepsAreNotEnabled(t *testing.T) { } func TestApexWithJavaImport(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["myjavaimport"], + updatable: false, } apex_key { @@ -4916,7 +5032,7 @@ func TestApexWithJavaImport(t *testing.T) { } func TestApexWithApps(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -4924,6 +5040,7 @@ func TestApexWithApps(t *testing.T) { "AppFoo", "AppFooPriv", ], + updatable: false, } apex_key { @@ -4994,7 +5111,7 @@ func TestApexWithApps(t *testing.T) { } func TestApexWithAppImports(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -5002,6 +5119,7 @@ func TestApexWithAppImports(t *testing.T) { "AppFooPrebuilt", "AppFooPrivPrebuilt", ], + updatable: false, } apex_key { @@ -5042,13 +5160,14 @@ func TestApexWithAppImports(t *testing.T) { } func TestApexWithAppImportsPrefer(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFoo", ], + updatable: false, } apex_key { @@ -5083,13 +5202,14 @@ func TestApexWithAppImportsPrefer(t *testing.T) { } func TestApexWithTestHelperApp(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "TesterHelpAppFoo", ], + updatable: false, } apex_key { @@ -5120,6 +5240,7 @@ func TestApexPropertiesShouldBeDefaultable(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5132,6 +5253,7 @@ func TestApexPropertiesShouldBeDefaultable(t *testing.T) { name: "otherapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } cc_defaults { @@ -5154,6 +5276,7 @@ func TestApexAvailable_DirectDep(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5166,6 +5289,7 @@ func TestApexAvailable_DirectDep(t *testing.T) { name: "otherapex", key: "otherapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5184,7 +5308,7 @@ func TestApexAvailable_DirectDep(t *testing.T) { func TestApexAvailable_IndirectDep(t *testing.T) { // libbbaz is an indirect dep - testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'. Dependency path: + testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path: .*via tag apex\.dependencyTag.*name:sharedLib.* .*-> libfoo.*link:shared.* .*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.* @@ -5195,6 +5319,7 @@ func TestApexAvailable_IndirectDep(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5232,6 +5357,7 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5252,6 +5378,7 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo", "libbar"], + updatable: false, } apex_key { @@ -5286,11 +5413,12 @@ func TestApexAvailable_InvalidApexName(t *testing.T) { } func TestApexAvailable_CheckForPlatform(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libbar", "libbaz"], + updatable: false, } apex_key { @@ -5348,11 +5476,12 @@ func TestApexAvailable_CheckForPlatform(t *testing.T) { } func TestApexAvailable_CreatedForApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], + updatable: false, } apex_key { @@ -5382,12 +5511,13 @@ func TestApexAvailable_CreatedForApex(t *testing.T) { } func TestOverrideApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["app"], overrides: ["oldapex"], + updatable: false, } override_apex { @@ -5465,7 +5595,7 @@ func TestOverrideApex(t *testing.T) { } func TestLegacyAndroid10Support(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", @@ -5525,11 +5655,12 @@ var filesForSdkLibrary = map[string][]byte{ } func TestJavaSDKLibrary(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["foo"], + updatable: false, } apex_key { @@ -5562,11 +5693,12 @@ func TestJavaSDKLibrary(t *testing.T) { } func TestJavaSDKLibrary_WithinApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["foo", "bar"], + updatable: false, } apex_key { @@ -5614,11 +5746,12 @@ func TestJavaSDKLibrary_WithinApex(t *testing.T) { } func TestJavaSDKLibrary_CrossBoundary(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["foo"], + updatable: false, } apex_key { @@ -5664,7 +5797,7 @@ func TestJavaSDKLibrary_CrossBoundary(t *testing.T) { } func TestJavaSDKLibrary_ImportPreferred(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` prebuilt_apis { name: "sdk", api_dirs: ["100"], @@ -5681,6 +5814,7 @@ func TestJavaSDKLibrary_ImportPreferred(t *testing.T) { name: "myapex", key: "myapex.key", java_libs: ["foo", "bar"], + updatable: false, } apex_key { @@ -5758,6 +5892,7 @@ func TestJavaSDKLibrary_ImportOnly(t *testing.T) { name: "myapex", key: "myapex.key", java_libs: ["foo"], + updatable: false, } apex_key { @@ -5779,12 +5914,13 @@ func TestJavaSDKLibrary_ImportOnly(t *testing.T) { } func TestCompatConfig(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", prebuilts: ["myjar-platform-compat-config"], java_libs: ["myjar"], + updatable: false, } apex_key { @@ -5818,6 +5954,7 @@ func TestRejectNonInstallableJavaLibrary(t *testing.T) { name: "myapex", key: "myapex.key", java_libs: ["myjar"], + updatable: false, } apex_key { @@ -5838,11 +5975,12 @@ func TestRejectNonInstallableJavaLibrary(t *testing.T) { } func TestCarryRequiredModuleNames(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -5882,6 +6020,7 @@ func TestSymlinksFromApexToSystem(t *testing.T) { key: "myapex.key", native_shared_libs: ["mylib"], java_libs: ["myjar"], + updatable: false, } apex { @@ -5980,7 +6119,7 @@ func TestSymlinksFromApexToSystem(t *testing.T) { // For unbundled build, symlink shouldn't exist regardless of whether an APEX // is updatable or not - ctx, _ := testApex(t, bp, withUnbundledBuild) + ctx := testApex(t, bp, withUnbundledBuild) files := getFiles(t, ctx, "myapex", "android_common_myapex_image") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") @@ -5992,7 +6131,7 @@ func TestSymlinksFromApexToSystem(t *testing.T) { ensureRealfileExists(t, files, "lib64/myotherlib.so") // For bundled build, symlink to the system for the non-updatable APEXes only - ctx, _ = testApex(t, bp) + ctx = testApex(t, bp) files = getFiles(t, ctx, "myapex", "android_common_myapex_image") ensureRealfileExists(t, files, "javalib/myjar.jar") ensureRealfileExists(t, files, "lib64/mylib.so") @@ -6005,11 +6144,12 @@ func TestSymlinksFromApexToSystem(t *testing.T) { } func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -6056,11 +6196,12 @@ func TestSymlinksFromApexToSystemRequiredModuleNames(t *testing.T) { } func TestApexWithJniLibs(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", jni_libs: ["mylib"], + updatable: false, } apex_key { @@ -6097,10 +6238,11 @@ func TestApexWithJniLibs(t *testing.T) { } func TestApexMutatorsDontRunIfDisabled(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { name: "myapex.key", @@ -6118,11 +6260,12 @@ func TestApexMutatorsDontRunIfDisabled(t *testing.T) { } func TestAppBundle(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], + updatable: false, } apex_key { @@ -6148,11 +6291,12 @@ func TestAppBundle(t *testing.T) { } func TestAppSetBundle(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["AppSet"], + updatable: false, } apex_key { @@ -6180,7 +6324,7 @@ func TestAppSetBundle(t *testing.T) { } func TestAppSetBundlePrebuilt(t *testing.T) { - ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) { + ctx := testApex(t, "", func(fs map[string][]byte, config android.Config) { bp := ` apex_set { name: "myapex", @@ -6259,6 +6403,7 @@ func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpre name: "some-non-updatable-apex", key: "some-non-updatable-apex.key", java_libs: ["some-non-updatable-apex-lib"], + updatable: false, } apex_key { @@ -6319,6 +6464,9 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt } cc.GatherRequiredFilesForTest(fs) + for k, v := range filesForSdkLibrary { + fs[k] = v + } config := android.TestArchConfig(buildDir, nil, bp, fs) ctx := android.NewTestArchContext(config) @@ -6327,6 +6475,7 @@ func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreopt ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + ctx.PreArchMutators(android.RegisterComponentsMutator) android.RegisterPrebuiltMutators(ctx) cc.RegisterRequiredBuildComponentsForTest(ctx) java.RegisterRequiredBuildComponentsForTest(ctx) @@ -6378,6 +6527,21 @@ func TestUpdatable_should_set_min_sdk_version(t *testing.T) { `) } +func TestUpdatableDefault_should_set_min_sdk_version(t *testing.T) { + testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, ` + apex { + name: "myapex", + key: "myapex.key", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) +} + func TestNoUpdatableJarsInBootImage(t *testing.T) { var err string var transform func(*dexpreopt.GlobalConfig) @@ -6592,6 +6756,7 @@ func TestApexPermittedPackagesRules(t *testing.T) { name: "myapex", key: "myapex.key", java_libs: ["bcp_lib1", "nonbcp_lib2"], + updatable: false, }`, bootJars: []string{"bcp_lib1"}, modulesPackages: map[string][]string{ @@ -6624,6 +6789,7 @@ func TestApexPermittedPackagesRules(t *testing.T) { name: "myapex", key: "myapex.key", java_libs: ["bcp_lib1", "bcp_lib2"], + updatable: false, } `, bootJars: []string{"bcp_lib1", "bcp_lib2"}, @@ -6643,11 +6809,12 @@ func TestApexPermittedPackagesRules(t *testing.T) { } func TestTestFor(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "myprivlib"], + updatable: false, } apex_key { @@ -6728,7 +6895,7 @@ func intPtr(i int) *int { } func TestApexSet(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_set { name: "myapex", set: "myapex.apks", @@ -6772,6 +6939,7 @@ func TestNoStaticLinkingToStubsLib(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -6803,10 +6971,11 @@ func TestNoStaticLinkingToStubsLib(t *testing.T) { } func TestApexKeysTxt(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", + updatable: false, } apex_key { @@ -6843,12 +7012,13 @@ func TestApexKeysTxt(t *testing.T) { } func TestAllowedFiles(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["app"], allowed_files: "allowed.txt", + updatable: false, } apex_key { @@ -6898,11 +7068,12 @@ func TestAllowedFiles(t *testing.T) { } func TestNonPreferredPrebuiltDependency(t *testing.T) { - _, _ = testApex(t, ` + testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -6933,11 +7104,12 @@ func TestNonPreferredPrebuiltDependency(t *testing.T) { } func TestCompressedApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", compressible: true, + updatable: false, } apex_key { name: "myapex.key", @@ -6967,11 +7139,12 @@ func TestCompressedApex(t *testing.T) { } func TestPreferredPrebuiltSharedLibDep(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -7018,11 +7191,12 @@ func TestPreferredPrebuiltSharedLibDep(t *testing.T) { } func TestExcludeDependency(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { @@ -7072,6 +7246,7 @@ func TestPrebuiltStubLibDep(t *testing.T) { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], + updatable: false, } apex_key { name: "myapex.key", @@ -7090,6 +7265,7 @@ func TestPrebuiltStubLibDep(t *testing.T) { enabled: %s, key: "myapex.key", native_shared_libs: ["stublib"], + updatable: false, } ` @@ -7159,7 +7335,7 @@ func TestPrebuiltStubLibDep(t *testing.T) { t.Run(test.name, func(t *testing.T) { for _, otherApexEnabled := range test.otherApexEnabled { t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) { - ctx, _ := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp) + ctx := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp) type modAndMkEntries struct { mod *cc.Module diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go index 27a15624d..2e6ed824e 100644 --- a/apex/boot_image_test.go +++ b/apex/boot_image_test.go @@ -28,7 +28,7 @@ import ( // modules from the ART apex. func TestBootImages(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` java_sdk_library { name: "foo", srcs: ["b.java"], @@ -48,6 +48,7 @@ func TestBootImages(t *testing.T) { "baz", "quuz", ], + updatable: false, } apex_key { @@ -180,13 +181,14 @@ func withFrameworkBootImageJars(bootJars ...string) func(fs map[string][]byte, c } func TestBootImageInApex(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", boot_images: [ "mybootimage", ], + updatable: false, } apex_key { 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/key.go b/apex/key.go index 752888da2..8b33b593f 100644 --- a/apex/key.go +++ b/apex/key.go @@ -27,8 +27,12 @@ import ( var String = proptools.String func init() { - android.RegisterModuleType("apex_key", ApexKeyFactory) - android.RegisterSingletonType("apex_keys_text", apexKeysTextFactory) + registerApexKeyBuildComponents(android.InitRegistrationContext) +} + +func registerApexKeyBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("apex_key", ApexKeyFactory) + ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory) } type apexKey struct { diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 6292da648..460e55930 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -108,8 +108,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 { @@ -127,15 +129,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: @@ -145,14 +152,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 { @@ -216,7 +223,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) { @@ -249,16 +256,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 @@ -531,7 +528,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..34b940896 100644 --- a/apex/vndk_test.go +++ b/apex/vndk_test.go @@ -9,14 +9,15 @@ import ( ) func TestVndkApexForVndkLite(t *testing.T) { - ctx, _ := testApex(t, ` + ctx := testApex(t, ` apex_vndk { - name: "myapex", - key: "myapex.key", + name: "com.android.vndk.current", + key: "com.android.vndk.current.key", + updatable: false, } apex_key { - name: "myapex.key", + name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } @@ -31,7 +32,7 @@ func TestVndkApexForVndkLite(t *testing.T) { }, system_shared_libs: [], stl: "none", - apex_available: [ "myapex" ], + apex_available: [ "com.android.vndk.current" ], } cc_library { @@ -45,13 +46,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,8 +68,9 @@ func TestVndkApexForVndkLite(t *testing.T) { func TestVndkApexUsesVendorVariant(t *testing.T) { bp := ` apex_vndk { - name: "myapex", + name: "com.android.vndk.current", key: "mykey", + updatable: false, } apex_key { name: "mykey", @@ -94,11 +96,11 @@ 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) { - ctx, _ := testApex(t, bp) + ctx := testApex(t, bp) // libfoo doesn't have apex variants for _, variant := range ctx.ModuleVariantsForTests("libfoo") { @@ -106,30 +108,30 @@ 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") }) t.Run("VNDK APEX gathers only vendor variants even if product variants are available", func(t *testing.T) { - ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) { + ctx := testApex(t, bp, func(fs map[string][]byte, config android.Config) { // Now product variant is available 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") }) t.Run("VNDK APEX supports coverage variants", func(t *testing.T) { - ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) { + ctx := testApex(t, bp, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true) 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 { diff --git a/bazel/properties.go b/bazel/properties.go index 8055306b2..a5ffa55cb 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -14,10 +14,7 @@ package bazel -import ( - "fmt" - "strings" -) +import "fmt" type bazelModuleProperties struct { // The label of the Bazel target replacing this Soong module. @@ -37,32 +34,15 @@ type Properties struct { // BazelTargetModuleProperties contain properties and metadata used for // Blueprint to BUILD file conversion. type BazelTargetModuleProperties struct { - Name *string - // The Bazel rule class for this target. - Rule_class string + Rule_class string `blueprint:"mutated"` // The target label for the bzl file containing the definition of the rule class. - Bzl_load_location string + Bzl_load_location string `blueprint:"mutated"` } const BazelTargetModuleNamePrefix = "__bp2build__" -func NewBazelTargetModuleProperties(name string, ruleClass string, bzlLoadLocation string) BazelTargetModuleProperties { - if strings.HasPrefix(name, BazelTargetModuleNamePrefix) { - panic(fmt.Errorf( - "The %s name prefix is added automatically, do not set it manually: %s", - BazelTargetModuleNamePrefix, - name)) - } - name = BazelTargetModuleNamePrefix + name - return BazelTargetModuleProperties{ - Name: &name, - Rule_class: ruleClass, - Bzl_load_location: bzlLoadLocation, - } -} - // Label is used to represent a Bazel compatible Label. Also stores the original bp text to support // string replacement. type Label struct { @@ -85,3 +65,72 @@ func (ll *LabelList) Append(other LabelList) { ll.Excludes = append(other.Excludes, other.Excludes...) } } + +// StringListAttribute corresponds to the string_list Bazel attribute type with +// support for additional metadata, like configurations. +type StringListAttribute struct { + // The base value of the string list attribute. + Value []string + + // Optional additive set of list values to the base value. + ArchValues stringListArchValues +} + +// Arch-specific string_list typed Bazel attribute values. This should correspond +// to the types of architectures supported for compilation in arch.go. +type stringListArchValues struct { + X86 []string + X86_64 []string + Arm []string + Arm64 []string + Default []string + // TODO(b/181299724): this is currently missing the "common" arch, which + // doesn't have an equivalent platform() definition yet. +} + +// HasArchSpecificValues returns true if the attribute contains +// architecture-specific string_list values. +func (attrs *StringListAttribute) HasArchSpecificValues() bool { + for _, arch := range []string{"x86", "x86_64", "arm", "arm64", "default"} { + if len(attrs.GetValueForArch(arch)) > 0 { + return true + } + } + return false +} + +// GetValueForArch returns the string_list attribute value for an architecture. +func (attrs *StringListAttribute) GetValueForArch(arch string) []string { + switch arch { + case "x86": + return attrs.ArchValues.X86 + case "x86_64": + return attrs.ArchValues.X86_64 + case "arm": + return attrs.ArchValues.Arm + case "arm64": + return attrs.ArchValues.Arm64 + case "default": + return attrs.ArchValues.Default + default: + panic(fmt.Errorf("Unknown arch: %s", arch)) + } +} + +// SetValueForArch sets the string_list attribute value for an architecture. +func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) { + switch arch { + case "x86": + attrs.ArchValues.X86 = value + case "x86_64": + attrs.ArchValues.X86_64 = value + case "arm": + attrs.ArchValues.Arm = value + case "arm64": + attrs.ArchValues.Arm64 = value + case "default": + attrs.ArchValues.Default = value + default: + panic(fmt.Errorf("Unknown arch: %s", arch)) + } +} diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go index 0bff8aac7..21bf4acf1 100644 --- a/bloaty/bloaty.go +++ b/bloaty/bloaty.go @@ -23,6 +23,7 @@ import ( ) const bloatyDescriptorExt = "bloaty.csv" +const protoFilename = "binary_sizes.pb" var ( fileSizeMeasurerKey blueprint.ProviderKey @@ -87,6 +88,10 @@ func (singleton *sizesSingleton) GenerateBuildActions(ctx android.SingletonConte ctx.Build(pctx, android.BuildParams{ Rule: bloatyMerger, Inputs: android.SortedUniquePaths(deps), - Output: android.PathForOutput(ctx, "binary_sizes.pb"), + Output: android.PathForOutput(ctx, protoFilename), }) } + +func (singleton *sizesSingleton) MakeVars(ctx android.MakeVarsContext) { + ctx.DistForGoalWithFilename("checkbuild", android.PathForOutput(ctx, protoFilename), protoFilename) +} diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 521bb0601..8deb5a217 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -10,7 +10,9 @@ bootstrap_go_package { "bp2build.go", "build_conversion.go", "bzl_conversion.go", + "configurability.go", "conversion.go", + "metrics.go", ], deps: [ "soong-android", diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go index b89d0a07f..7169d7e97 100644 --- a/bp2build/bp2build.go +++ b/bp2build/bp2build.go @@ -22,13 +22,13 @@ import ( // The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to // Android.bp files that are capable of being built with Bazel. -func Codegen(ctx CodegenContext) { +func Codegen(ctx CodegenContext) CodegenMetrics { outputDir := android.PathForOutput(ctx, "bp2build") android.RemoveAllOutputDir(outputDir) ruleShims := CreateRuleShims(android.ModuleTypeFactories()) - buildToTargets := GenerateBazelTargets(ctx.Context(), ctx.mode) + buildToTargets, metrics := GenerateBazelTargets(ctx) filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode) for _, f := range filesToWrite { @@ -36,6 +36,8 @@ func Codegen(ctx CodegenContext) { fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err) } } + + return metrics } func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error { diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index 7ffcfa4a3..7fa499683 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -19,7 +19,6 @@ import ( "android/soong/bazel" "fmt" "reflect" - "strconv" "strings" "github.com/google/blueprint" @@ -105,6 +104,10 @@ type CodegenContext struct { mode CodegenMode } +func (c *CodegenContext) Mode() CodegenMode { + return c.mode +} + // CodegenMode is an enum to differentiate code-generation modes. type CodegenMode int @@ -160,65 +163,58 @@ func propsToAttributes(props map[string]string) string { return attributes } -func GenerateBazelTargets(ctx bpToBuildContext, codegenMode CodegenMode) map[string]BazelTargets { +func GenerateBazelTargets(ctx CodegenContext) (map[string]BazelTargets, CodegenMetrics) { buildFileToTargets := make(map[string]BazelTargets) - ctx.VisitAllModules(func(m blueprint.Module) { - dir := ctx.ModuleDir(m) + + // Simple metrics tracking for bp2build + totalModuleCount := 0 + ruleClassCount := make(map[string]int) + + bpCtx := ctx.Context() + bpCtx.VisitAllModules(func(m blueprint.Module) { + dir := bpCtx.ModuleDir(m) var t BazelTarget - switch codegenMode { + switch ctx.Mode() { case Bp2Build: - if _, ok := m.(android.BazelTargetModule); !ok { + if b, ok := m.(android.BazelTargetModule); !ok { + // Only include regular Soong modules (non-BazelTargetModules) into the total count. + totalModuleCount += 1 return + } else { + t = generateBazelTarget(bpCtx, m, b) + ruleClassCount[t.ruleClass] += 1 } - t = generateBazelTarget(ctx, m) case QueryView: // Blocklist certain module types from being generated. - if canonicalizeModuleType(ctx.ModuleType(m)) == "package" { + if canonicalizeModuleType(bpCtx.ModuleType(m)) == "package" { // package module name contain slashes, and thus cannot // be mapped cleanly to a bazel label. return } - t = generateSoongModuleTarget(ctx, m) + t = generateSoongModuleTarget(bpCtx, m) default: - panic(fmt.Errorf("Unknown code-generation mode: %s", codegenMode)) + panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode())) } buildFileToTargets[dir] = append(buildFileToTargets[dir], t) }) - return buildFileToTargets -} -// Helper method to trim quotes around strings. -func trimQuotes(s string) string { - if s == "" { - // strconv.Unquote would error out on empty strings, but this method - // allows them, so return the empty string directly. - return "" + metrics := CodegenMetrics{ + TotalModuleCount: totalModuleCount, + RuleClassCount: ruleClassCount, } - ret, err := strconv.Unquote(s) - if err != nil { - // Panic the error immediately. - panic(fmt.Errorf("Trying to unquote '%s', but got error: %s", s, err)) - } - return ret + + return buildFileToTargets, metrics } -func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget { +func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, b android.BazelTargetModule) BazelTarget { + ruleClass := b.RuleClass() + bzlLoadLocation := b.BzlLoadLocation() + // extract the bazel attributes from the module. props := getBuildProperties(ctx, m) - // extract the rule class name from the attributes. Since the string value - // will be string-quoted, remove the quotes here. - ruleClass := trimQuotes(props.Attrs["rule_class"]) - // Delete it from being generated in the BUILD file. - delete(props.Attrs, "rule_class") - - // extract the bzl_load_location, and also remove the quotes around it here. - bzlLoadLocation := trimQuotes(props.Attrs["bzl_load_location"]) - // Delete it from being generated in the BUILD file. - delete(props.Attrs, "bzl_load_location") - delete(props.Attrs, "bp2build_available") // Return the Bazel target with rule class and attributes, ready to be @@ -358,11 +354,42 @@ func prettyPrint(propertyValue reflect.Value, indent int) (string, error) { ret += makeIndent(indent) ret += "]" case reflect.Struct: + // Special cases where the bp2build sends additional information to the codegenerator + // by wrapping the attributes in a custom struct type. if labels, ok := propertyValue.Interface().(bazel.LabelList); ok { // TODO(b/165114590): convert glob syntax return prettyPrint(reflect.ValueOf(labels.Includes), indent) } else if label, ok := propertyValue.Interface().(bazel.Label); ok { return fmt.Sprintf("%q", label.Label), nil + } else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok { + // A Bazel string_list attribute that may contain a select statement. + ret, err := prettyPrint(reflect.ValueOf(stringList.Value), indent) + if err != nil { + return ret, err + } + + if !stringList.HasArchSpecificValues() { + // Select statement not needed. + return ret, nil + } + + ret += " + " + "select({\n" + for _, arch := range android.ArchTypeList() { + value := stringList.GetValueForArch(arch.Name) + if len(value) > 0 { + ret += makeIndent(indent + 1) + list, _ := prettyPrint(reflect.ValueOf(value), indent+1) + ret += fmt.Sprintf("\"%s\": %s,\n", platformArchMap[arch], list) + } + } + + ret += makeIndent(indent + 1) + list, _ := prettyPrint(reflect.ValueOf(stringList.GetValueForArch("default")), indent+1) + ret += fmt.Sprintf("\"%s\": %s,\n", "//conditions:default", list) + + ret += makeIndent(indent) + ret += "})" + return ret, err } ret = "{\n" diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go index 422422b1f..aa4fc1d97 100644 --- a/bp2build/build_conversion_test.go +++ b/bp2build/build_conversion_test.go @@ -194,6 +194,7 @@ func TestGenerateSoongModuleTargets(t *testing.T) { for _, testCase := range testCases { config := android.TestConfig(buildDir, nil, testCase.bp, nil) ctx := android.NewTestContext(config) + ctx.RegisterModuleType("custom", customModuleFactory) ctx.Register() @@ -202,7 +203,8 @@ func TestGenerateSoongModuleTargets(t *testing.T) { _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) - bazelTargets := GenerateBazelTargets(ctx.Context.Context, QueryView)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount { t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount) } @@ -245,6 +247,7 @@ func TestGenerateBazelTargetModules(t *testing.T) { for _, testCase := range testCases { config := android.TestConfig(buildDir, nil, testCase.bp, nil) ctx := android.NewTestContext(config) + ctx.RegisterModuleType("custom", customModuleFactory) ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator) ctx.RegisterForBazelConversion() @@ -258,7 +261,9 @@ func TestGenerateBazelTargetModules(t *testing.T) { continue } - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) + if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount { t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount) } else { @@ -415,7 +420,8 @@ load("//build/bazel/rules:rules.bzl", "my_library")`, _, errs = ctx.ResolveDependencies(config) android.FailIfErrored(t, errs) - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount { t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount) } @@ -904,7 +910,9 @@ genrule { if testCase.dir != "" { checkDir = testCase.dir } - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir] + + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) } else { @@ -1118,7 +1126,8 @@ genrule { _, errs = ctx.ResolveDependencies(config) android.FailIfErrored(t, errs) - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) if actualCount := len(bazelTargets); actualCount != 1 { t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount) } @@ -1205,7 +1214,8 @@ func TestAllowlistingBp2buildTargets(t *testing.T) { _, errs = ctx.ResolveDependencies(config) android.FailIfErrored(t, errs) - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) if actualCount := len(bazelTargets); actualCount != testCase.expectedCount { t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount) } diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go index 3cd37628f..5bf5c802f 100644 --- a/bp2build/cc_library_headers_conversion_test.go +++ b/bp2build/cc_library_headers_conversion_test.go @@ -202,7 +202,8 @@ cc_library_headers { if testCase.dir != "" { checkDir = testCase.dir } - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) } else { diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go index e4ffe1680..1d4e32221 100644 --- a/bp2build/cc_object_conversion_test.go +++ b/bp2build/cc_object_conversion_test.go @@ -38,9 +38,10 @@ func TestCcObjectBp2Build(t *testing.T) { moduleTypeUnderTestFactory: cc.ObjectFactory, moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, filesystem: map[string]string{ - "a/b/foo.h": "", - "a/b/bar.h": "", - "a/b/c.c": "", + "a/b/foo.h": "", + "a/b/bar.h": "", + "a/b/exclude.c": "", + "a/b/c.c": "", }, blueprint: `cc_object { name: "foo", @@ -52,8 +53,9 @@ func TestCcObjectBp2Build(t *testing.T) { ], srcs: [ "a/b/*.h", - "a/b/c.c" + "a/b/*.c" ], + exclude_srcs: ["a/b/exclude.c"], bazel_module: { bp2build_available: true }, } @@ -97,15 +99,6 @@ func TestCcObjectBp2Build(t *testing.T) { cc_defaults { name: "foo_defaults", defaults: ["foo_bar_defaults"], - // TODO(b/178130668): handle configurable attributes that depend on the platform - arch: { - x86: { - cflags: ["-fPIC"], - }, - x86_64: { - cflags: ["-fPIC"], - }, - }, } cc_defaults { @@ -134,6 +127,52 @@ cc_defaults { )`, }, }, + { + description: "cc_object with cc_object deps in objs props", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + filesystem: map[string]string{ + "a/b/c.c": "", + "x/y/z.c": "", + }, + blueprint: `cc_object { + name: "foo", + srcs: ["a/b/c.c"], + objs: ["bar"], + + bazel_module: { bp2build_available: true }, +} + +cc_object { + name: "bar", + srcs: ["x/y/z.c"], + + bazel_module: { bp2build_available: true }, +} +`, + expectedBazelTargets: []string{`cc_object( + name = "bar", + copts = [ + "-fno-addrsig", + ], + srcs = [ + "x/y/z.c", + ], +)`, `cc_object( + name = "foo", + copts = [ + "-fno-addrsig", + ], + deps = [ + ":bar", + ], + srcs = [ + "a/b/c.c", + ], +)`, + }, + }, } dir := "." @@ -166,7 +205,142 @@ cc_defaults { continue } - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[dir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) + if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { + fmt.Println(bazelTargets) + t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) + } else { + for i, target := range bazelTargets { + if w, g := testCase.expectedBazelTargets[i], target.content; w != g { + t.Errorf( + "%s: Expected generated Bazel target to be '%s', got '%s'", + testCase.description, + w, + g, + ) + } + } + } + } +} + +func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { + testCases := []struct { + description string + moduleTypeUnderTest string + moduleTypeUnderTestFactory android.ModuleFactory + moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) + blueprint string + expectedBazelTargets []string + filesystem map[string]string + }{ + { + description: "cc_object setting cflags for one arch", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { + name: "foo", + arch: { + x86: { + cflags: ["-fPIC"], + }, + }, + bazel_module: { bp2build_available: true }, +} +`, + expectedBazelTargets: []string{ + `cc_object( + name = "foo", + copts = [ + "-fno-addrsig", + ] + select({ + "@bazel_tools//platforms:x86_32": [ + "-fPIC", + ], + "//conditions:default": [ + ], + }), +)`, + }, + }, + { + description: "cc_object setting cflags for 4 architectures", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { + name: "foo", + arch: { + x86: { + cflags: ["-fPIC"], + }, + x86_64: { + cflags: ["-fPIC"], + }, + arm: { + cflags: ["-Wall"], + }, + arm64: { + cflags: ["-Wall"], + }, + }, + bazel_module: { bp2build_available: true }, +} +`, + expectedBazelTargets: []string{ + `cc_object( + name = "foo", + copts = [ + "-fno-addrsig", + ] + select({ + "@bazel_tools//platforms:arm": [ + "-Wall", + ], + "@bazel_tools//platforms:aarch64": [ + "-Wall", + ], + "@bazel_tools//platforms:x86_32": [ + "-fPIC", + ], + "@bazel_tools//platforms:x86_64": [ + "-fPIC", + ], + "//conditions:default": [ + ], + }), +)`, + }, + }, + } + + dir := "." + for _, testCase := range testCases { + filesystem := make(map[string][]byte) + toParse := []string{ + "Android.bp", + } + config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) + ctx := android.NewTestContext(config) + // Always register cc_defaults module factory + ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) + + ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) + ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) + ctx.RegisterForBazelConversion() + + _, errs := ctx.ParseFileList(dir, toParse) + if Errored(t, testCase.description, errs) { + continue + } + _, errs = ctx.ResolveDependencies(config) + if Errored(t, testCase.description, errs) { + continue + } + + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { fmt.Println(bazelTargets) t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) diff --git a/bp2build/configurability.go b/bp2build/configurability.go new file mode 100644 index 000000000..47cf3c612 --- /dev/null +++ b/bp2build/configurability.go @@ -0,0 +1,15 @@ +package bp2build + +import "android/soong/android" + +// Configurability support for bp2build. + +var ( + // A map of architectures to the Bazel label of the constraint_value. + platformArchMap = map[android.ArchType]string{ + android.Arm: "@bazel_tools//platforms:arm", + android.Arm64: "@bazel_tools//platforms:aarch64", + android.X86: "@bazel_tools//platforms:x86_32", + android.X86_64: "@bazel_tools//platforms:x86_64", + } +) diff --git a/bp2build/conversion.go b/bp2build/conversion.go index 081e08243..1225f2bed 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -93,6 +93,7 @@ var ( "name": true, // redundant, since this is explicitly generated for every target "from": true, // reserved keyword "in": true, // reserved keyword + "size": true, // reserved for tests "arch": true, // interface prop type is not supported yet. "multilib": true, // interface prop type is not supported yet. "target": true, // interface prop type is not supported yet. diff --git a/bp2build/metrics.go b/bp2build/metrics.go new file mode 100644 index 000000000..916129fba --- /dev/null +++ b/bp2build/metrics.go @@ -0,0 +1,30 @@ +package bp2build + +import ( + "android/soong/android" + "fmt" +) + +// Simple metrics struct to collect information about a Blueprint to BUILD +// conversion process. +type CodegenMetrics struct { + // Total number of Soong/Blueprint modules + TotalModuleCount int + + // Counts of generated Bazel targets per Bazel rule class + RuleClassCount map[string]int +} + +// Print the codegen metrics to stdout. +func (metrics CodegenMetrics) Print() { + generatedTargetCount := 0 + for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) { + count := metrics.RuleClassCount[ruleClass] + fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count) + generatedTargetCount += count + } + fmt.Printf( + "[bp2build] Generated %d total BUILD targets from %d Android.bp modules.\n", + generatedTargetCount, + metrics.TotalModuleCount) +} diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go index dcc75bd5c..2aa373c3f 100644 --- a/bp2build/sh_conversion_test.go +++ b/bp2build/sh_conversion_test.go @@ -115,7 +115,8 @@ func TestShBinaryBp2Build(t *testing.T) { if testCase.dir != "" { checkDir = testCase.dir } - bazelTargets := GenerateBazelTargets(ctx.Context.Context, Bp2Build)[checkDir] + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) } else { diff --git a/bp2build/testing.go b/bp2build/testing.go index 2e59999c2..bd75a8fc8 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -25,10 +25,9 @@ type customProps struct { type customModule struct { android.ModuleBase + android.BazelModuleBase props customProps - - bazelProps bazel.Properties } // OutputFiles is needed because some instances of this module use dist with a @@ -44,7 +43,7 @@ func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { func customModuleFactoryBase() android.Module { module := &customModule{} module.AddProperties(&module.props) - module.AddProperties(&module.bazelProps) + android.InitBazelModule(module) return module } @@ -127,7 +126,7 @@ func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContex func customBp2BuildMutator(ctx android.TopDownMutatorContext) { if m, ok := ctx.Module().(*customModule); ok { - if !m.bazelProps.Bazel_module.Bp2build_available { + if !m.ConvertWithBp2build() { return } @@ -136,9 +135,11 @@ func customBp2BuildMutator(ctx android.TopDownMutatorContext) { String_list_prop: m.props.String_list_prop, } - props := bazel.NewBazelTargetModuleProperties(m.Name(), "custom", "") + props := bazel.BazelTargetModuleProperties{ + Rule_class: "custom", + } - ctx.CreateBazelTargetModule(customBazelModuleFactory, props, attrs) + ctx.CreateBazelTargetModule(customBazelModuleFactory, m.Name(), props, attrs) } } @@ -146,32 +147,35 @@ func customBp2BuildMutator(ctx android.TopDownMutatorContext) { // module to target. func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) { if m, ok := ctx.Module().(*customModule); ok { - if !m.bazelProps.Bazel_module.Bp2build_available { + if !m.ConvertWithBp2build() { return } baseName := m.Name() attrs := &customBazelModuleAttributes{} - myLibraryProps := bazel.NewBazelTargetModuleProperties( - baseName, - "my_library", - "//build/bazel/rules:rules.bzl", - ) - ctx.CreateBazelTargetModule(customBazelModuleFactory, myLibraryProps, attrs) - - protoLibraryProps := bazel.NewBazelTargetModuleProperties( - baseName+"_proto_library_deps", - "proto_library", - "//build/bazel/rules:proto.bzl", - ) - ctx.CreateBazelTargetModule(customBazelModuleFactory, protoLibraryProps, attrs) - - myProtoLibraryProps := bazel.NewBazelTargetModuleProperties( - baseName+"_my_proto_library_deps", - "my_proto_library", - "//build/bazel/rules:proto.bzl", - ) - ctx.CreateBazelTargetModule(customBazelModuleFactory, myProtoLibraryProps, attrs) + myLibraryProps := bazel.BazelTargetModuleProperties{ + Rule_class: "my_library", + Bzl_load_location: "//build/bazel/rules:rules.bzl", + } + ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName, myLibraryProps, attrs) + + protoLibraryProps := bazel.BazelTargetModuleProperties{ + Rule_class: "proto_library", + Bzl_load_location: "//build/bazel/rules:proto.bzl", + } + ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_proto_library_deps", protoLibraryProps, attrs) + + myProtoLibraryProps := bazel.BazelTargetModuleProperties{ + Rule_class: "my_proto_library", + Bzl_load_location: "//build/bazel/rules:proto.bzl", + } + ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_my_proto_library_deps", myProtoLibraryProps, attrs) } } + +// Helper method for tests to easily access the targets in a dir. +func generateBazelTargetsForDir(codegenCtx CodegenContext, dir string) BazelTargets { + buildFileToTargets, _ := GenerateBazelTargets(codegenCtx) + return buildFileToTargets[dir] +} diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go index 94b825208..fae610189 100644 --- a/bpfix/bpfix/bpfix.go +++ b/bpfix/bpfix/bpfix.go @@ -670,6 +670,26 @@ func rewriteAndroidAppImport(f *Fixer) error { return nil } +func RewriteRuntimeResourceOverlay(f *Fixer) error { + for _, def := range f.tree.Defs { + mod, ok := def.(*parser.Module) + if !(ok && mod.Type == "runtime_resource_overlay") { + continue + } + // runtime_resource_overlays are always product specific in Make. + if _, ok := mod.GetProperty("product_specific"); !ok { + prop := &parser.Property{ + Name: "product_specific", + Value: &parser.Bool{ + Value: true, + }, + } + mod.Properties = append(mod.Properties, prop) + } + } + return nil +} + // Removes library dependencies which are empty (and restricted from usage in Soong) func removeEmptyLibDependencies(f *Fixer) error { emptyLibraries := []string{ diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go index ef9814fb8..61dfe1af4 100644 --- a/bpfix/bpfix/bpfix_test.go +++ b/bpfix/bpfix/bpfix_test.go @@ -1056,3 +1056,71 @@ func TestRemovePdkProperty(t *testing.T) { }) } } + +func TestRewriteRuntimeResourceOverlay(t *testing.T) { + tests := []struct { + name string + in string + out string + }{ + { + name: "product_specific runtime_resource_overlay", + in: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + product_specific: true, + } + `, + out: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + product_specific: true, + } + `, + }, + { + // It's probably wrong for runtime_resource_overlay not to be product specific, but let's not + // debate it here. + name: "non-product_specific runtime_resource_overlay", + in: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + product_specific: false, + } + `, + out: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + product_specific: false, + } + `, + }, + { + name: "runtime_resource_overlay without product_specific value", + in: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + } + `, + out: ` + runtime_resource_overlay { + name: "foo", + resource_dirs: ["res"], + product_specific: true, + } + `, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + runPass(t, test.in, test.out, func(fixer *Fixer) error { + return RewriteRuntimeResourceOverlay(fixer) + }) + }) + } +} diff --git a/build_kzip.bash b/build_kzip.bash index 0018ea9b2..a4659d4c8 100755 --- a/build_kzip.bash +++ b/build_kzip.bash @@ -7,14 +7,17 @@ # BUILD_NUMBER build number, used to generate unique ID (will use UUID if not set) # DIST_DIR where the resulting all.kzip will be placed # KYTHE_KZIP_ENCODING proto or json (proto is default) +# KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit # OUT_DIR output directory (out if not specified}) # TARGET_BUILD_VARIANT variant, e.g., `userdebug` # TARGET_PRODUCT target device name, e.g., 'aosp_blueline' # XREF_CORPUS source code repository URI, e.g., 'android.googlesource.com/platform/superproject' : ${BUILD_NUMBER:=$(uuidgen)} +: ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500} : ${KYTHE_KZIP_ENCODING:=proto} -export KYTHE_KZIP_ENCODING +: ${XREF_CORPUS:?should be set} +export KYTHE_JAVA_SOURCE_BATCH_SIZE KYTHE_KZIP_ENCODING # The extraction might fail for some source files, so run with -k and then check that # sufficiently many files were generated. @@ -27,11 +30,15 @@ build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xr declare -r abspath_out=$(realpath "${out}") declare -r go_extractor=$(realpath prebuilts/build-tools/linux-x86/bin/go_extractor) declare -r go_root=$(realpath prebuilts/go/linux-x86) -declare -r vnames_path=$(realpath build/soong/vnames.go.json) declare -r source_root=$PWD + +# TODO(asmundak): Until b/182183061 is fixed, default corpus has to be specified +# in the rules file. Generate this file on the fly with corpus value set from the +# environment variable. for dir in blueprint soong; do (cd "build/$dir"; - KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" --rules="${vnames_path}" \ + KYTHE_ROOT_DIRECTORY="${source_root}" "$go_extractor" --goroot="$go_root" \ + --rules=<(printf '[{"pattern": "(.*)","vname": {"path": "@1@", "corpus":"%s"}}]' "${XREF_CORPUS}") \ --canonicalize_package_corpus --output "${abspath_out}/soong/build_${dir}.go.kzip" ./... ) done @@ -28,7 +28,6 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/cc/config" "android/soong/genrule" ) @@ -369,8 +368,6 @@ type BaseProperties struct { // can depend on libraries that are not exported by the APEXes and use private symbols // from the exported libraries. Test_for []string - - bazel.Properties } type VendorProperties struct { @@ -584,6 +581,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 } @@ -769,6 +777,7 @@ type Module struct { android.DefaultableModuleBase android.ApexModuleBase android.SdkBase + android.BazelModuleBase Properties BaseProperties VendorProperties VendorProperties @@ -785,9 +794,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 @@ -1058,6 +1068,7 @@ func (c *Module) Init() android.Module { } android.InitAndroidArchModule(c, c.hod, c.multilib) + android.InitBazelModule(c) android.InitApexModule(c) android.InitSdkAwareModule(c) android.InitDefaultableModule(c) @@ -1564,24 +1575,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 { @@ -1611,6 +1605,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, @@ -2426,36 +2457,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) -} - func (c *Module) sdclang(ctx BaseModuleContext) bool { sdclang := Bool(c.Properties.Sdclang) @@ -2654,16 +2655,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/cc_test.go b/cc/cc_test.go index 2283d5977..e640f123e 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "reflect" + "regexp" "strings" "testing" @@ -3910,3 +3911,274 @@ func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) { checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async) checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync) } + +func TestIncludeDirsExporting(t *testing.T) { + + // Trim spaces from the beginning, end and immediately after any newline characters. Leaves + // embedded newline characters alone. + trimIndentingSpaces := func(s string) string { + return strings.TrimSpace(regexp.MustCompile("(^|\n)\\s+").ReplaceAllString(s, "$1")) + } + + checkPaths := func(t *testing.T, message string, expected string, paths android.Paths) { + t.Helper() + expected = trimIndentingSpaces(expected) + actual := trimIndentingSpaces(strings.Join(android.FirstUniqueStrings(android.NormalizePathsForTesting(paths)), "\n")) + if expected != actual { + t.Errorf("%s: expected:\n%s\n actual:\n%s\n", message, expected, actual) + } + } + + type exportedChecker func(t *testing.T, name string, exported FlagExporterInfo) + + checkIncludeDirs := func(t *testing.T, ctx *android.TestContext, module android.Module, checkers ...exportedChecker) { + t.Helper() + exported := ctx.ModuleProvider(module, FlagExporterInfoProvider).(FlagExporterInfo) + name := module.Name() + + for _, checker := range checkers { + checker(t, name, exported) + } + } + + expectedIncludeDirs := func(expectedPaths string) exportedChecker { + return func(t *testing.T, name string, exported FlagExporterInfo) { + t.Helper() + checkPaths(t, fmt.Sprintf("%s: include dirs", name), expectedPaths, exported.IncludeDirs) + } + } + + expectedSystemIncludeDirs := func(expectedPaths string) exportedChecker { + return func(t *testing.T, name string, exported FlagExporterInfo) { + t.Helper() + checkPaths(t, fmt.Sprintf("%s: system include dirs", name), expectedPaths, exported.SystemIncludeDirs) + } + } + + expectedGeneratedHeaders := func(expectedPaths string) exportedChecker { + return func(t *testing.T, name string, exported FlagExporterInfo) { + t.Helper() + checkPaths(t, fmt.Sprintf("%s: generated headers", name), expectedPaths, exported.GeneratedHeaders) + } + } + + expectedOrderOnlyDeps := func(expectedPaths string) exportedChecker { + return func(t *testing.T, name string, exported FlagExporterInfo) { + t.Helper() + checkPaths(t, fmt.Sprintf("%s: order only deps", name), expectedPaths, exported.Deps) + } + } + + genRuleModules := ` + genrule { + name: "genrule_foo", + cmd: "generate-foo", + out: [ + "generated_headers/foo/generated_header.h", + ], + export_include_dirs: [ + "generated_headers", + ], + } + + genrule { + name: "genrule_bar", + cmd: "generate-bar", + out: [ + "generated_headers/bar/generated_header.h", + ], + export_include_dirs: [ + "generated_headers", + ], + } + ` + + t.Run("ensure exported include dirs are not automatically re-exported from shared_libs", func(t *testing.T) { + ctx := testCc(t, genRuleModules+` + cc_library { + name: "libfoo", + srcs: ["foo.c"], + export_include_dirs: ["foo/standard"], + export_system_include_dirs: ["foo/system"], + generated_headers: ["genrule_foo"], + export_generated_headers: ["genrule_foo"], + } + + cc_library { + name: "libbar", + srcs: ["bar.c"], + shared_libs: ["libfoo"], + export_include_dirs: ["bar/standard"], + export_system_include_dirs: ["bar/system"], + generated_headers: ["genrule_bar"], + export_generated_headers: ["genrule_bar"], + } + `) + foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, foo, + expectedIncludeDirs(` + foo/standard + .intermediates/genrule_foo/gen/generated_headers + `), + expectedSystemIncludeDirs(`foo/system`), + expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`), + expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`), + ) + + bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, bar, + expectedIncludeDirs(` + bar/standard + .intermediates/genrule_bar/gen/generated_headers + `), + expectedSystemIncludeDirs(`bar/system`), + expectedGeneratedHeaders(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`), + expectedOrderOnlyDeps(`.intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h`), + ) + }) + + t.Run("ensure exported include dirs are automatically re-exported from whole_static_libs", func(t *testing.T) { + ctx := testCc(t, genRuleModules+` + cc_library { + name: "libfoo", + srcs: ["foo.c"], + export_include_dirs: ["foo/standard"], + export_system_include_dirs: ["foo/system"], + generated_headers: ["genrule_foo"], + export_generated_headers: ["genrule_foo"], + } + + cc_library { + name: "libbar", + srcs: ["bar.c"], + whole_static_libs: ["libfoo"], + export_include_dirs: ["bar/standard"], + export_system_include_dirs: ["bar/system"], + generated_headers: ["genrule_bar"], + export_generated_headers: ["genrule_bar"], + } + `) + foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, foo, + expectedIncludeDirs(` + foo/standard + .intermediates/genrule_foo/gen/generated_headers + `), + expectedSystemIncludeDirs(`foo/system`), + expectedGeneratedHeaders(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`), + expectedOrderOnlyDeps(`.intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h`), + ) + + bar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, bar, + expectedIncludeDirs(` + bar/standard + foo/standard + .intermediates/genrule_foo/gen/generated_headers + .intermediates/genrule_bar/gen/generated_headers + `), + expectedSystemIncludeDirs(` + bar/system + foo/system + `), + expectedGeneratedHeaders(` + .intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h + .intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h + `), + expectedOrderOnlyDeps(` + .intermediates/genrule_foo/gen/generated_headers/foo/generated_header.h + .intermediates/genrule_bar/gen/generated_headers/bar/generated_header.h + `), + ) + }) + + t.Run("ensure only aidl headers are exported", func(t *testing.T) { + ctx := testCc(t, genRuleModules+` + cc_library_shared { + name: "libfoo", + srcs: [ + "foo.c", + "b.aidl", + "a.proto", + ], + aidl: { + export_aidl_headers: true, + } + } + `) + foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, foo, + expectedIncludeDirs(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl + `), + expectedSystemIncludeDirs(``), + expectedGeneratedHeaders(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h + `), + expectedOrderOnlyDeps(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h + `), + ) + }) + + t.Run("ensure only proto headers are exported", func(t *testing.T) { + ctx := testCc(t, genRuleModules+` + cc_library_shared { + name: "libfoo", + srcs: [ + "foo.c", + "b.aidl", + "a.proto", + ], + proto: { + export_proto_headers: true, + } + } + `) + foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, foo, + expectedIncludeDirs(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto + `), + expectedSystemIncludeDirs(``), + expectedGeneratedHeaders(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h + `), + expectedOrderOnlyDeps(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/proto/a.pb.h + `), + ) + }) + + t.Run("ensure only sysprop headers are exported", func(t *testing.T) { + ctx := testCc(t, genRuleModules+` + cc_library_shared { + name: "libfoo", + srcs: [ + "foo.c", + "a.sysprop", + "b.aidl", + "a.proto", + ], + } + `) + foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module() + checkIncludeDirs(t, ctx, foo, + expectedIncludeDirs(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include + `), + expectedSystemIncludeDirs(``), + expectedGeneratedHeaders(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h + `), + expectedOrderOnlyDeps(` + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/include/a.sysprop.h + .intermediates/libfoo/android_arm64_armv8-a_shared/gen/sysprop/public/include/a.sysprop.h + `), + ) + }) +} diff --git a/cc/compiler.go b/cc/compiler.go index 5f30d3d6e..2e71922e1 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -230,6 +230,8 @@ type baseCompiler struct { // other modules and filegroups. May include source files that have not yet been translated to // C/C++ (.aidl, .proto, etc.) srcsBeforeGen android.Paths + + generatedSourceInfo } var _ compiler = (*baseCompiler)(nil) @@ -440,7 +442,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps fmt.Sprintf("${config.%sClangGlobalCflags}", hod)) if isThirdParty(modulePath) { - flags.Global.CommonFlags = append([]string{"${config.ClangExternalCflags}"}, flags.Global.CommonFlags...) + flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ClangExternalCflags}") } if tc.Bionic() { @@ -634,10 +636,11 @@ func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathD srcs := append(android.Paths(nil), compiler.srcsBeforeGen...) - srcs, genDeps := genSources(ctx, srcs, buildFlags) + srcs, genDeps, info := genSources(ctx, srcs, buildFlags) pathDeps = append(pathDeps, genDeps...) compiler.pathDeps = pathDeps + compiler.generatedSourceInfo = info compiler.cFlagsDeps = flags.CFlagsDeps // Save src, buildFlags and context 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-*", @@ -220,8 +220,35 @@ func genWinMsg(ctx android.ModuleContext, srcFile android.Path, flags builderFla return rcFile, headerFile } +// Used to communicate information from the genSources method back to the library code that uses +// it. +type generatedSourceInfo struct { + // The headers created from .proto files + protoHeaders android.Paths + + // The files that can be used as order only dependencies in order to ensure that the proto header + // files are up to date. + protoOrderOnlyDeps android.Paths + + // The headers created from .aidl files + aidlHeaders android.Paths + + // The files that can be used as order only dependencies in order to ensure that the aidl header + // files are up to date. + aidlOrderOnlyDeps android.Paths + + // The headers created from .sysprop files + syspropHeaders android.Paths + + // The files that can be used as order only dependencies in order to ensure that the sysprop + // header files are up to date. + syspropOrderOnlyDeps android.Paths +} + func genSources(ctx android.ModuleContext, srcFiles android.Paths, - buildFlags builderFlags) (android.Paths, android.Paths) { + buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) { + + var info generatedSourceInfo var deps android.Paths var rsFiles android.Paths @@ -258,7 +285,9 @@ func genSources(ctx android.ModuleContext, srcFiles android.Paths, case ".proto": ccFile, headerFile := genProto(ctx, srcFile, buildFlags) srcFiles[i] = ccFile - deps = append(deps, headerFile) + info.protoHeaders = append(info.protoHeaders, headerFile) + // Use the generated header as an order only dep to ensure that it is up to date when needed. + info.protoOrderOnlyDeps = append(info.protoOrderOnlyDeps, headerFile) case ".aidl": if aidlRule == nil { aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"), @@ -267,7 +296,12 @@ func genSources(ctx android.ModuleContext, srcFiles android.Paths, cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp") depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d") srcFiles[i] = cppFile - deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...) + aidlHeaders := genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags) + info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...) + // Use the generated headers as order only deps to ensure that they are up to date when + // needed. + // TODO: Reduce the size of the ninja file by using one order only dep for the whole rule + info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...) case ".rscript", ".fs": cppFile := rsGeneratedCppFile(ctx, srcFile) rsFiles = append(rsFiles, srcFiles[i]) @@ -279,7 +313,10 @@ func genSources(ctx android.ModuleContext, srcFiles android.Paths, case ".sysprop": cppFile, headerFiles := genSysprop(ctx, srcFile) srcFiles[i] = cppFile - deps = append(deps, headerFiles...) + info.syspropHeaders = append(info.syspropHeaders, headerFiles...) + // Use the generated headers as order only deps to ensure that they are up to date when + // needed. + info.syspropOrderOnlyDeps = append(info.syspropOrderOnlyDeps, headerFiles...) } } @@ -291,9 +328,13 @@ func genSources(ctx android.ModuleContext, srcFiles android.Paths, yaccRule_.Build("yacc", "gen yacc") } + deps = append(deps, info.protoOrderOnlyDeps...) + deps = append(deps, info.aidlOrderOnlyDeps...) + deps = append(deps, info.syspropOrderOnlyDeps...) + if len(rsFiles) > 0 { deps = append(deps, rsGenerateCpp(ctx, rsFiles, buildFlags.rsFlags)...) } - return srcFiles, deps + return srcFiles, deps, info } diff --git a/cc/library.go b/cc/library.go index d75059182..6acbc5b8e 100644 --- a/cc/library.go +++ b/cc/library.go @@ -1331,9 +1331,8 @@ func (library *libraryDecorator) link(ctx ModuleContext, dir := android.PathForModuleGen(ctx, "aidl") library.reexportDirs(dir) - // TODO: restrict to aidl deps - library.reexportDeps(library.baseCompiler.pathDeps...) - library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...) + library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...) + library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...) } } @@ -1347,9 +1346,8 @@ func (library *libraryDecorator) link(ctx ModuleContext, includes = append(includes, flags.proto.Dir) library.reexportDirs(includes...) - // TODO: restrict to proto deps - library.reexportDeps(library.baseCompiler.pathDeps...) - library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...) + library.reexportDeps(library.baseCompiler.protoOrderOnlyDeps...) + library.addExportedGeneratedHeaders(library.baseCompiler.protoHeaders...) } } @@ -1368,10 +1366,16 @@ func (library *libraryDecorator) link(ctx ModuleContext, } } + // Make sure to only export headers which are within the include directory. + _, headers := android.FilterPathListPredicate(library.baseCompiler.syspropHeaders, func(path android.Path) bool { + _, isRel := android.MaybeRel(ctx, dir.String(), path.String()) + return isRel + }) + // Add sysprop-related directories to the exported directories of this library. library.reexportDirs(dir) - library.reexportDeps(library.baseCompiler.pathDeps...) - library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...) + library.reexportDeps(library.baseCompiler.syspropOrderOnlyDeps...) + library.addExportedGeneratedHeaders(headers...) } // Add stub-related flags if this library is a stub library. diff --git a/cc/library_headers.go b/cc/library_headers.go index e5a555729..dc851a59c 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -86,7 +86,7 @@ func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) { return } - if !module.Properties.Bazel_module.Bp2build_available { + if !module.ConvertWithBp2build() { return } @@ -129,13 +129,12 @@ func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) { Deps: headerLibLabels, } - props := bazel.NewBazelTargetModuleProperties( - module.Name(), - "cc_library_headers", - "//build/bazel/rules:cc_library_headers.bzl", - ) + props := bazel.BazelTargetModuleProperties{ + Rule_class: "cc_library_headers", + Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl", + } - ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, props, attrs) + ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, module.Name(), props, attrs) } func (m *bazelCcLibraryHeaders) Name() string { diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 14029919e..d5f2adf8b 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -206,22 +206,22 @@ var includeDirProperties = []includeDirsProperty{ dirs: true, }, { - // exportedGeneratedIncludeDirs lists directories that contains some header files - // that are explicitly listed in the exportedGeneratedHeaders property. So, the contents + // ExportedGeneratedIncludeDirs lists directories that contains some header files + // that are explicitly listed in the ExportedGeneratedHeaders property. So, the contents // of these directories do not need to be copied, but these directories do need adding to // the export_include_dirs property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedIncludeDirs }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedIncludeDirs }, propertyName: "export_include_dirs", snapshotDir: nativeGeneratedIncludeDir, copy: false, dirs: true, }, { - // exportedGeneratedHeaders lists header files that are in one of the directories - // specified in exportedGeneratedIncludeDirs must be copied into the snapshot. - // As they are in a directory in exportedGeneratedIncludeDirs they do not need adding to a + // ExportedGeneratedHeaders lists header files that are in one of the directories + // specified in ExportedGeneratedIncludeDirs must be copied into the snapshot. + // As they are in a directory in ExportedGeneratedIncludeDirs they do not need adding to a // property in the prebuilt module in the snapshot. - pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.exportedGeneratedHeaders }, + pathsGetter: func(libInfo *nativeLibInfoProperties) android.Paths { return libInfo.ExportedGeneratedHeaders }, propertyName: "", snapshotDir: nativeGeneratedIncludeDir, copy: true, @@ -258,42 +258,52 @@ func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, b // values where necessary. for _, propertyInfo := range includeDirProperties { // Calculate the base directory in the snapshot into which the files will be copied. - // lib.ArchType is "" for common properties. + // lib.archType is "" for common properties. targetDir := filepath.Join(libInfo.OsPrefix(), libInfo.archType, propertyInfo.snapshotDir) propertyName := propertyInfo.propertyName // Iterate over each path in one of the include directory properties. for _, path := range propertyInfo.pathsGetter(libInfo) { + inputPath := path.String() + + // Map the input path to a snapshot relative path. The mapping is independent of the module + // that references them so that if multiple modules within the same snapshot export the same + // header files they end up in the same place in the snapshot and so do not get duplicated. + targetRelativePath := inputPath + if isGeneratedHeaderDirectory(path) { + // Remove everything up to the .intermediates/ from the generated output directory to + // leave a module relative path. + base := android.PathForIntermediates(sdkModuleContext, "") + targetRelativePath = android.Rel(sdkModuleContext, base.String(), inputPath) + } + + snapshotRelativePath := filepath.Join(targetDir, targetRelativePath) // Copy the files/directories when necessary. if propertyInfo.copy { if propertyInfo.dirs { // When copying a directory glob and copy all the headers within it. // TODO(jiyong) copy headers having other suffixes - headers, _ := sdkModuleContext.GlobWithDeps(path.String()+"/**/*.h", nil) + headers, _ := sdkModuleContext.GlobWithDeps(inputPath+"/**/*.h", nil) for _, file := range headers { src := android.PathForSource(sdkModuleContext, file) - dest := filepath.Join(targetDir, file) + + // The destination path in the snapshot is constructed from the snapshot relative path + // of the input directory and the input directory relative path of the header file. + inputRelativePath := android.Rel(sdkModuleContext, inputPath, file) + dest := filepath.Join(snapshotRelativePath, inputRelativePath) builder.CopyToSnapshot(src, dest) } } else { - // Otherwise, just copy the files. - dest := filepath.Join(targetDir, libInfo.name, path.Rel()) - builder.CopyToSnapshot(path, dest) + // Otherwise, just copy the file to its snapshot relative path. + builder.CopyToSnapshot(path, snapshotRelativePath) } } // Only directories are added to a property. if propertyInfo.dirs { - var snapshotPath string - if isGeneratedHeaderDirectory(path) { - snapshotPath = filepath.Join(targetDir, libInfo.name) - } else { - snapshotPath = filepath.Join(targetDir, path.String()) - } - - includeDirs[propertyName] = append(includeDirs[propertyName], snapshotPath) + includeDirs[propertyName] = append(includeDirs[propertyName], snapshotRelativePath) } } } @@ -330,9 +340,6 @@ type nativeLibInfoProperties struct { memberType *librarySdkMemberType - // The name of the library, is not exported as this must not be changed during optimization. - name string - // archType is not exported as if set (to a non default value) it is always arch specific. // This is "" for common properties. archType string @@ -344,13 +351,13 @@ type nativeLibInfoProperties struct { // The list of arch specific exported generated include dirs. // - // This field is not exported as its contents are always arch specific. - exportedGeneratedIncludeDirs android.Paths + // This field is exported as its contents may not be arch specific, e.g. protos. + ExportedGeneratedIncludeDirs android.Paths `android:"arch_variant"` // The list of arch specific exported generated header files. // - // This field is not exported as its contents are is always arch specific. - exportedGeneratedHeaders android.Paths + // This field is exported as its contents may not be arch specific, e.g. protos. + ExportedGeneratedHeaders android.Paths `android:"arch_variant"` // The list of possibly common exported system include dirs. // @@ -419,12 +426,11 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte exportedIncludeDirs, exportedGeneratedIncludeDirs := android.FilterPathListPredicate( exportedInfo.IncludeDirs, isGeneratedHeaderDirectory) - p.name = variant.Name() p.archType = ccModule.Target().Arch.ArchType.String() // Make sure that the include directories are unique. p.ExportedIncludeDirs = android.FirstUniquePaths(exportedIncludeDirs) - p.exportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs) + p.ExportedGeneratedIncludeDirs = android.FirstUniquePaths(exportedGeneratedIncludeDirs) // Take a copy before filtering out duplicates to avoid changing the slice owned by the // ccModule. @@ -450,7 +456,7 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte } p.SystemSharedLibs = specifiedDeps.systemSharedLibs } - p.exportedGeneratedHeaders = exportedInfo.GeneratedHeaders + p.ExportedGeneratedHeaders = exportedInfo.GeneratedHeaders if !p.memberType.noOutputFiles && addOutputFile { p.outputFile = getRequiredMemberOutputFile(ctx, ccModule) diff --git a/cc/object.go b/cc/object.go index d92e1109f..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"}) @@ -92,7 +113,8 @@ func ObjectFactory() android.Module { // For bp2build conversion. type bazelObjectAttributes struct { Srcs bazel.LabelList - Copts []string + Deps bazel.LabelList + Copts bazel.StringListAttribute Local_include_dirs []string } @@ -118,7 +140,7 @@ func BazelObjectFactory() android.Module { // Bazel equivalent target, plus any necessary include deps for the cc_object. func ObjectBp2Build(ctx android.TopDownMutatorContext) { m, ok := ctx.Module().(*Module) - if !ok || !m.Properties.Bazel_module.Bp2build_available { + if !ok || !m.ConvertWithBp2build() { return } @@ -132,31 +154,48 @@ func ObjectBp2Build(ctx android.TopDownMutatorContext) { ctx.ModuleErrorf("compiler must not be nil for a cc_object module") } - var copts []string + // Set arch-specific configurable attributes + var copts bazel.StringListAttribute var srcs []string + var excludeSrcs []string var localIncludeDirs []string for _, props := range m.compiler.compilerProps() { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { - copts = baseCompilerProps.Cflags + copts.Value = baseCompilerProps.Cflags srcs = baseCompilerProps.Srcs + excludeSrcs = baseCompilerProps.Exclude_srcs localIncludeDirs = baseCompilerProps.Local_include_dirs break } } + var deps bazel.LabelList + for _, props := range m.linker.linkerProps() { + if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok { + deps = android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs) + } + } + + for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) { + if cProps, ok := p.(*BaseCompilerProperties); ok { + copts.SetValueForArch(arch.Name, cProps.Cflags) + } + } + copts.SetValueForArch("default", []string{}) + attrs := &bazelObjectAttributes{ - Srcs: android.BazelLabelForModuleSrc(ctx, srcs), + Srcs: android.BazelLabelForModuleSrcExcludes(ctx, srcs, excludeSrcs), + Deps: deps, Copts: copts, Local_include_dirs: localIncludeDirs, } - props := bazel.NewBazelTargetModuleProperties( - m.Name(), - "cc_object", - "//build/bazel/rules:cc_object.bzl", - ) + props := bazel.BazelTargetModuleProperties{ + Rule_class: "cc_object", + Bzl_load_location: "//build/bazel/rules:cc_object.bzl", + } - ctx.CreateBazelTargetModule(BazelObjectFactory, props, attrs) + ctx.CreateBazelTargetModule(BazelObjectFactory, m.Name(), props, attrs) } func (object *objectLinker) appendLdflags(flags []string) { 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/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go index 62daafde4..f9aea0cfe 100644 --- a/cc/snapshot_prebuilt.go +++ b/cc/snapshot_prebuilt.go @@ -280,50 +280,36 @@ func (s *snapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Nothing, the snapshot module is only used to forward dependency information in DepsMutator. } -func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) { - collectSnapshotMap := func(variations []blueprint.Variation, depTag blueprint.DependencyTag, - names []string, snapshotSuffix, moduleSuffix string) map[string]string { - - decoratedNames := make([]string, 0, len(names)) - for _, name := range names { - decoratedNames = append(decoratedNames, name+ - snapshotSuffix+moduleSuffix+ - s.baseSnapshot.version()+ - "."+ctx.Arch().ArchType.Name) - } +func getSnapshotNameSuffix(moduleSuffix, version, arch string) string { + versionSuffix := version + if arch != "" { + versionSuffix += "." + arch + } + return moduleSuffix + versionSuffix +} - deps := ctx.AddVariationDependencies(variations, depTag, decoratedNames...) +func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) { + collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string { snapshotMap := make(map[string]string) - for _, dep := range deps { - if dep == nil { - continue - } - - snapshotMap[dep.(*Module).BaseModuleName()] = ctx.OtherModuleName(dep) + for _, name := range names { + snapshotMap[name] = name + + getSnapshotNameSuffix(snapshotSuffix+moduleSuffix, + s.baseSnapshot.version(), ctx.Arch().ArchType.Name) } return snapshotMap } snapshotSuffix := s.image.moduleNameSuffix() - headers := collectSnapshotMap(nil, HeaderDepTag(), s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix) - binaries := collectSnapshotMap(nil, nil, s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix) - objects := collectSnapshotMap(nil, nil, s.properties.Objects, snapshotSuffix, snapshotObjectSuffix) - - staticLibs := collectSnapshotMap([]blueprint.Variation{ - {Mutator: "link", Variation: "static"}, - }, StaticDepTag(), s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix) - - sharedLibs := collectSnapshotMap([]blueprint.Variation{ - {Mutator: "link", Variation: "shared"}, - }, SharedDepTag(), s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix) - - vndkLibs := collectSnapshotMap([]blueprint.Variation{ - {Mutator: "link", Variation: "shared"}, - }, SharedDepTag(), s.properties.Vndk_libs, "", vndkSuffix) - + headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix) + binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix) + objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix) + staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix) + sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix) + vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix) for k, v := range vndkLibs { sharedLibs[k] = v } + ctx.SetProvider(SnapshotInfoProvider, SnapshotInfo{ HeaderLibs: headers, Binaries: binaries, @@ -395,12 +381,7 @@ func (p *baseSnapshotDecorator) Name(name string) string { } func (p *baseSnapshotDecorator) NameSuffix() string { - versionSuffix := p.version() - if p.arch() != "" { - versionSuffix += "." + p.arch() - } - - return p.baseProperties.ModuleSuffix + versionSuffix + return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch()) } func (p *baseSnapshotDecorator) version() string { diff --git a/cc/testing.go b/cc/testing.go index 45e531208..f62c5f114 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -25,7 +25,6 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { RegisterBinaryBuildComponents(ctx) RegisterLibraryBuildComponents(ctx) RegisterLibraryHeadersBuildComponents(ctx) - genrule.RegisterGenruleBuildComponents(ctx) ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory) ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory) @@ -591,6 +590,7 @@ func TestConfig(buildDir string, os android.OsType, env map[string]string, func CreateTestContext(config android.Config) *android.TestContext { ctx := android.NewTestArchContext(config) + genrule.RegisterGenruleBuildComponents(ctx) ctx.RegisterModuleType("cc_fuzz", FuzzFactory) ctx.RegisterModuleType("cc_test", TestFactory) ctx.RegisterModuleType("cc_test_library", TestLibraryFactory) diff --git a/cc/tidy.go b/cc/tidy.go index 251c67b07..616cf8ab4 100644 --- a/cc/tidy.go +++ b/cc/tidy.go @@ -42,6 +42,21 @@ type tidyFeature struct { Properties TidyProperties } +var quotedFlagRegexp, _ = regexp.Compile(`^-?-[^=]+=('|").*('|")$`) + +// When passing flag -name=value, if user add quotes around 'value', +// the quotation marks will be preserved by NinjaAndShellEscapeList +// and the 'value' string with quotes won't work like the intended value. +// So here we report an error if -*='*' is found. +func checkNinjaAndShellEscapeList(ctx ModuleContext, prop string, slice []string) []string { + for _, s := range slice { + if quotedFlagRegexp.MatchString(s) { + ctx.PropertyErrorf(prop, "Extra quotes in: %s", s) + } + } + return proptools.NinjaAndShellEscapeList(slice) +} + func (tidy *tidyFeature) props() []interface{} { return []interface{}{&tidy.Properties} } @@ -74,8 +89,8 @@ func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags { if len(withTidyFlags) > 0 { flags.TidyFlags = append(flags.TidyFlags, withTidyFlags) } - esc := proptools.NinjaAndShellEscapeList - flags.TidyFlags = append(flags.TidyFlags, esc(tidy.Properties.Tidy_flags)...) + esc := checkNinjaAndShellEscapeList + flags.TidyFlags = append(flags.TidyFlags, esc(ctx, "tidy_flags", tidy.Properties.Tidy_flags)...) // If TidyFlags does not contain -header-filter, add default header filter. // Find the substring because the flag could also appear as --header-filter=... // and with or without single or double quotes. @@ -119,7 +134,7 @@ func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags { tidyChecks += config.TidyChecksForDir(ctx.ModuleDir()) } if len(tidy.Properties.Tidy_checks) > 0 { - tidyChecks = tidyChecks + "," + strings.Join(esc( + tidyChecks = tidyChecks + "," + strings.Join(esc(ctx, "tidy_checks", config.ClangRewriteTidyChecks(tidy.Properties.Tidy_checks)), ",") } if ctx.Windows() { @@ -165,7 +180,7 @@ func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags { flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*") } } else if len(tidy.Properties.Tidy_checks_as_errors) > 0 { - tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",") + tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(ctx, "tidy_checks_as_errors", tidy.Properties.Tidy_checks_as_errors), ",") flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors) } return flags diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go index 7e283fb89..083327740 100644 --- a/cc/vendor_snapshot_test.go +++ b/cc/vendor_snapshot_test.go @@ -416,7 +416,13 @@ func TestVendorSnapshotUse(t *testing.T) { name: "libvendor", version: "BOARD", target_arch: "arm64", + compile_multilib: "64", vendor: true, + shared_libs: [ + "libvendor_without_snapshot", + "libvendor_available", + "libvndk", + ], arch: { arm64: { src: "libvendor.so", 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_build/main.go b/cmd/soong_build/main.go index c17e23d3a..4586f44a6 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -21,6 +21,7 @@ import ( "path/filepath" "strings" + "android/soong/shared" "github.com/google/blueprint/bootstrap" "android/soong/android" @@ -28,13 +29,21 @@ import ( ) var ( + topDir string + outDir string docFile string bazelQueryViewDir string + delveListen string + delvePath string ) func init() { + flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree") + flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)") + flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging") + flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set") flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output") - flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory") + flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top") } func newNameResolver(config android.Config) *android.NameResolver { @@ -80,18 +89,23 @@ func newConfig(srcDir string) android.Config { } func main() { - android.ReexecWithDelveMaybe() flag.Parse() + shared.ReexecWithDelveMaybe(delveListen, delvePath) + android.InitSandbox(topDir) + android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available")) + // The top-level Blueprints file is passed as the first argument. srcDir := filepath.Dir(flag.Arg(0)) var ctx *android.Context configuration := newConfig(srcDir) extraNinjaDeps := []string{configuration.ProductVariablesFileName} - // Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable - // and soong_build will rerun when it is set for the first time. - if listen := configuration.Getenv("SOONG_DELVE"); listen != "" { + // These two are here so that we restart a non-debugged soong_build when the + // user sets SOONG_DELVE the first time. + configuration.Getenv("SOONG_DELVE") + configuration.Getenv("SOONG_DELVE_PATH") + if shared.IsDebugging() { // Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is // enabled even if it completed successfully. extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve")) @@ -134,7 +148,10 @@ func main() { // Convert the Soong module graph into Bazel BUILD files. if bazelQueryViewDir != "" { - if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil { + // Run the code-generation phase to convert BazelTargetModules to BUILD files. + codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView) + absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir) + if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil { fmt.Fprintf(os.Stderr, "%s", err) os.Exit(1) } @@ -188,9 +205,15 @@ func runBp2Build(srcDir string, configuration android.Config) { // from the regular Modules. bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...) - // Run the code-generation phase to convert BazelTargetModules to BUILD files. + // Run the code-generation phase to convert BazelTargetModules to BUILD files + // and print conversion metrics to the user. codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build) - bp2build.Codegen(codegenContext) + metrics := bp2build.Codegen(codegenContext) + + // Only report metrics when in bp2build mode. The metrics aren't relevant + // for queryview, since that's a total repo-wide conversion and there's a + // 1:1 mapping for each module. + metrics.Print() // Workarounds to support running bp2build in a clean AOSP checkout with no // prior builds, and exiting early as soon as the BUILD files get generated, diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go index dc0b3234e..0a77d67a9 100644 --- a/cmd/soong_build/queryview.go +++ b/cmd/soong_build/queryview.go @@ -22,9 +22,12 @@ import ( "path/filepath" ) -func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error { +func createBazelQueryView(ctx bp2build.CodegenContext, bazelQueryViewDir string) error { ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories()) - buildToTargets := bp2build.GenerateBazelTargets(*ctx, bp2build.QueryView) + + // Ignore metrics reporting for queryview, since queryview is already a full-repo + // conversion and can use data from bazel query directly. + buildToTargets, _ := bp2build.GenerateBazelTargets(ctx) filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView) for _, f := range filesToWrite { 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/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index 74ede68b1..1c5e78ad6 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -25,6 +25,7 @@ import ( "strings" "time" + "android/soong/shared" "android/soong/ui/build" "android/soong/ui/logger" "android/soong/ui/metrics" @@ -118,6 +119,8 @@ func inList(s string, list []string) bool { // Command is the type of soong_ui execution. Only one type of // execution is specified. The args are specific to the command. func main() { + shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary()) + buildStarted := time.Now() c, args, err := getCommand(os.Args) 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..fdb00bd89 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -288,12 +288,9 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g } else { // Other libraries or APKs for which the exact <uses-library> list is unknown. - // Pass special class loader context to skip the classpath and collision check. - // This will get removed once LOCAL_USES_LIBRARIES is enforced. - // Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default - // to the &. + // We assume the class loader context is empty. rule.Command(). - Text(`class_loader_context_arg=--class-loader-context=\&`). + Text(`class_loader_context_arg=--class-loader-context=PCL[]`). Text(`stored_class_loader_context_arg=""`) } @@ -369,11 +366,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/filesystem/Android.bp b/filesystem/Android.bp index 42a4c88b5..dcdbdcf43 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -13,6 +13,7 @@ bootstrap_go_package { srcs: [ "bootimg.go", "filesystem.go", + "logical_partition.go", ], testSrcs: [ ], diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index 29e83da07..764f0452b 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -107,14 +107,8 @@ func (b *bootimg) partitionName() string { } func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { - var unsignedOutput android.OutputPath - if proptools.Bool(b.properties.Vendor_boot) { - unsignedOutput = b.buildVendorBootImage(ctx) - } else { - // TODO(jiyong): fix this - ctx.PropertyErrorf("vendor_boot", "only vendor_boot:true is supported") - return - } + vendor := proptools.Bool(b.properties.Vendor_boot) + unsignedOutput := b.buildBootImage(ctx, vendor) if proptools.Bool(b.properties.Use_avb) { b.output = b.signImage(ctx, unsignedOutput) @@ -126,17 +120,24 @@ func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.InstallFile(b.installDir, b.installFileName(), b.output) } -func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.OutputPath { +func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.OutputPath { output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()).OutputPath builder := android.NewRuleBuilder(pctx, ctx) cmd := builder.Command().BuiltTool("mkbootimg") - kernel := android.OptionalPathForModuleSrc(ctx, b.properties.Kernel_prebuilt) - if kernel.Valid() { + kernel := proptools.String(b.properties.Kernel_prebuilt) + if vendor && kernel != "" { ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") return output } + if !vendor && kernel == "" { + ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel") + return output + } + if kernel != "" { + cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel)) + } dtbName := proptools.String(b.properties.Dtb_prebuilt) if dtbName == "" { @@ -148,7 +149,11 @@ func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.Output cmdline := proptools.String(b.properties.Cmdline) if cmdline != "" { - cmd.FlagWithArg("--vendor_cmdline ", "\""+cmdline+"\"") + flag := "--cmdline " + if vendor { + flag = "--vendor_cmdline " + } + cmd.FlagWithArg(flag, "\""+proptools.ShellEscape(cmdline)+"\"") } headerVersion := proptools.String(b.properties.Header_version) @@ -174,15 +179,23 @@ func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.Output } ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep) if filesystem, ok := ramdisk.(*filesystem); ok { - cmd.FlagWithInput("--vendor_ramdisk ", filesystem.OutputPath()) + flag := "--ramdisk " + if vendor { + flag = "--vendor_ramdisk " + } + cmd.FlagWithInput(flag, filesystem.OutputPath()) } else { ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name()) return output } - cmd.FlagWithOutput("--vendor_boot ", output) + flag := "--output " + if vendor { + flag = "--vendor_boot " + } + cmd.FlagWithOutput(flag, output) - builder.Build("build_vendor_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName())) + builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName())) return output } diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go new file mode 100644 index 000000000..e547203bc --- /dev/null +++ b/filesystem/logical_partition.go @@ -0,0 +1,210 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package filesystem + +import ( + "fmt" + "strconv" + + "github.com/google/blueprint/proptools" + + "android/soong/android" +) + +func init() { + android.RegisterModuleType("logical_partition", logicalPartitionFactory) +} + +type logicalPartition struct { + android.ModuleBase + + properties logicalPartitionProperties + + output android.OutputPath + installDir android.InstallPath +} + +type logicalPartitionProperties struct { + // Set the name of the output. Defaults to <module_name>.img. + Stem *string + + // Total size of the logical partition + Size *string + + // List of groups. A group defines a fixed sized region. It can host one or more logical + // partitions and their total size is limited by the size of the group they are in. + Groups []groupProperties + + // Whether the output is a sparse image or not. Default is false. + Sparse *bool +} + +type groupProperties struct { + // Name of the partition group + Name *string + + // Size of the partition group + Size *string + + // List of logical partitions in this group + Partitions []partitionProperties +} + +type partitionProperties struct { + // Name of the partition + Name *string + + // Filesystem that is placed on the partition + Filesystem *string `android:"path"` +} + +// logical_partition is a partition image which has one or more logical partitions in it. +func logicalPartitionFactory() android.Module { + module := &logicalPartition{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + +func (l *logicalPartition) DepsMutator(ctx android.BottomUpMutatorContext) { + // do nothing +} + +func (l *logicalPartition) installFileName() string { + return proptools.StringDefault(l.properties.Stem, l.BaseModuleName()+".img") +} + +func (l *logicalPartition) GenerateAndroidBuildActions(ctx android.ModuleContext) { + builder := android.NewRuleBuilder(pctx, ctx) + + // Sparse the filesystem images and calculate their sizes + sparseImages := make(map[string]android.OutputPath) + sparseImageSizes := make(map[string]android.OutputPath) + for _, group := range l.properties.Groups { + for _, part := range group.Partitions { + sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder) + pName := proptools.String(part.Name) + sparseImages[pName] = sparseImg + sparseImageSizes[pName] = sizeTxt + } + } + + cmd := builder.Command().BuiltTool("lpmake") + + size := proptools.String(l.properties.Size) + if size == "" { + ctx.PropertyErrorf("size", "must be set") + } + if _, err := strconv.Atoi(size); err != nil { + ctx.PropertyErrorf("size", "must be a number") + } + cmd.FlagWithArg("--device-size=", size) + + // TODO(jiyong): consider supporting A/B devices. Then we need to adjust num of slots. + cmd.FlagWithArg("--metadata-slots=", "2") + cmd.FlagWithArg("--metadata-size=", "65536") + + if proptools.Bool(l.properties.Sparse) { + cmd.Flag("--sparse") + } + + groupNames := make(map[string]bool) + partitionNames := make(map[string]bool) + + for _, group := range l.properties.Groups { + gName := proptools.String(group.Name) + if gName == "" { + ctx.PropertyErrorf("groups.name", "must be set") + } + if _, ok := groupNames[gName]; ok { + ctx.PropertyErrorf("group.name", "already exists") + } else { + groupNames[gName] = true + } + gSize := proptools.String(group.Size) + if gSize == "" { + ctx.PropertyErrorf("groups.size", "must be set") + } + if _, err := strconv.Atoi(gSize); err != nil { + ctx.PropertyErrorf("groups.size", "must be a number") + } + cmd.FlagWithArg("--group=", gName+":"+gSize) + + for _, part := range group.Partitions { + pName := proptools.String(part.Name) + if pName == "" { + ctx.PropertyErrorf("groups.partitions.name", "must be set") + } + if _, ok := partitionNames[pName]; ok { + ctx.PropertyErrorf("groups.partitions.name", "already exists") + } else { + partitionNames[pName] = true + } + // Get size of the partition by reading the -size.txt file + pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName]) + cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName)) + cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName]) + } + } + + l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath + cmd.FlagWithOutput("--output=", l.output) + + builder.Build("build_logical_partition", fmt.Sprintf("Creating %s", l.BaseModuleName())) + + l.installDir = android.PathForModuleInstall(ctx, "etc") + ctx.InstallFile(l.installDir, l.installFileName(), l.output) +} + +// Add a rule that converts the filesystem for the given partition to the given rule builder. The +// path to the sparse file and the text file having the size of the partition are returned. +func sparseFilesystem(ctx android.ModuleContext, p partitionProperties, builder *android.RuleBuilder) (sparseImg android.OutputPath, sizeTxt android.OutputPath) { + img := android.PathForModuleSrc(ctx, proptools.String(p.Filesystem)) + name := proptools.String(p.Name) + sparseImg = android.PathForModuleOut(ctx, name+".img").OutputPath + + builder.Temporary(sparseImg) + builder.Command().BuiltTool("img2simg").Input(img).Output(sparseImg) + + sizeTxt = android.PathForModuleOut(ctx, name+"-size.txt").OutputPath + builder.Temporary(sizeTxt) + builder.Command().BuiltTool("sparse_img").Flag("--get_partition_size").Input(sparseImg). + Text("| ").Text("tr").FlagWithArg("-d ", "'\n'"). + Text("> ").Output(sizeTxt) + + return sparseImg, sizeTxt +} + +var _ android.AndroidMkEntriesProvider = (*logicalPartition)(nil) + +// Implements android.AndroidMkEntriesProvider +func (l *logicalPartition) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(l.output), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName()) + }, + }, + }} +} + +var _ Filesystem = (*logicalPartition)(nil) + +func (l *logicalPartition) OutputPath() android.Path { + return l.output +} diff --git a/genrule/genrule.go b/genrule/genrule.go index 9fa6c484f..50c77cf9a 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -124,14 +124,12 @@ type generatorProperties struct { // input files to exclude Exclude_srcs []string `android:"path,arch_variant"` - - // Properties for Bazel migration purposes. - bazel.Properties } type Module struct { android.ModuleBase android.DefaultableModuleBase + android.BazelModuleBase android.ApexModuleBase // For other packages to make their own genrules with extra @@ -208,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 { @@ -519,7 +517,7 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) { g.outputFiles = outputFiles.Paths() - bazelModuleLabel := g.properties.Bazel_module.Label + bazelModuleLabel := g.GetBazelLabel() bazelActionsUsed := false if ctx.Config().BazelContext.BazelEnabled() && len(bazelModuleLabel) > 0 { bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel) @@ -771,6 +769,7 @@ func GenRuleFactory() android.Module { m := NewGenRule() android.InitAndroidModule(m) android.InitDefaultableModule(m) + android.InitBazelModule(m) return m } @@ -800,7 +799,7 @@ func BazelGenruleFactory() android.Module { func GenruleBp2Build(ctx android.TopDownMutatorContext) { m, ok := ctx.Module().(*Module) - if !ok || !m.properties.Bazel_module.Bp2build_available { + if !ok || !m.ConvertWithBp2build() { return } @@ -853,10 +852,12 @@ func GenruleBp2Build(ctx android.TopDownMutatorContext) { Tools: tools, } - props := bazel.NewBazelTargetModuleProperties(m.Name(), "genrule", "") + props := bazel.BazelTargetModuleProperties{ + Rule_class: "genrule", + } // Create the BazelTargetModule. - ctx.CreateBazelTargetModule(BazelGenruleFactory, props, attrs) + ctx.CreateBazelTargetModule(BazelGenruleFactory, m.Name(), props, attrs) } func (m *bazelGenrule) Name() string { diff --git a/java/Android.bp b/java/Android.bp index 9bfd009c6..461b16d58 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -58,7 +58,6 @@ bootstrap_go_package { "sdk_library.go", "sdk_library_external.go", "support_libraries.go", - "sysprop.go", "system_modules.go", "testing.go", "tradefed.go", diff --git a/java/aar.go b/java/aar.go index 602d2c49c..554ea670b 100644 --- a/java/aar.go +++ b/java/aar.go @@ -805,14 +805,6 @@ func (a *AARImport) HeaderJars() android.Paths { return android.Paths{a.classpathFile} } -func (a *AARImport) ImplementationJars() android.Paths { - return android.Paths{a.classpathFile} -} - -func (a *AARImport) ResourceJars() android.Paths { - return nil -} - func (a *AARImport) ImplementationAndResourcesJars() android.Paths { return android.Paths{a.classpathFile} } @@ -825,22 +817,10 @@ func (a *AARImport) DexJarInstallPath() android.Path { return nil } -func (a *AARImport) AidlIncludeDirs() android.Paths { - return nil -} - func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return nil } -func (d *AARImport) ExportedPlugins() (android.Paths, []string, bool) { - return nil, nil, false -} - -func (a *AARImport) SrcJarArgs() ([]string, android.Paths) { - return nil, nil -} - var _ android.ApexModule = (*AARImport)(nil) // Implements android.ApexModule diff --git a/java/android_manifest.go b/java/android_manifest.go index c76bb2fda..b30f3d2c8 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -91,7 +91,7 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext if err != nil { ctx.ModuleErrorf("invalid targetSdkVersion: %s", err) } - if UseApiFingerprint(ctx) { + if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) deps = append(deps, ApiFingerprintPath(ctx)) } @@ -100,7 +100,7 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext if err != nil { ctx.ModuleErrorf("invalid minSdkVersion: %s", err) } - if UseApiFingerprint(ctx) { + if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) deps = append(deps, ApiFingerprintPath(ctx)) } diff --git a/java/androidmk.go b/java/androidmk.go index e7261f894..3d3eae530 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -220,7 +220,7 @@ func (prebuilt *DexImport) AndroidMkEntries() []android.AndroidMkEntries { } return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "JAVA_LIBRARIES", - OutputFile: android.OptionalPathForPath(prebuilt.maybeStrippedDexJarFile), + OutputFile: android.OptionalPathForPath(prebuilt.dexJarFile), Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk", ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { @@ -287,9 +287,16 @@ func (binary *Binary) AndroidMkEntries() []android.AndroidMkEntries { }, }} } else { + outputFile := binary.wrapperFile + // Have Make installation trigger Soong installation by using Soong's install path as + // the output file. + if binary.Host() { + outputFile = binary.binaryFile + } + return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "EXECUTABLES", - OutputFile: android.OptionalPathForPath(binary.wrapperFile), + OutputFile: android.OptionalPathForPath(outputFile), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetBool("LOCAL_STRIP_MODULE", false) diff --git a/java/app.go b/java/app.go index 8287533e0..0660aa62e 100755 --- a/java/app.go +++ b/java/app.go @@ -469,7 +469,7 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { a.Module.compile(ctx, a.aaptSrcJar) } - return a.maybeStrippedDexJarFile + return a.dexJarFile } func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath { @@ -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}) @@ -1262,6 +1280,14 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml") statusFile := dexpreopt.UsesLibrariesStatusFile(ctx) + // Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the + // check is not necessary, and although it is good to have, it is difficult to maintain on + // non-linux build platforms where dexpreopt is generally disabled (the check may fail due to + // various unrelated reasons, such as a failure to get manifest from an APK). + if dexpreopt.GetGlobalConfig(ctx).DisablePreopt { + return manifest + } + rule := android.NewRuleBuilder(pctx, ctx) cmd := rule.Command().BuiltTool("manifest_check"). Flag("--enforce-uses-libraries"). @@ -1292,6 +1318,14 @@ func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk andr outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base()) statusFile := dexpreopt.UsesLibrariesStatusFile(ctx) + // Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the + // check is not necessary, and although it is good to have, it is difficult to maintain on + // non-linux build platforms where dexpreopt is generally disabled (the check may fail due to + // various unrelated reasons, such as a failure to get manifest from an APK). + if dexpreopt.GetGlobalConfig(ctx).DisablePreopt { + return apk + } + rule := android.NewRuleBuilder(pctx, ctx) aapt := ctx.Config().HostToolPath(ctx, "aapt") rule.Command(). diff --git a/java/app_import.go b/java/app_import.go index 59eb10a9b..d38f63e06 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -67,6 +67,9 @@ type AndroidAppImportProperties struct { // module name in the form ":module". Should be empty if presigned or default_dev_cert is set. Certificate *string + // Names of extra android_app_certificate modules to sign the apk with in the form ":module". + Additional_certificates []string + // Set this flag to true if the prebuilt apk is already signed. The certificate property must not // be set for presigned modules. Presigned *bool @@ -156,6 +159,16 @@ func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.AddDependency(ctx.Module(), certificateTag, cert) } + for _, cert := range a.properties.Additional_certificates { + cert = android.SrcIsModule(cert) + if cert != "" { + ctx.AddDependency(ctx.Module(), certificateTag, cert) + } else { + ctx.PropertyErrorf("additional_certificates", + `must be names of android_app_certificate modules in the form ":module"`) + } + } + a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes()) } @@ -244,10 +257,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 +285,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") @@ -303,9 +316,6 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext // If the certificate property is empty at this point, default_dev_cert must be set to true. // Which makes processMainCert's behavior for the empty cert string WAI. certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx) - if len(certificates) != 1 { - ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates) - } a.certificate = certificates[0] signed := android.PathForModuleOut(ctx, "signed", apkFilename) var lineageFile android.Path diff --git a/java/app_import_test.go b/java/app_import_test.go index dc31d07c9..00406aa2e 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -109,16 +109,30 @@ func TestAndroidAppImport_SigningLineage(t *testing.T) { name: "foo", apk: "prebuilts/apk/app.apk", certificate: "platform", + additional_certificates: [":additional_certificate"], lineage: "lineage.bin", } + + android_app_certificate { + name: "additional_certificate", + certificate: "cert/additional_cert", + } `) variant := ctx.ModuleForTests("foo", "android_common") - // Check cert signing lineage flag. signedApk := variant.Output("signed/foo.apk") + // Check certificates + certificatesFlag := signedApk.Args["certificates"] + expected := "build/make/target/product/security/platform.x509.pem " + + "build/make/target/product/security/platform.pk8 " + + "cert/additional_cert.x509.pem cert/additional_cert.pk8" + if expected != certificatesFlag { + t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag) + } + // Check cert signing lineage flag. signingFlag := signedApk.Args["flags"] - expected := "--lineage lineage.bin" + expected = "--lineage lineage.bin" if expected != signingFlag { t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) } diff --git a/java/app_test.go b/java/app_test.go index b1abe3db3..f41047aa6 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -685,6 +685,51 @@ func TestLibraryAssets(t *testing.T) { } } +func TestAppJavaResources(t *testing.T) { + bp := ` + android_app { + name: "foo", + sdk_version: "current", + java_resources: ["resources/a"], + srcs: ["a.java"], + } + + android_app { + name: "bar", + sdk_version: "current", + java_resources: ["resources/a"], + } + ` + + ctx := testApp(t, bp) + + foo := ctx.ModuleForTests("foo", "android_common") + fooResources := foo.Output("res/foo.jar") + fooDexJar := foo.Output("dex-withres/foo.jar") + fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar") + fooApk := foo.Rule("combineApk") + + if g, w := fooDexJar.Inputs.Strings(), fooResources.Output.String(); !android.InList(w, g) { + t.Errorf("expected resource jar %q in foo dex jar inputs %q", w, g) + } + + if g, w := fooDexJarAligned.Input.String(), fooDexJar.Output.String(); g != w { + t.Errorf("expected dex jar %q in foo aligned dex jar inputs %q", w, g) + } + + if g, w := fooApk.Inputs.Strings(), fooDexJarAligned.Output.String(); !android.InList(w, g) { + t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g) + } + + bar := ctx.ModuleForTests("bar", "android_common") + barResources := bar.Output("res/bar.jar") + barApk := bar.Rule("combineApk") + + if g, w := barApk.Inputs.Strings(), barResources.Output.String(); !android.InList(w, g) { + t.Errorf("expected resources jar %q in bar apk inputs %q", w, g) + } +} + func TestAndroidResources(t *testing.T) { testCases := []struct { name string @@ -2245,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", @@ -2267,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", @@ -2286,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) { @@ -2348,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/builder.go b/java/builder.go index 22a891ae1..33206ceeb 100644 --- a/java/builder.go +++ b/java/builder.go @@ -80,6 +80,8 @@ var ( func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() }) _ = pctx.VariableFunc("kytheCuEncoding", func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() }) + _ = pctx.VariableFunc("kytheCuJavaSourceMax", + func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuJavaSourceMax() }) _ = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json") // Run it with -add-opens=java.base/java.nio=ALL-UNNAMED to avoid JDK9's warning about // "Illegal reflective access by com.google.protobuf.Utf8$UnsafeProcessor ... @@ -93,6 +95,7 @@ var ( `KYTHE_CORPUS=${kytheCorpus} ` + `KYTHE_VNAMES=${kytheVnames} ` + `KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` + + `KYTHE_JAVA_SOURCE_BATCH_SIZE=${kytheCuJavaSourceMax} ` + `${config.SoongJavacWrapper} ${config.JavaCmd} ` + `--add-opens=java.base/java.nio=ALL-UNNAMED ` + `-jar ${config.JavaKytheExtractorJar} ` + diff --git a/java/device_host_converter.go b/java/device_host_converter.go index ee7d01820..39fb04a8e 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -146,14 +146,6 @@ func (d *DeviceHostConverter) HeaderJars() android.Paths { return d.headerJars } -func (d *DeviceHostConverter) ImplementationJars() android.Paths { - return d.implementationJars -} - -func (d *DeviceHostConverter) ResourceJars() android.Paths { - return d.resourceJars -} - func (d *DeviceHostConverter) ImplementationAndResourcesJars() android.Paths { return d.implementationAndResourceJars } @@ -174,14 +166,6 @@ func (d *DeviceHostConverter) ClassLoaderContexts() dexpreopt.ClassLoaderContext return nil } -func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string, bool) { - return nil, nil, false -} - -func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) { - return d.srcJarArgs, d.srcJarDeps -} - func (d *DeviceHostConverter) JacocoReportClassesFile() android.Path { return nil } diff --git a/java/dex.go b/java/dex.go index e52fdb5d9..b2a998f78 100644 --- a/java/dex.go +++ b/java/dex.go @@ -260,6 +260,9 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl r8Flags = append(r8Flags, "--debug") } + // TODO(b/180878971): missing classes should be added to the relevant builds. + r8Flags = append(r8Flags, "-ignorewarnings") + return r8Flags, r8Deps } 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/java/droiddoc.go b/java/droiddoc.go index 8f1644c7f..f0decec74 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -1203,8 +1203,14 @@ func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *a } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths, - srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicitsRsp android.WritablePath, sandbox bool) *android.RuleBuilderCommand { + srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, + implicitsRsp, homeDir android.WritablePath, sandbox bool) *android.RuleBuilderCommand { + rule.Command().Text("rm -rf").Flag(homeDir.String()) + rule.Command().Text("mkdir -p").Flag(homeDir.String()) + cmd := rule.Command() + cmd.FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()) + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") { rule.Remoteable(android.RemoteRuleSupports{RBE: true}) pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava") @@ -1214,17 +1220,21 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersi execStrategy = remoteexec.LocalExecStrategy labels["shallow"] = "true" } - inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()} + inputs := []string{ + ctx.Config().HostJavaToolPath(ctx, "metalava").String(), + homeDir.String(), + } if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" { inputs = append(inputs, strings.Split(v, ",")...) } cmd.Text((&remoteexec.REParams{ - Labels: labels, - ExecStrategy: execStrategy, - Inputs: inputs, - RSPFile: implicitsRsp.String(), - ToolchainInputs: []string{config.JavaCmd(ctx).String()}, - Platform: map[string]string{remoteexec.PoolKey: pool}, + Labels: labels, + ExecStrategy: execStrategy, + Inputs: inputs, + RSPFile: implicitsRsp.String(), + ToolchainInputs: []string{config.JavaCmd(ctx).String()}, + Platform: map[string]string{remoteexec.PoolKey: pool}, + EnvironmentVariables: []string{"ANDROID_SDK_HOME"}, }).NoVarTemplate(ctx.Config())) } @@ -1302,9 +1312,9 @@ func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) { srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars) implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp") - + homeDir := android.PathForModuleOut(ctx, "metalava-home") cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList, - deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp, + deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp, homeDir, Bool(d.Javadoc.properties.Sandbox)) cmd.Implicits(d.Javadoc.implicits) diff --git a/java/hiddenapi.go b/java/hiddenapi.go index f8e41c458..208ced769 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -15,8 +15,6 @@ package java import ( - "strings" - "github.com/google/blueprint" "android/soong/android" @@ -29,8 +27,8 @@ var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", bl type hiddenAPI struct { // The name of the module as it would be used in the boot jars configuration, e.g. without any - // prebuilt_ prefix (if it is a prebuilt), without any "-hiddenapi" suffix if it just provides - // annotations and without any ".impl" suffix if it is a java_sdk_library implementation library. + // prebuilt_ prefix (if it is a prebuilt) and without any ".impl" suffix if it is a + // java_sdk_library implementation library. configurationName string // True if the module containing this structure contributes to the hiddenapi information or has @@ -49,11 +47,6 @@ type hiddenAPI struct { // annotation information. primary bool - // True if the module only contains additional annotations and so does not require hiddenapi - // information to be encoded in its dex file and should not be used to generate the - // hiddenAPISingletonPathsStruct.stubFlags file. - annotationsOnly bool - // The path to the dex jar that is in the boot class path. If this is nil then the associated // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar @@ -119,55 +112,67 @@ type hiddenAPIIntf interface { var _ hiddenAPIIntf = (*hiddenAPI)(nil) // Initialize the hiddenapi structure -func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, name string) { +func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) { // If hiddenapi processing is disabled treat this as inactive. if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { return } - // Modules whose names are of the format <x>-hiddenapi provide hiddenapi information for the boot - // jar module <x>. Otherwise, the module provides information for itself. Either way extract the - // configurationName of the boot jar module. - configurationName := strings.TrimSuffix(name, "-hiddenapi") h.configurationName = configurationName // It is important that hiddenapi information is only gathered for/from modules that are actually // on the boot jars list because the runtime only enforces access to the hidden API for the // bootclassloader. If information is gathered for modules not on the list then that will cause // failures in the CtsHiddenApiBlocklist... tests. - h.active = inList(configurationName, ctx.Config().BootJars()) + module := ctx.Module() + h.active = isModuleInBootClassPath(ctx, module) if !h.active { // The rest of the properties will be ignored if active is false. return } - // If this module has a suffix of -hiddenapi then it only provides additional annotation - // information for a module on the boot jars list. - h.annotationsOnly = strings.HasSuffix(name, "-hiddenapi") - // Determine whether this module is the primary module or not. primary := true // A prebuilt module is only primary if it is preferred and conversely a source module is only // primary if it has not been replaced by a prebuilt module. - module := ctx.Module() if pi, ok := module.(android.PrebuiltInterface); ok { if p := pi.Prebuilt(); p != nil { primary = p.UsePrebuilt() } } else { - // The only module that will pass a different name to its module name to this method is the - // implementation library of a java_sdk_library. It has a configuration name of <x> the same - // as its parent java_sdk_library but a module name of <x>.impl. It is not the primary module, - // the java_sdk_library with the name of <x> is. - primary = name == ctx.ModuleName() + // The only module that will pass a different configurationName to its module name to this + // method is the implementation library of a java_sdk_library. It has a configuration name of + // <x> the same as its parent java_sdk_library but a module name of <x>.impl. It is not the + // primary module, the java_sdk_library with the name of <x> is. + primary = configurationName == ctx.ModuleName() // A source module that has been replaced by a prebuilt can never be the primary module. - primary = primary && !module.IsReplacedByPrebuilt() + if module.IsReplacedByPrebuilt() { + ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) { + if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil { + primary = false + } else { + ctx.ModuleErrorf( + "hiddenapi has determined that the source module %q should be ignored as it has been"+ + " replaced by the prebuilt module %q but unfortunately it does not provide a"+ + " suitable boot dex jar", ctx.ModuleName(), ctx.OtherModuleName(prebuilt)) + } + }) + } } h.primary = primary } +func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool { + // Get the configured non-updatable and updatable boot jars. + nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars() + updatableBootJars := ctx.Config().UpdatableBootJars() + active := isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) || + isModuleInConfiguredList(ctx, module, updatableBootJars) + return active +} + // hiddenAPIExtractAndEncode is called by any module that could contribute to the hiddenapi // processing. // @@ -191,15 +196,13 @@ func (h *hiddenAPI) hiddenAPIExtractAndEncode(ctx android.ModuleContext, dexJar h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar) - if !h.annotationsOnly { - hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath + hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath - // Create a copy of the dex jar which has been encoded with hiddenapi flags. - hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex) + // Create a copy of the dex jar which has been encoded with hiddenapi flags. + hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex) - // Use the encoded dex jar from here onwards. - dexJar = hiddenAPIJar - } + // Use the encoded dex jar from here onwards. + dexJar = hiddenAPIJar return dexJar } @@ -262,6 +265,7 @@ func (h *hiddenAPI) hiddenAPIExtractInformation(ctx android.ModuleContext, dexJa rule.Command(). BuiltTool("merge_csv"). Flag("--zip_input"). + Flag("--key_field signature"). FlagWithOutput("--output=", indexCSV). Inputs(classesJars) rule.Build("merged-hiddenapi-index", "Merged Hidden API index") diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index 6341a3406..82e8b3f75 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -217,10 +217,6 @@ func stubFlagsRule(ctx android.SingletonContext) { var bootDexJars android.Paths - // Get the configured non-updatable and updatable boot jars. - nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars() - updatableBootJars := ctx.Config().UpdatableBootJars() - ctx.VisitAllModules(func(module android.Module) { // Collect dex jar paths for the modules listed above. if j, ok := module.(UsesLibraryDependency); ok { @@ -235,11 +231,6 @@ func stubFlagsRule(ctx android.SingletonContext) { // Collect dex jar paths for modules that had hiddenapi encode called on them. if h, ok := module.(hiddenAPIIntf); ok { if jar := h.bootDexJar(); jar != nil { - if !isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) && - !isModuleInConfiguredList(ctx, module, updatableBootJars) { - return - } - bootDexJars = append(bootDexJars, jar) } } @@ -291,8 +282,8 @@ func stubFlagsRule(ctx android.SingletonContext) { // there too. // // TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it. -func isModuleInConfiguredList(ctx android.SingletonContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool { - name := ctx.ModuleName(module) +func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool { + name := ctx.OtherModuleName(module) // Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed. name = android.RemoveOptionalPrebuiltPrefix(name) @@ -305,11 +296,11 @@ func isModuleInConfiguredList(ctx android.SingletonContext, module android.Modul // It is an error if the module is not an ApexModule. if _, ok := module.(android.ApexModule); !ok { - ctx.Errorf("module %q configured in boot jars does not support being added to an apex", module) + ctx.ModuleErrorf("is configured in boot jars but does not support being added to an apex") return false } - apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) // Now match the apex part of the boot image configuration. requiredApex := configuredBootJars.Apex(index) @@ -433,6 +424,7 @@ func metadataRule(ctx android.SingletonContext) android.Path { rule.Command(). BuiltTool("merge_csv"). + Flag("--key_field signature"). FlagWithOutput("--output=", outputPath). Inputs(metadataCSV) @@ -544,6 +536,7 @@ func (h *hiddenAPIIndexSingleton) GenerateBuildActions(ctx android.SingletonCont rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("merge_csv"). + Flag("--key_field signature"). FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index). Inputs(indexes) diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index 4670d0311..fb63820a8 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -89,12 +89,6 @@ func TestHiddenAPIIndexSingleton(t *testing.T) { } java_library { - name: "foo-hiddenapi", - srcs: ["a.java"], - compile_dex: true, - } - - java_library { name: "foo-hiddenapi-annotations", srcs: ["a.java"], compile_dex: true, @@ -118,7 +112,6 @@ func TestHiddenAPIIndexSingleton(t *testing.T) { indexRule := hiddenAPIIndex.Rule("singleton-merged-hiddenapi-index") CheckHiddenAPIRuleInputs(t, ` .intermediates/bar/android_common/hiddenapi/index.csv -.intermediates/foo-hiddenapi/android_common/hiddenapi/index.csv .intermediates/foo/android_common/hiddenapi/index.csv `, indexRule) @@ -133,6 +126,29 @@ func TestHiddenAPIIndexSingleton(t *testing.T) { `, indexParams) } +func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) { + config := testConfigWithBootJars(` + java_library { + name: "foo", + srcs: ["a.java"], + compile_dex: true, + } + + java_import { + name: "foo", + jars: ["a.jar"], + prefer: true, + } + `, []string{"platform:foo"}, nil) + + ctx := testContextWithHiddenAPI(config) + + runWithErrors(t, ctx, config, + "hiddenapi has determined that the source module \"foo\" should be ignored as it has been"+ + " replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a"+ + " suitable boot dex jar") +} + func TestHiddenAPISingletonWithPrebuilt(t *testing.T) { ctx, _ := testHiddenAPIBootJars(t, ` java_import { diff --git a/java/java.go b/java/java.go index dbfad029b..9e3583501 100644 --- a/java/java.go +++ b/java/java.go @@ -370,6 +370,10 @@ type CompilerDeviceProperties struct { // If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file. // Defaults to false. V4_signature *bool + + // Only for libraries created by a sysprop_library module, SyspropPublicStub is the name of the + // public stubs library. + SyspropPublicStub string `blueprint:"mutated"` } // Functionality common to Module and Import @@ -434,9 +438,6 @@ type Module struct { // output file containing classes.dex and resources dexJarFile android.Path - // output file that contains classes.dex if it should be in the output file - maybeStrippedDexJarFile android.Path - // output file containing uninstrumented classes that will be instrumented by jacoco jacocoReportClassesFile android.Path @@ -583,6 +584,16 @@ type JavaInfo struct { var JavaInfoProvider = blueprint.NewProvider(JavaInfo{}) +// SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to +// the sysprop implementation library. +type SyspropPublicStubInfo struct { + // JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to + // the sysprop implementation library. + JavaInfo JavaInfo +} + +var SyspropPublicStubInfoProvider = blueprint.NewProvider(SyspropPublicStubInfo{}) + // Methods that need to be implemented for a module that is added to apex java_libs property. type ApexDependency interface { HeaderJars() android.Paths @@ -652,29 +663,30 @@ func IsJniDepTag(depTag blueprint.DependencyTag) bool { } var ( - dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} - staticLibTag = dependencyTag{name: "staticlib"} - libTag = dependencyTag{name: "javalib"} - java9LibTag = dependencyTag{name: "java9lib"} - pluginTag = dependencyTag{name: "plugin"} - errorpronePluginTag = dependencyTag{name: "errorprone-plugin"} - exportedPluginTag = dependencyTag{name: "exported-plugin"} - bootClasspathTag = dependencyTag{name: "bootclasspath"} - systemModulesTag = dependencyTag{name: "system modules"} - frameworkResTag = dependencyTag{name: "framework-res"} - kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} - kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} - proguardRaiseTag = dependencyTag{name: "proguard-raise"} - certificateTag = dependencyTag{name: "certificate"} - instrumentationForTag = dependencyTag{name: "instrumentation_for"} - extraLintCheckTag = dependencyTag{name: "extra-lint-check"} - jniLibTag = dependencyTag{name: "jnilib"} - jniInstallTag = installDependencyTag{name: "jni install"} - binaryInstallTag = installDependencyTag{name: "binary install"} - usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion) - usesLibCompat28Tag = makeUsesLibraryDependencyTag(28) - usesLibCompat29Tag = makeUsesLibraryDependencyTag(29) - usesLibCompat30Tag = makeUsesLibraryDependencyTag(30) + dataNativeBinsTag = dependencyTag{name: "dataNativeBins"} + staticLibTag = dependencyTag{name: "staticlib"} + libTag = dependencyTag{name: "javalib"} + java9LibTag = dependencyTag{name: "java9lib"} + pluginTag = dependencyTag{name: "plugin"} + errorpronePluginTag = dependencyTag{name: "errorprone-plugin"} + exportedPluginTag = dependencyTag{name: "exported-plugin"} + bootClasspathTag = dependencyTag{name: "bootclasspath"} + systemModulesTag = dependencyTag{name: "system modules"} + frameworkResTag = dependencyTag{name: "framework-res"} + kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} + kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} + proguardRaiseTag = dependencyTag{name: "proguard-raise"} + certificateTag = dependencyTag{name: "certificate"} + instrumentationForTag = dependencyTag{name: "instrumentation_for"} + extraLintCheckTag = dependencyTag{name: "extra-lint-check"} + jniLibTag = dependencyTag{name: "jnilib"} + syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} + jniInstallTag = installDependencyTag{name: "jni install"} + binaryInstallTag = installDependencyTag{name: "binary install"} + usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion) + usesLibCompat28Tag = makeUsesLibraryDependencyTag(28) + usesLibCompat29Tag = makeUsesLibraryDependencyTag(29) + usesLibCompat30Tag = makeUsesLibraryDependencyTag(30) ) func IsLibDepTag(depTag blueprint.DependencyTag) bool { @@ -813,35 +825,17 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { j.linter.deps(ctx) sdkDeps(ctx, sdkContext(j), j.dexer) - } - - syspropPublicStubs := syspropPublicStubs(ctx.Config()) - - // rewriteSyspropLibs validates if a java module can link against platform's sysprop_library, - // and redirects dependency to public stub depending on the link type. - rewriteSyspropLibs := func(libs []string, prop string) []string { - // make a copy - ret := android.CopyOf(libs) - - for idx, lib := range libs { - stub, ok := syspropPublicStubs[lib] - if !ok { - continue - } - - linkType, _ := j.getLinkType(ctx.ModuleName()) - // only platform modules can use internal props - if linkType != javaPlatform { - ret[idx] = stub - } + if j.deviceProperties.SyspropPublicStub != "" { + // This is a sysprop implementation library that has a corresponding sysprop public + // stubs library, and a dependency on it so that dependencies on the implementation can + // be forwarded to the public stubs library when necessary. + ctx.AddVariationDependencies(nil, syspropPublicStubDepTag, j.deviceProperties.SyspropPublicStub) } - - return ret } - libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...) - ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...) + libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...) + ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...) // Add dependency on libraries that provide additional hidden api annotations. ctx.AddVariationDependencies(nil, hiddenApiAnnotationsTag, j.properties.Hiddenapi_additional_annotations...) @@ -856,15 +850,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { // if true, enable enforcement // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST // exception list of java_library names to allow inter-partition dependency - for idx, lib := range j.properties.Libs { + for idx := range j.properties.Libs { if libDeps[idx] == nil { continue } - if _, ok := syspropPublicStubs[lib]; ok { - continue - } - if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok { // java_sdk_library is always allowed at inter-partition dependency. // So, skip check. @@ -1038,7 +1028,7 @@ func (lt linkType) String() string { case javaPlatform: return "private API" default: - panic(fmt.Errorf("unrecognized linktype: %v", lt)) + panic(fmt.Errorf("unrecognized linktype: %d", lt)) } } @@ -1134,6 +1124,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } } + linkType, _ := j.getLinkType(ctx.ModuleName()) + ctx.VisitDirectDeps(func(module android.Module) { otherName := ctx.OtherModuleName(module) tag := ctx.OtherModuleDependencyTag(module) @@ -1156,6 +1148,14 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { } } else if ctx.OtherModuleHasProvider(module, JavaInfoProvider) { dep := ctx.OtherModuleProvider(module, JavaInfoProvider).(JavaInfo) + if linkType != javaPlatform && + ctx.OtherModuleHasProvider(module, SyspropPublicStubInfoProvider) { + // dep is a sysprop implementation library, but this module is not linking against + // the platform, so it gets the sysprop public stubs library instead. Replace + // dep with the JavaInfo from the SyspropPublicStubInfoProvider. + syspropDep := ctx.OtherModuleProvider(module, SyspropPublicStubInfoProvider).(SyspropPublicStubInfo) + dep = syspropDep.JavaInfo + } switch tag { case bootClasspathTag: deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars...) @@ -1214,6 +1214,12 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...) case kotlinAnnotationsTag: deps.kotlinAnnotations = dep.HeaderJars + case syspropPublicStubDepTag: + // This is a sysprop implementation library, forward the JavaInfoProvider from + // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. + ctx.SetProvider(SyspropPublicStubInfoProvider, SyspropPublicStubInfo{ + JavaInfo: dep, + }) } } else if dep, ok := module.(android.SourceFileProducer); ok { switch tag { @@ -1818,46 +1824,50 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } - if ctx.Device() && j.hasCode(ctx) && - (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) { - if j.shouldInstrumentStatic(ctx) { - j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles, - android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) - } - // Dex compilation - var dexOutputFile android.OutputPath - dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName) - if ctx.Failed() { - return - } + if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) { + if j.hasCode(ctx) { + if j.shouldInstrumentStatic(ctx) { + j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles, + android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) + } + // Dex compilation + var dexOutputFile android.OutputPath + dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName) + if ctx.Failed() { + return + } - // Hidden API CSV generation and dex encoding - dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile, - proptools.Bool(j.dexProperties.Uncompress_dex)) + // Hidden API CSV generation and dex encoding + dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile, + proptools.Bool(j.dexProperties.Uncompress_dex)) - // merge dex jar with resources if necessary - if j.resourceJar != nil { - jars := android.Paths{dexOutputFile, j.resourceJar} - combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath - TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{}, - false, nil, nil) - if *j.dexProperties.Uncompress_dex { - combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath - TransformZipAlign(ctx, combinedAlignedJar, combinedJar) - dexOutputFile = combinedAlignedJar - } else { - dexOutputFile = combinedJar + // merge dex jar with resources if necessary + if j.resourceJar != nil { + jars := android.Paths{dexOutputFile, j.resourceJar} + combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath + TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{}, + false, nil, nil) + if *j.dexProperties.Uncompress_dex { + combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath + TransformZipAlign(ctx, combinedAlignedJar, combinedJar) + dexOutputFile = combinedAlignedJar + } else { + dexOutputFile = combinedJar + } } - } - - j.dexJarFile = dexOutputFile - // Dexpreopting - j.dexpreopt(ctx, dexOutputFile) + j.dexJarFile = dexOutputFile - j.maybeStrippedDexJarFile = dexOutputFile + // Dexpreopting + j.dexpreopt(ctx, dexOutputFile) - outputFile = dexOutputFile + outputFile = dexOutputFile + } else { + // There is no code to compile into a dex jar, make sure the resources are propagated + // to the APK if this is an app. + outputFile = implementationAndResourcesJar + j.dexJarFile = j.resourceJar + } if ctx.Failed() { return @@ -2036,13 +2046,6 @@ func (j *Module) DexJarInstallPath() android.Path { return j.installFile } -func (j *Module) ResourceJars() android.Paths { - if j.resourceJar == nil { - return nil - } - return android.Paths{j.resourceJar} -} - func (j *Module) ImplementationAndResourcesJars() android.Paths { if j.implementationAndResourcesJar == nil { return nil @@ -2059,17 +2062,6 @@ func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return j.classLoaderContexts } -// ExportedPlugins returns the list of jars needed to run the exported plugins, the list of -// classes for the plugins, and a boolean for whether turbine needs to be disabled due to plugins -// that generate APIs. -func (j *Module) ExportedPlugins() (android.Paths, []string, bool) { - return j.exportedPluginJars, j.exportedPluginClasses, j.exportedDisableTurbine -} - -func (j *Module) SrcJarArgs() ([]string, android.Paths) { - return j.srcJarArgs, j.srcJarDeps -} - var _ logtagsProducer = (*Module)(nil) func (j *Module) logtags() android.Paths { @@ -2505,6 +2497,11 @@ func (j *TestHost) DepsMutator(ctx android.BottomUpMutatorContext) { } func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if j.testProperties.Test_options.Unit_test == nil && ctx.Host() { + // TODO(b/): Clean temporary heuristic to avoid unexpected onboarding. + defaultUnitTest := !inList("tradefed", j.properties.Static_libs) && !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites) && !inList("robolectric-host-android_all", j.properties.Static_libs) && !inList("robolectric-host-android_all", j.properties.Libs) + j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest) + } j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test) @@ -2665,6 +2662,7 @@ func TestHostFactory() android.Module { module.Module.properties.Installable = proptools.BoolPtr(true) InitJavaModuleMultiTargets(module, android.HostSupported) + return module } @@ -3036,17 +3034,6 @@ func (j *Import) HeaderJars() android.Paths { return android.Paths{j.combinedClasspathFile} } -func (j *Import) ImplementationJars() android.Paths { - if j.combinedClasspathFile == nil { - return nil - } - return android.Paths{j.combinedClasspathFile} -} - -func (j *Import) ResourceJars() android.Paths { - return nil -} - func (j *Import) ImplementationAndResourcesJars() android.Paths { if j.combinedClasspathFile == nil { return nil @@ -3062,22 +3049,10 @@ func (j *Import) DexJarInstallPath() android.Path { return nil } -func (j *Import) AidlIncludeDirs() android.Paths { - return j.exportAidlIncludeDirs -} - func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return j.classLoaderContexts } -func (j *Import) ExportedPlugins() (android.Paths, []string, bool) { - return nil, nil, false -} - -func (j *Import) SrcJarArgs() ([]string, android.Paths) { - return nil, nil -} - var _ android.ApexModule = (*Import)(nil) // Implements android.ApexModule @@ -3183,8 +3158,7 @@ type DexImport struct { properties DexImportProperties - dexJarFile android.Path - maybeStrippedDexJarFile android.Path + dexJarFile android.Path dexpreopter @@ -3271,8 +3245,6 @@ func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.dexpreopt(ctx, dexOutputFile) - j.maybeStrippedDexJarFile = dexOutputFile - if apexInfo.IsForPlatform() { ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"), j.Stem()+".jar", dexOutputFile) diff --git a/java/java_test.go b/java/java_test.go index 11f6a7c21..911265532 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "android/soong/genrule" "github.com/google/blueprint/proptools" "android/soong/android" @@ -74,11 +75,13 @@ func testContext(config android.Config) *android.TestContext { ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) - ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory)) - ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory)) + ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory) + ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory) android.RegisterPrebuiltMutators(ctx) + genrule.RegisterGenruleBuildComponents(ctx) + // Register module types and mutators from cc needed for JNI testing cc.RegisterRequiredBuildComponentsForTest(ctx) @@ -114,20 +117,26 @@ func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config pathCtx := android.PathContextForTesting(config) dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx)) + runWithErrors(t, ctx, config, pattern) + + return ctx, config +} + +func runWithErrors(t *testing.T, ctx *android.TestContext, config android.Config, pattern string) { ctx.Register() _, errs := ctx.ParseBlueprintsFiles("Android.bp") if len(errs) > 0 { android.FailIfNoMatchingErrors(t, pattern, errs) - return ctx, config + return } _, errs = ctx.PrepareBuildActions(config) if len(errs) > 0 { android.FailIfNoMatchingErrors(t, pattern, errs) - return ctx, config + return } t.Fatalf("missing expected error %q (0 errors are returned)", pattern) - return ctx, config + return } func testJavaWithFS(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) { diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index d6ff47974..b5668a1c2 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -22,6 +22,7 @@ import ( var legacyCorePlatformApiModules = []string{ "AAECarSystemUI", "AAECarSystemUI-tests", + "ArcSettings", "ahat-test-dump", "android.car", "android.test.mock", @@ -77,6 +78,7 @@ var legacyCorePlatformApiModules = []string{ "DownloadProvider", "DownloadProviderTests", "DownloadProviderUi", + "ds-car-docs", // for AAOS API documentation only "DynamicSystemInstallationService", "EmergencyInfo-lib", "ethernet-service", @@ -141,7 +143,7 @@ var legacyCorePlatformApiModules = []string{ "saminterfacelibrary", "sammanagerlibrary", "service-blobstore", - "service-connectivity", + "service-connectivity-pre-jarjar", "service-jobscheduler", "services", "services.accessibility", @@ -152,6 +154,7 @@ var legacyCorePlatformApiModules = []string{ "services.usage", "services.usb", "Settings-core", + "SettingsGoogle", "SettingsGoogleOverlayCoral", "SettingsGoogleOverlayFlame", "SettingsLib", diff --git a/java/lint.go b/java/lint.go index 827259573..f9a89d0f7 100644 --- a/java/lint.go +++ b/java/lint.go @@ -22,6 +22,8 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/java/config" + "android/soong/remoteexec" ) type LintProperties struct { @@ -172,8 +174,38 @@ func (l *linter) deps(ctx android.BottomUpMutatorContext) { extraLintCheckTag, extraCheckModules...) } -func (l *linter) writeLintProjectXML(ctx android.ModuleContext, - rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) { +type lintPaths struct { + projectXML android.WritablePath + configXML android.WritablePath + cacheDir android.WritablePath + homeDir android.WritablePath + srcjarDir android.WritablePath + + deps android.Paths + + remoteInputs android.Paths + remoteRSPInputs android.Paths +} + +func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths { + var deps android.Paths + var remoteInputs android.Paths + var remoteRSPInputs android.Paths + + // Paths passed to trackInputDependency will be added as dependencies of the rule that runs + // lint and passed as inputs to the remote execution proxy. + trackInputDependency := func(paths ...android.Path) { + deps = append(deps, paths...) + remoteInputs = append(remoteInputs, paths...) + } + + // Paths passed to trackRSPDependency will be added as dependencies of the rule that runs + // lint, but the RSP file will be used by the remote execution proxy to find the files so that + // it doesn't overflow command line limits. + trackRSPDependency := func(paths android.Paths, rsp android.Path) { + deps = append(deps, paths...) + remoteRSPInputs = append(remoteRSPInputs, rsp) + } var resourcesList android.WritablePath if len(l.resources) > 0 { @@ -184,17 +216,27 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, resListRule := android.NewRuleBuilder(pctx, ctx) resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList) resListRule.Build("lint_resources_list", "lint resources list") - deps = append(deps, l.resources...) + trackRSPDependency(l.resources, resourcesList) } - projectXMLPath = android.PathForModuleOut(ctx, "lint", "project.xml") + projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml") // Lint looks for a lint.xml file next to the project.xml file, give it one. - configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml") - cacheDir = android.PathForModuleOut(ctx, "lint", "cache") - homeDir = android.PathForModuleOut(ctx, "lint", "home") + configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml") + cacheDir := android.PathForModuleOut(ctx, "lint", "cache") + homeDir := android.PathForModuleOut(ctx, "lint", "home") srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars") srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) + // TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced + // by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars, + // not the extracted files. + trackRSPDependency(l.srcJars, srcJarList) + + // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to + // lint separately. + srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list") + rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList) + trackRSPDependency(l.srcs, srcsList) cmd := rule.Command(). BuiltTool("lint-project-xml"). @@ -209,38 +251,39 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, cmd.Flag("--test") } if l.manifest != nil { - deps = append(deps, l.manifest) cmd.FlagWithArg("--manifest ", l.manifest.String()) + trackInputDependency(l.manifest) } if l.mergedManifest != nil { - deps = append(deps, l.mergedManifest) cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String()) + trackInputDependency(l.mergedManifest) } - // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to - // lint separately. - cmd.FlagWithRspFileInputList("--srcs ", l.srcs) - deps = append(deps, l.srcs...) + cmd.FlagWithInput("--srcs ", srcsList) cmd.FlagWithInput("--generated_srcs ", srcJarList) - deps = append(deps, l.srcJars...) if resourcesList != nil { cmd.FlagWithInput("--resources ", resourcesList) } if l.classes != nil { - deps = append(deps, l.classes) cmd.FlagWithArg("--classes ", l.classes.String()) + trackInputDependency(l.classes) } cmd.FlagForEachArg("--classpath ", l.classpath.Strings()) - deps = append(deps, l.classpath...) + trackInputDependency(l.classpath...) cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings()) - deps = append(deps, l.extraLintCheckJars...) + trackInputDependency(l.extraLintCheckJars...) - cmd.FlagWithArg("--root_dir ", "$PWD") + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") { + // TODO(b/181912787): remove these and use "." instead. + cmd.FlagWithArg("--root_dir ", "/b/f/w") + } else { + cmd.FlagWithArg("--root_dir ", "$PWD") + } // The cache tag in project.xml is relative to the root dir, or the project.xml file if // the root dir is not set. @@ -254,7 +297,18 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) - return projectXMLPath, configXMLPath, cacheDir, homeDir, deps + return lintPaths{ + projectXML: projectXMLPath, + configXML: configXMLPath, + cacheDir: cacheDir, + homeDir: homeDir, + + deps: deps, + + remoteInputs: remoteInputs, + remoteRSPInputs: remoteRSPInputs, + } + } // generateManifest adds a command to the rule to write a simple manifest that contains the @@ -297,7 +351,7 @@ func (l *linter) lint(ctx android.ModuleContext) { l.manifest = manifest } - projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule) + lintPaths := l.writeLintProjectXML(ctx, rule) html := android.PathForModuleOut(ctx, "lint-report.html") text := android.PathForModuleOut(ctx, "lint-report.txt") @@ -311,8 +365,9 @@ func (l *linter) lint(ctx android.ModuleContext) { } }) - rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String()) - rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String()) + rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) + rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) + rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) var annotationsZipPath, apiVersionsXMLPath android.Path if ctx.Config().AlwaysUsePrebuiltSdks() { @@ -323,17 +378,52 @@ func (l *linter) lint(ctx android.ModuleContext) { apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx) } - cmd := rule.Command(). - Text("("). - Flag("JAVA_OPTS=-Xmx3072m"). - FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()). + cmd := rule.Command() + + cmd.Flag("JAVA_OPTS=-Xmx3072m"). + FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()). FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath). - FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath). - Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")). - Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")). + FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath) + + if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") { + pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16") + // TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml + // is removed. + execStrategy := ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.RemoteExecStrategy) + labels := map[string]string{"type": "tool", "name": "lint"} + rule.Remoteable(android.RemoteRuleSupports{RBE: true}) + remoteInputs := lintPaths.remoteInputs + remoteInputs = append(remoteInputs, + lintPaths.projectXML, + lintPaths.configXML, + lintPaths.homeDir, + lintPaths.cacheDir, + ctx.Config().HostJavaToolPath(ctx, "lint.jar"), + annotationsZipPath, + apiVersionsXMLPath, + ) + + cmd.Text((&remoteexec.REParams{ + Labels: labels, + ExecStrategy: execStrategy, + ToolchainInputs: []string{config.JavaCmd(ctx).String()}, + Inputs: remoteInputs.Strings(), + OutputFiles: android.Paths{html, text, xml}.Strings(), + RSPFile: strings.Join(lintPaths.remoteRSPInputs.Strings(), ","), + EnvironmentVariables: []string{ + "JAVA_OPTS", + "ANDROID_SDK_HOME", + "SDK_ANNOTATIONS", + "LINT_OPTS", + }, + Platform: map[string]string{remoteexec.PoolKey: pool}, + }).NoVarTemplate(ctx.Config())) + } + + cmd.BuiltTool("lint"). Flag("--quiet"). - FlagWithInput("--project ", projectXML). - FlagWithInput("--config ", lintXML). + FlagWithInput("--project ", lintPaths.projectXML). + FlagWithInput("--config ", lintPaths.configXML). FlagWithOutput("--html ", html). FlagWithOutput("--text ", text). FlagWithOutput("--xml ", xml). @@ -343,7 +433,9 @@ func (l *linter) lint(ctx android.ModuleContext) { FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). Flag("--exitcode"). Flags(l.properties.Lint.Flags). - Implicits(deps) + Implicit(annotationsZipPath). + Implicit(apiVersionsXMLPath). + Implicits(lintPaths.deps) if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" { cmd.FlagWithArg("--check ", checkOnly) @@ -362,9 +454,9 @@ func (l *linter) lint(ctx android.ModuleContext) { } } - cmd.Text("|| (").Text("cat").Input(text).Text("; exit 7)").Text(")") + cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)") - rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String()) + rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String()) rule.Build("lint", "lint") @@ -438,13 +530,13 @@ func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) { } ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, + Rule: android.CpIfChanged, Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"), Output: copiedAnnotationsZipPath(ctx), }) ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, + Rule: android.CpIfChanged, Input: android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"), Output: copiedAPIVersionsXmlPath(ctx), }) diff --git a/java/robolectric.go b/java/robolectric.go index 54ee8232b..9fe1f0e0f 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -31,13 +31,15 @@ func init() { } var robolectricDefaultLibs = []string{ - "Robolectric_all-target", "mockito-robolectric-prebuilt", "truth-prebuilt", // TODO(ccross): this is not needed at link time "junitxml", } +const robolectricCurrentLib = "Robolectric_all-target" +const robolectricPrebuiltLibPattern = "platform-robolectric-%s-prebuilt" + var ( roboCoverageLibsTag = dependencyTag{name: "roboCoverageLibs"} roboRuntimesTag = dependencyTag{name: "roboRuntimes"} @@ -57,6 +59,10 @@ type robolectricProperties struct { // Number of shards to use when running the tests. Shards *int64 } + + // The version number of a robolectric prebuilt to use from prebuilts/misc/common/robolectric + // instead of the one built from source in external/robolectric-shadows. + Robolectric_prebuilt_version *string } type robolectricTest struct { @@ -94,6 +100,12 @@ func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) { ctx.PropertyErrorf("instrumentation_for", "missing required instrumented module") } + if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" { + ctx.AddVariationDependencies(nil, libTag, fmt.Sprintf(robolectricPrebuiltLibPattern, v)) + } else { + ctx.AddVariationDependencies(nil, libTag, robolectricCurrentLib) + } + ctx.AddVariationDependencies(nil, libTag, robolectricDefaultLibs...) ctx.AddVariationDependencies(nil, roboCoverageLibsTag, r.robolectricProperties.Coverage_libs...) @@ -298,7 +310,11 @@ func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, test if t := r.robolectricProperties.Test_options.Timeout; t != nil { fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t) } - fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk") + if v := String(r.robolectricProperties.Robolectric_prebuilt_version); v != "" { + fmt.Fprintf(w, "-include prebuilts/misc/common/robolectric/%s/run_robotests.mk\n", v) + } else { + fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk") + } } // An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host diff --git a/java/rro_test.go b/java/rro_test.go index edbf1708b..061d9d348 100644 --- a/java/rro_test.go +++ b/java/rro_test.go @@ -20,6 +20,7 @@ import ( "testing" "android/soong/android" + "android/soong/shared" ) func TestRuntimeResourceOverlay(t *testing.T) { @@ -105,7 +106,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { // Check device location. path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"] - expectedPath = []string{"/tmp/target/product/test_device/product/overlay"} + expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay")} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } @@ -114,7 +115,7 @@ func TestRuntimeResourceOverlay(t *testing.T) { m = ctx.ModuleForTests("foo_themed", "android_common") androidMkEntries = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0] path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"] - expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"} + expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/faza")} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } @@ -160,7 +161,7 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { // Check device location. path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] - expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"} + expectedPath := []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/default_theme")} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath) } @@ -179,7 +180,7 @@ func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { // Check device location. path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] - expectedPath = []string{"/tmp/target/product/test_device/system/overlay"} + expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/system/overlay")} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 90823a0c6..30d120d5c 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1772,6 +1772,8 @@ type SdkLibraryImport struct { android.ApexModuleBase android.SdkBase + hiddenAPI + properties sdkLibraryImportProperties // Map from api scope to the scope specific property structure. @@ -1786,6 +1788,9 @@ type SdkLibraryImport struct { // The reference to the xml permissions module created by the source module. // Is nil if the source module does not exist. xmlPermissionsFileModule *sdkLibraryXml + + // Path to the dex implementation jar obtained from the prebuilt_apex, if any. + dexJarFile android.Path } var _ SdkLibraryDependency = (*SdkLibraryImport)(nil) @@ -1982,6 +1987,8 @@ func (module *SdkLibraryImport) OutputFiles(tag string) (android.Paths, error) { func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { module.generateCommonBuildActions(ctx) + var deapexerModule android.Module + // Record the paths to the prebuilt stubs library and stubs source. ctx.VisitDirectDeps(func(to android.Module) { tag := ctx.OtherModuleDependencyTag(to) @@ -2007,6 +2014,11 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo ctx.ModuleErrorf("xml permissions file module must be of type *sdkLibraryXml but was %T", to) } } + + // Save away the `deapexer` module on which this depends, if any. + if tag == android.DeapexerTag { + deapexerModule = to + } }) // Populate the scope paths with information from the properties. @@ -2019,6 +2031,32 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api) paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api) } + + if ctx.Device() { + // If this is a variant created for a prebuilt_apex then use the dex implementation jar + // obtained from the associated deapexer module. + ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if ai.ForPrebuiltApex { + if deapexerModule == nil { + // This should never happen as a variant for a prebuilt_apex is only created if the + // deapxer module has been configured to export the dex implementation jar for this module. + ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q", + module.Name(), ai.ApexVariationName) + } + + // Get the path of the dex implementation jar from the `deapexer` module. + di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) + if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil { + module.dexJarFile = dexOutputPath + module.initHiddenAPI(ctx, module.configurationName) + module.hiddenAPIExtractInformation(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0]) + } else { + // This should never happen as a variant for a prebuilt_apex is only created if the + // prebuilt_apex has been configured to export the java library dex file. + ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name()) + } + } + } } func (module *SdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths { @@ -2051,6 +2089,11 @@ func (module *SdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleCont // to satisfy UsesLibraryDependency interface func (module *SdkLibraryImport) DexJarBuildPath() android.Path { + // The dex implementation jar extracted from the .apex file should be used in preference to the + // source. + if module.dexJarFile != nil { + return module.dexJarFile + } if module.implLibraryModule == nil { return nil } else { diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go index 293493685..0acaa13b2 100644 --- a/java/sdk_library_external.go +++ b/java/sdk_library_external.go @@ -75,10 +75,15 @@ func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleCon return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList()) } +func (j *Module) syspropWithPublicStubs() bool { + return j.deviceProperties.SyspropPublicStub != "" +} + type javaSdkLibraryEnforceContext interface { Name() string allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool partitionGroup(ctx android.EarlyModuleContext) partitionGroup + syspropWithPublicStubs() bool } var _ javaSdkLibraryEnforceContext = (*Module)(nil) @@ -88,6 +93,10 @@ func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext return } + if dep.syspropWithPublicStubs() { + return + } + // If product interface is not enforced, skip check between system and product partition. // But still need to check between product and vendor partition because product interface flag // just represents enforcement between product and system, and vendor interface enforcement diff --git a/java/sysprop.go b/java/sysprop.go deleted file mode 100644 index e41aef68a..000000000 --- a/java/sysprop.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package java - -// This file contains a map to redirect dependencies towards sysprop_library. If a sysprop_library -// is owned by Platform, and the client module links against system API, the public stub of the -// sysprop_library should be used. The map will contain public stub names of sysprop_libraries. - -import ( - "sync" - - "android/soong/android" -) - -type syspropLibraryInterface interface { - BaseModuleName() string - Owner() string - HasPublicStub() bool - JavaPublicStubName() string -} - -var ( - syspropPublicStubsKey = android.NewOnceKey("syspropPublicStubsJava") - syspropPublicStubsLock sync.Mutex -) - -func init() { - android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("sysprop_java", SyspropMutator).Parallel() - }) -} - -func syspropPublicStubs(config android.Config) map[string]string { - return config.Once(syspropPublicStubsKey, func() interface{} { - return make(map[string]string) - }).(map[string]string) -} - -// gather list of sysprop libraries owned by platform. -func SyspropMutator(mctx android.BottomUpMutatorContext) { - if m, ok := mctx.Module().(syspropLibraryInterface); ok { - if m.Owner() != "Platform" || !m.HasPublicStub() { - return - } - - syspropPublicStubs := syspropPublicStubs(mctx.Config()) - syspropPublicStubsLock.Lock() - defer syspropPublicStubsLock.Unlock() - - syspropPublicStubs[m.BaseModuleName()] = m.JavaPublicStubName() - } -} diff --git a/java/testing.go b/java/testing.go index 781106ff2..bfa1e6b2a 100644 --- a/java/testing.go +++ b/java/testing.go @@ -240,6 +240,7 @@ func CheckModuleDependencies(t *testing.T, ctx *android.TestContext, name, varia } func CheckHiddenAPIRuleInputs(t *testing.T, expected string, hiddenAPIRule android.TestingBuildParams) { + t.Helper() actual := strings.TrimSpace(strings.Join(android.NormalizePathsForTesting(hiddenAPIRule.Implicits), "\n")) expected = strings.TrimSpace(expected) if actual != expected { diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go index d6e2c0a75..5f0426a7f 100644 --- a/remoteexec/remoteexec.go +++ b/remoteexec/remoteexec.go @@ -81,6 +81,9 @@ type REParams struct { // ToolchainInputs is a list of paths or ninja variables pointing to the location of // toolchain binaries used by the rule. ToolchainInputs []string + // EnvironmentVariables is a list of environment variables whose values should be passed through + // to the remote execution. + EnvironmentVariables []string } func init() { @@ -162,6 +165,10 @@ func (r *REParams) wrapperArgs() string { args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",") } + if len(r.EnvironmentVariables) > 0 { + args += " --env_var_allowlist=" + strings.Join(r.EnvironmentVariables, ",") + } + return args + " -- " } diff --git a/rust/builder.go b/rust/builder.go index 547d70569..632612457 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -230,6 +230,8 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String())) } + envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion) + if flags.Clippy { clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") ctx.Build(pctx, android.BuildParams{ diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go index fc11d2901..408d433c9 100644 --- a/rust/config/allowed_list.go +++ b/rust/config/allowed_list.go @@ -14,6 +14,7 @@ var ( "external/rust", "external/vm_tools/p9", "frameworks/native/libs/binder/rust", + "frameworks/proto_logging/stats", "packages/modules/DnsResolver", "packages/modules/Virtualization", "prebuilts/rust", diff --git a/rust/config/lints.go b/rust/config/lints.go index ac8165b22..7c05e4f1d 100644 --- a/rust/config/lints.go +++ b/rust/config/lints.go @@ -53,6 +53,7 @@ var ( defaultClippyLints = []string{ "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", + "-A clippy::unusual-byte-groupings", } // Rust lints for vendor code. diff --git a/rust/testing.go b/rust/testing.go index 1afe27ef0..9534ab580 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -17,6 +17,7 @@ package rust import ( "android/soong/android" "android/soong/cc" + "android/soong/genrule" ) func GatherRequiredDepsForTest() string { @@ -211,6 +212,7 @@ func CreateTestContext(config android.Config) *android.TestContext { ctx := android.NewTestArchContext(config) android.RegisterPrebuiltMutators(ctx) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) + genrule.RegisterGenruleBuildComponents(ctx) cc.RegisterRequiredBuildComponentsForTest(ctx) RegisterRequiredBuildComponentsForTest(ctx) diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh index b93c88349..18174a452 100755 --- a/scripts/build-mainline-modules.sh +++ b/scripts/build-mainline-modules.sh @@ -29,14 +29,16 @@ MODULES_SDK_AND_EXPORTS=( runtime-module-sdk stats-log-api-gen-exports statsd-module-sdk + statsd-module-sdk-for-art tzdata-module-test-exports ) # List of libraries installed on the platform that are needed for ART chroot # testing. PLATFORM_LIBRARIES=( - liblog + heapprofd_client_api libartpalette-system + liblog ) # We want to create apex modules for all supported architectures. diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py index 5ad61b2f6..b047aab71 100755 --- a/scripts/hiddenapi/merge_csv.py +++ b/scripts/hiddenapi/merge_csv.py @@ -20,6 +20,9 @@ Merge multiple CSV files, possibly with different columns. import argparse import csv import io +import heapq +import itertools +import operator from zipfile import ZipFile @@ -28,6 +31,10 @@ args_parser.add_argument('--header', help='Comma separated field names; ' 'if missing determines the header from input files.') args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.', action="store_true") +args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. ' + 'Must be in the field names. ' + 'Will be the first field in the output. ' + 'All input files must be sorted by that field.') args_parser.add_argument('--output', help='Output file for merged CSV.', default='-', type=argparse.FileType('w')) args_parser.add_argument('files', nargs=argparse.REMAINDER) @@ -57,10 +64,29 @@ else: headers = headers.union(reader.fieldnames) fieldnames = sorted(headers) -# Concatenate all files to output: +# By default chain the csv readers together so that the resulting output is +# the concatenation of the rows from each of them: +all_rows = itertools.chain.from_iterable(csv_readers) + +if len(csv_readers) > 0: + keyField = args.key_field + if keyField: + assert keyField in fieldnames, ( + "--key_field {} not found, must be one of {}\n").format( + keyField, ",".join(fieldnames)) + # Make the key field the first field in the output + keyFieldIndex = fieldnames.index(args.key_field) + fieldnames.insert(0, fieldnames.pop(keyFieldIndex)) + # Create an iterable that performs a lazy merge sort on the csv readers + # sorting the rows by the key field. + all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField)) + +# Write all rows from the input files to the output: writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, dialect='unix', fieldnames=fieldnames) writer.writeheader() -for reader in csv_readers: - for row in reader: - writer.writerow(row) + +# Read all the rows from the input and write them to the output in the correct +# order: +for row in all_rows: + writer.writerow(row) diff --git a/scripts/strip.sh b/scripts/strip.sh index 40f018425..5b7a6da7e 100755 --- a/scripts/strip.sh +++ b/scripts/strip.sh @@ -89,7 +89,7 @@ do_strip_keep_mini_debug_info() { "${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo" "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo" "${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo" - "${XZ}" "${outfile}.mini_debuginfo" + "${XZ}" --block-size=64k --threads=0 "${outfile}.mini_debuginfo" "${CLANG_BIN}/llvm-objcopy" --add-section .gnu_debugdata="${outfile}.mini_debuginfo.xz" "${outfile}.tmp" rm -f "${outfile}.dynsyms" "${outfile}.funcsyms" "${outfile}.keep_symbols" "${outfile}.debug" "${outfile}.mini_debuginfo" "${outfile}.mini_debuginfo.xz" diff --git a/sdk/bp_test.go b/sdk/bp_test.go index e1edc5131..a7164a5e9 100644 --- a/sdk/bp_test.go +++ b/sdk/bp_test.go @@ -54,7 +54,7 @@ func propertyStructFixture() interface{} { return str } -func checkPropertySetFixture(h *TestHelper, val interface{}, hasTags bool) { +func checkPropertySetFixture(h android.TestHelper, val interface{}, hasTags bool) { set := val.(*bpPropertySet) h.AssertDeepEquals("wrong x value", "taxi", set.getValue("x")) h.AssertDeepEquals("wrong y value", 1729, set.getValue("y")) @@ -73,7 +73,7 @@ func checkPropertySetFixture(h *TestHelper, val interface{}, hasTags bool) { } func TestAddPropertySimple(t *testing.T) { - h := &TestHelper{t} + h := android.TestHelper{t} set := newPropertySet() for name, val := range map[string]interface{}{ "x": "taxi", @@ -92,7 +92,7 @@ func TestAddPropertySimple(t *testing.T) { } func TestAddPropertySubset(t *testing.T) { - h := &TestHelper{t} + h := android.TestHelper{t} getFixtureMap := map[string]func() interface{}{ "property set": propertySetFixture, "property struct": propertyStructFixture, @@ -139,7 +139,7 @@ func TestAddPropertySubset(t *testing.T) { } func TestAddPropertySetNew(t *testing.T) { - h := &TestHelper{t} + h := android.TestHelper{t} set := newPropertySet() subset := set.AddPropertySet("sub") subset.AddProperty("new", "d^^b") @@ -147,7 +147,7 @@ func TestAddPropertySetNew(t *testing.T) { } func TestAddPropertySetExisting(t *testing.T) { - h := &TestHelper{t} + h := android.TestHelper{t} set := propertySetFixture().(*bpPropertySet) subset := set.AddPropertySet("sub") subset.AddProperty("new", "d^^b") @@ -181,7 +181,7 @@ func (t removeFredTransformation) transformPropertySetAfterContents(name string, func TestTransformRemoveProperty(t *testing.T) { - helper := &TestHelper{t} + helper := android.TestHelper{t} set := newPropertySet() set.AddProperty("name", "name") @@ -196,7 +196,7 @@ func TestTransformRemoveProperty(t *testing.T) { func TestTransformRemovePropertySet(t *testing.T) { - helper := &TestHelper{t} + helper := android.TestHelper{t} set := newPropertySet() set.AddProperty("name", "name") diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index b1eebe98c..359177771 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -22,14 +22,14 @@ import ( ) var ccTestFs = map[string][]byte{ - "Test.cpp": nil, - "include/Test.h": nil, - "include-android/AndroidTest.h": nil, - "include-host/HostTest.h": nil, - "arm64/include/Arm64Test.h": nil, - "libfoo.so": nil, - "aidl/foo/bar/Test.aidl": nil, - "some/where/stubslib.map.txt": nil, + "Test.cpp": nil, + "myinclude/Test.h": nil, + "myinclude-android/AndroidTest.h": nil, + "myinclude-host/HostTest.h": nil, + "arm64/include/Arm64Test.h": nil, + "libfoo.so": nil, + "aidl/foo/bar/Test.aidl": nil, + "some/where/stubslib.map.txt": nil, } func testSdkWithCc(t *testing.T, bp string) *testSdkResult { @@ -102,16 +102,15 @@ func TestSdkCompileMultilibOverride(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_sdkmember@current", - sdk_member_name: "sdkmember", + name: "sdkmember", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, - installable: false, stl: "none", compile_multilib: "64", target: { @@ -127,13 +126,17 @@ cc_prebuilt_library_shared { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "sdkmember", - prefer: false, + name: "mysdk_sdkmember@current", + sdk_member_name: "sdkmember", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, + installable: false, stl: "none", compile_multilib: "64", target: { @@ -248,6 +251,7 @@ func TestBasicSdkWithCc(t *testing.T) { uses_sdks: ["mysdk@1"], key: "myapex.key", certificate: ":myapex.cert", + updatable: false, } apex { @@ -256,6 +260,7 @@ func TestBasicSdkWithCc(t *testing.T) { uses_sdks: ["mysdk@2"], key: "myapex.key", certificate: ":myapex.cert", + updatable: false, } apex { @@ -263,6 +268,7 @@ func TestBasicSdkWithCc(t *testing.T) { native_shared_libs: ["sdkmember"], key: "myapex.key", certificate: ":myapex.cert", + updatable: false, } `) @@ -348,12 +354,12 @@ func TestSnapshotWithObject(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_object { - name: "mysdk_crtobj@current", - sdk_member_name: "crtobj", + name: "crtobj", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], stl: "none", @@ -370,10 +376,14 @@ cc_prebuilt_object { }, }, } +`), + // Make sure that the generated sdk_snapshot uses the native_objects property. + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_object { - name: "crtobj", - prefer: false, + name: "mysdk_crtobj@current", + sdk_member_name: "crtobj", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], stl: "none", @@ -416,7 +426,7 @@ func TestSnapshotWithCcDuplicateHeaders(t *testing.T) { srcs: [ "Test.cpp", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], stl: "none", } @@ -425,14 +435,14 @@ func TestSnapshotWithCcDuplicateHeaders(t *testing.T) { srcs: [ "Test.cpp", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], stl: "none", } `) result.CheckSnapshot("mysdk", "", checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib1/android_arm64_armv8-a_shared/mynativelib1.so -> arm64/lib/mynativelib1.so .intermediates/mynativelib1/android_arm_armv7-a-neon_shared/mynativelib1.so -> arm/lib/mynativelib1.so .intermediates/mynativelib2/android_arm64_armv8-a_shared/mynativelib2.so -> arm64/lib/mynativelib2.so @@ -441,6 +451,76 @@ include/Test.h -> include/include/Test.h ) } +func TestSnapshotWithCcExportGeneratedHeaders(t *testing.T) { + result := testSdkWithCc(t, ` + sdk { + name: "mysdk", + native_shared_libs: ["mynativelib"], + } + + cc_library_shared { + name: "mynativelib", + srcs: [ + "Test.cpp", + ], + generated_headers: [ + "generated_foo", + ], + export_generated_headers: [ + "generated_foo", + ], + export_include_dirs: ["myinclude"], + stl: "none", + } + + genrule { + name: "generated_foo", + cmd: "generate-foo", + out: [ + "generated_foo/protos/foo/bar.h", + ], + export_include_dirs: [ + ".", + "protos", + ], + } + `) + + result.CheckSnapshot("mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +cc_prebuilt_library_shared { + name: "mynativelib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + stl: "none", + compile_multilib: "both", + export_include_dirs: [ + "include/myinclude", + "include_gen/generated_foo/gen", + "include_gen/generated_foo/gen/protos", + ], + arch: { + arm64: { + srcs: ["arm64/lib/mynativelib.so"], + }, + arm: { + srcs: ["arm/lib/mynativelib.so"], + }, + }, +} +`), + checkAllCopyRules(` +myinclude/Test.h -> include/myinclude/Test.h +.intermediates/generated_foo/gen/generated_foo/protos/foo/bar.h -> include_gen/generated_foo/gen/generated_foo/protos/foo/bar.h +.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so +.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so +`), + ) +} + // Verify that when the shared library has some common and some arch specific // properties that the generated snapshot is optimized properly. Substruct // handling is tested with the sanitize clauses (but note there's a lot of @@ -458,7 +538,7 @@ func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) { "Test.cpp", "aidl/foo/bar/Test.aidl", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], sanitize: { fuzzer: false, integer_overflow: true, @@ -477,49 +557,17 @@ func TestSnapshotWithCcSharedLibraryCommonProperties(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - stl: "none", - compile_multilib: "both", - export_include_dirs: ["include/include"], - sanitize: { - fuzzer: false, - diag: { - undefined: false, - }, - }, - arch: { - arm64: { - srcs: ["arm64/lib/mynativelib.so"], - export_system_include_dirs: ["arm64/include/arm64/include"], - sanitize: { - integer_overflow: false, - }, - }, - arm: { - srcs: ["arm/lib/mynativelib.so"], - sanitize: { - integer_overflow: true, - }, - }, - }, -} - -cc_prebuilt_library_shared { name: "mynativelib", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], sanitize: { fuzzer: false, diag: { @@ -542,15 +590,9 @@ cc_prebuilt_library_shared { }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: ["mysdk_mynativelib@current"], -} `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`), @@ -574,15 +616,14 @@ func TestSnapshotWithCcBinary(t *testing.T) { `) result.CheckSnapshot("mymodule_exports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "mymodule_exports_mynativebinary@current", - sdk_member_name: "mynativebinary", + name: "mynativebinary", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - installable: false, compile_multilib: "both", arch: { arm64: { @@ -593,12 +634,17 @@ cc_prebuilt_binary { }, }, } +`), + // Make sure that the generated sdk_snapshot uses the native_binaries property. + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "mynativebinary", - prefer: false, + name: "mymodule_exports_mynativebinary@current", + sdk_member_name: "mynativebinary", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], + installable: false, compile_multilib: "both", arch: { arm64: { @@ -655,17 +701,16 @@ func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) { `) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "myexports_mynativebinary@current", - sdk_member_name: "mynativebinary", + name: "mynativebinary", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", target: { host: { @@ -691,14 +736,18 @@ cc_prebuilt_binary { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "mynativebinary", - prefer: false, + name: "myexports_mynativebinary@current", + sdk_member_name: "mynativebinary", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", target: { host: { @@ -805,17 +854,16 @@ func TestSnapshotWithSingleHostOsType(t *testing.T) { result := runTests(t, ctx, config) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "myexports_mynativebinary@current", - sdk_member_name: "mynativebinary", + name: "mynativebinary", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", compile_multilib: "64", target: { @@ -829,8 +877,8 @@ cc_prebuilt_binary { }, } -cc_prebuilt_binary { - name: "mynativebinary", +cc_prebuilt_library_shared { + name: "mynativelib", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], @@ -844,14 +892,17 @@ cc_prebuilt_binary { }, linux_bionic_x86_64: { enabled: true, - srcs: ["x86_64/bin/mynativebinary"], + srcs: ["x86_64/lib/mynativelib.so"], }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. -cc_prebuilt_library_shared { - name: "myexports_mynativelib@current", - sdk_member_name: "mynativelib", +cc_prebuilt_binary { + name: "myexports_mynativebinary@current", + sdk_member_name: "mynativebinary", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, @@ -865,18 +916,19 @@ cc_prebuilt_library_shared { }, linux_bionic_x86_64: { enabled: true, - srcs: ["x86_64/lib/mynativelib.so"], + srcs: ["x86_64/bin/mynativebinary"], }, }, } cc_prebuilt_library_shared { - name: "mynativelib", - prefer: false, + name: "myexports_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", compile_multilib: "64", target: { @@ -940,17 +992,16 @@ func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) { `) result.CheckSnapshot("mymodule_exports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "mymodule_exports_linker@current", - sdk_member_name: "linker", + name: "linker", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", compile_multilib: "both", static_executable: true, @@ -969,14 +1020,18 @@ cc_prebuilt_binary { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_binary { - name: "linker", - prefer: false, + name: "mymodule_exports_linker@current", + sdk_member_name: "linker", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", compile_multilib: "both", static_executable: true, @@ -1036,7 +1091,7 @@ func TestSnapshotWithCcSharedLibrary(t *testing.T) { "aidl/foo/bar/Test.aidl", ], apex_available: ["apex1", "apex2"], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], aidl: { export_aidl_headers: true, }, @@ -1045,34 +1100,10 @@ func TestSnapshotWithCcSharedLibrary(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", - visibility: ["//visibility:public"], - apex_available: [ - "apex1", - "apex2", - ], - installable: false, - stl: "none", - compile_multilib: "both", - export_include_dirs: ["include/include"], - arch: { - arm64: { - srcs: ["arm64/lib/mynativelib.so"], - export_include_dirs: ["arm64/include_gen/mynativelib"], - }, - arm: { - srcs: ["arm/lib/mynativelib.so"], - export_include_dirs: ["arm/include_gen/mynativelib"], - }, - }, -} - -cc_prebuilt_library_shared { name: "mynativelib", prefer: false, visibility: ["//visibility:public"], @@ -1082,35 +1113,29 @@ cc_prebuilt_library_shared { ], stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], arch: { arm64: { srcs: ["arm64/lib/mynativelib.so"], - export_include_dirs: ["arm64/include_gen/mynativelib"], + export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl"], }, arm: { srcs: ["arm/lib/mynativelib.so"], - export_include_dirs: ["arm/include_gen/mynativelib"], + export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl"], }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: ["mysdk_mynativelib@current"], -} `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so -.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_shared/gen/aidl/aidl/foo/bar/BpTest.h .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so -.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_shared/gen/aidl/aidl/foo/bar/BpTest.h `), ) } @@ -1176,32 +1201,10 @@ func TestSnapshotWithCcSharedLibrarySharedLibs(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - stl: "none", - compile_multilib: "both", - shared_libs: [ - "mysdk_myothernativelib@current", - "libc", - ], - arch: { - arm64: { - srcs: ["arm64/lib/mynativelib.so"], - }, - arm: { - srcs: ["arm/lib/mynativelib.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "mynativelib", prefer: false, visibility: ["//visibility:public"], @@ -1223,25 +1226,6 @@ cc_prebuilt_library_shared { } cc_prebuilt_library_shared { - name: "mysdk_myothernativelib@current", - sdk_member_name: "myothernativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - stl: "none", - compile_multilib: "both", - system_shared_libs: ["libm"], - arch: { - arm64: { - srcs: ["arm64/lib/myothernativelib.so"], - }, - arm: { - srcs: ["arm/lib/myothernativelib.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "myothernativelib", prefer: false, visibility: ["//visibility:public"], @@ -1260,24 +1244,6 @@ cc_prebuilt_library_shared { } cc_prebuilt_library_shared { - name: "mysdk_mysystemnativelib@current", - sdk_member_name: "mysystemnativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - stl: "none", - compile_multilib: "both", - arch: { - arm64: { - srcs: ["arm64/lib/mysystemnativelib.so"], - }, - arm: { - srcs: ["arm/lib/mysystemnativelib.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "mysystemnativelib", prefer: false, visibility: ["//visibility:public"], @@ -1293,16 +1259,6 @@ cc_prebuilt_library_shared { }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: [ - "mysdk_mynativelib@current", - "mysdk_myothernativelib@current", - "mysdk_mysystemnativelib@current", - ], -} `), checkAllCopyRules(` .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so @@ -1332,7 +1288,7 @@ func TestHostSnapshotWithCcSharedLibrary(t *testing.T) { "Test.cpp", "aidl/foo/bar/Test.aidl", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], aidl: { export_aidl_headers: true, }, @@ -1342,21 +1298,20 @@ func TestHostSnapshotWithCcSharedLibrary(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", + name: "mynativelib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, sdk_version: "minimum", stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -1364,27 +1319,31 @@ cc_prebuilt_library_shared { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.so"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], + export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"], }, linux_glibc_x86: { enabled: true, srcs: ["x86/lib/mynativelib.so"], - export_include_dirs: ["x86/include_gen/mynativelib"], + export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"], }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mynativelib", - prefer: false, + name: "mysdk_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, sdk_version: "minimum", stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -1392,12 +1351,12 @@ cc_prebuilt_library_shared { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.so"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], + export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl"], }, linux_glibc_x86: { enabled: true, srcs: ["x86/lib/mynativelib.so"], - export_include_dirs: ["x86/include_gen/mynativelib"], + export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl"], }, }, } @@ -1422,15 +1381,15 @@ sdk_snapshot { } `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/linux_glibc_x86_64_shared/mynativelib.so -> x86_64/lib/mynativelib.so -.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_shared/gen/aidl/aidl/foo/bar/BpTest.h .intermediates/mynativelib/linux_glibc_x86_shared/mynativelib.so -> x86/lib/mynativelib.so -.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_shared/gen/aidl/aidl/foo/bar/BpTest.h `), ) } @@ -1466,17 +1425,16 @@ func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", + name: "mynativelib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", target: { host: { @@ -1502,14 +1460,18 @@ cc_prebuilt_library_shared { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mynativelib", - prefer: false, + name: "mysdk_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", target: { host: { @@ -1582,7 +1544,7 @@ func TestSnapshotWithCcStaticLibrary(t *testing.T) { "Test.cpp", "aidl/foo/bar/Test.aidl", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], aidl: { export_aidl_headers: true, }, @@ -1591,66 +1553,39 @@ func TestSnapshotWithCcStaticLibrary(t *testing.T) { `) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_static { - name: "myexports_mynativelib@current", - sdk_member_name: "mynativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - stl: "none", - compile_multilib: "both", - export_include_dirs: ["include/include"], - arch: { - arm64: { - srcs: ["arm64/lib/mynativelib.a"], - export_include_dirs: ["arm64/include_gen/mynativelib"], - }, - arm: { - srcs: ["arm/lib/mynativelib.a"], - export_include_dirs: ["arm/include_gen/mynativelib"], - }, - }, -} - -cc_prebuilt_library_static { name: "mynativelib", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], arch: { arm64: { srcs: ["arm64/lib/mynativelib.a"], - export_include_dirs: ["arm64/include_gen/mynativelib"], + export_include_dirs: ["arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl"], }, arm: { srcs: ["arm/lib/mynativelib.a"], - export_include_dirs: ["arm/include_gen/mynativelib"], + export_include_dirs: ["arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl"], }, }, } - -module_exports_snapshot { - name: "myexports@current", - visibility: ["//visibility:public"], - native_static_libs: ["myexports_mynativelib@current"], -} `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a -.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm64/include_gen/mynativelib/android_arm64_armv8-a_static/gen/aidl/aidl/foo/bar/BpTest.h .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a -.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h -> arm/include_gen/mynativelib/android_arm_armv7-a-neon_static/gen/aidl/aidl/foo/bar/BpTest.h `), ) } @@ -1672,7 +1607,7 @@ func TestHostSnapshotWithCcStaticLibrary(t *testing.T) { "Test.cpp", "aidl/foo/bar/Test.aidl", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], aidl: { export_aidl_headers: true, }, @@ -1681,20 +1616,19 @@ func TestHostSnapshotWithCcStaticLibrary(t *testing.T) { `) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_static { - name: "myexports_mynativelib@current", - sdk_member_name: "mynativelib", + name: "mynativelib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -1702,26 +1636,30 @@ cc_prebuilt_library_static { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.a"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], + export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"], }, linux_glibc_x86: { enabled: true, srcs: ["x86/lib/mynativelib.a"], - export_include_dirs: ["x86/include_gen/mynativelib"], + export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"], }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_static { - name: "mynativelib", - prefer: false, + name: "myexports_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -1729,12 +1667,12 @@ cc_prebuilt_library_static { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.a"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], + export_include_dirs: ["x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl"], }, linux_glibc_x86: { enabled: true, srcs: ["x86/lib/mynativelib.a"], - export_include_dirs: ["x86/include_gen/mynativelib"], + export_include_dirs: ["x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl"], }, }, } @@ -1759,15 +1697,15 @@ module_exports_snapshot { } `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h .intermediates/mynativelib/linux_glibc_x86_static/mynativelib.a -> x86/lib/mynativelib.a -.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/aidl/foo/bar/BpTest.h +.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86/include_gen/mynativelib/linux_glibc_x86_static/gen/aidl/aidl/foo/bar/BpTest.h `), ) } @@ -1784,7 +1722,7 @@ func TestSnapshotWithCcLibrary(t *testing.T) { srcs: [ "Test.cpp", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], stl: "none", recovery_available: true, vendor_available: true, @@ -1792,20 +1730,19 @@ func TestSnapshotWithCcLibrary(t *testing.T) { `) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library { - name: "myexports_mynativelib@current", - sdk_member_name: "mynativelib", + name: "mynativelib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - installable: false, recovery_available: true, vendor_available: true, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], arch: { arm64: { static: { @@ -1825,17 +1762,22 @@ cc_prebuilt_library { }, }, } +`), + // Make sure that the generated sdk_snapshot uses the native_libs property. + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library { - name: "mynativelib", - prefer: false, + name: "myexports_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], + installable: false, recovery_available: true, vendor_available: true, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], arch: { arm64: { static: { @@ -1863,7 +1805,7 @@ module_exports_snapshot { } `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a @@ -1893,7 +1835,7 @@ func TestHostSnapshotWithMultiLib64(t *testing.T) { "Test.cpp", "aidl/foo/bar/Test.aidl", ], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], aidl: { export_aidl_headers: true, }, @@ -1902,20 +1844,22 @@ func TestHostSnapshotWithMultiLib64(t *testing.T) { `) result.CheckSnapshot("myexports", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_static { - name: "myexports_mynativelib@current", - sdk_member_name: "mynativelib", + name: "mynativelib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, - installable: false, stl: "none", compile_multilib: "64", - export_include_dirs: ["include/include"], + export_include_dirs: [ + "include/myinclude", + "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl", + ], target: { host: { enabled: false, @@ -1923,21 +1867,27 @@ cc_prebuilt_library_static { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.a"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_static { - name: "mynativelib", - prefer: false, + name: "myexports_mynativelib@current", + sdk_member_name: "mynativelib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, + installable: false, stl: "none", compile_multilib: "64", - export_include_dirs: ["include/include"], + export_include_dirs: [ + "include/myinclude", + "include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl", + ], target: { host: { enabled: false, @@ -1945,7 +1895,6 @@ cc_prebuilt_library_static { linux_glibc_x86_64: { enabled: true, srcs: ["x86_64/lib/mynativelib.a"], - export_include_dirs: ["x86_64/include_gen/mynativelib"], }, }, } @@ -1965,13 +1914,14 @@ module_exports_snapshot { enabled: true, }, }, -}`), +} +`), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h +.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> include_gen/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h .intermediates/mynativelib/linux_glibc_x86_64_static/mynativelib.a -> x86_64/lib/mynativelib.a -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/Test.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/Test.h -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BnTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BnTest.h -.intermediates/mynativelib/linux_glibc_x86_64_static/gen/aidl/aidl/foo/bar/BpTest.h -> x86_64/include_gen/mynativelib/aidl/foo/bar/BpTest.h `), ) } @@ -1985,43 +1935,27 @@ func TestSnapshotWithCcHeadersLibrary(t *testing.T) { cc_library_headers { name: "mynativeheaders", - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], stl: "none", } `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_headers { - name: "mysdk_mynativeheaders@current", - sdk_member_name: "mynativeheaders", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - stl: "none", - compile_multilib: "both", - export_include_dirs: ["include/include"], -} - -cc_prebuilt_library_headers { name: "mynativeheaders", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], -} - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_header_libs: ["mysdk_mynativeheaders@current"], + export_include_dirs: ["include/myinclude"], } `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h `), ) } @@ -2039,25 +1973,25 @@ func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) { name: "mynativeheaders", device_supported: false, host_supported: true, - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], stl: "none", } `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_headers { - name: "mysdk_mynativeheaders@current", - sdk_member_name: "mynativeheaders", + name: "mynativeheaders", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -2070,17 +2004,20 @@ cc_prebuilt_library_headers { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_headers { - name: "mynativeheaders", - prefer: false, + name: "mysdk_mynativeheaders@current", + sdk_member_name: "mynativeheaders", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], device_supported: false, host_supported: true, stl: "none", compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], target: { host: { enabled: false, @@ -2114,7 +2051,7 @@ sdk_snapshot { } `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h `), ) } @@ -2131,40 +2068,40 @@ func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) { name: "mynativeheaders", host_supported: true, stl: "none", - export_system_include_dirs: ["include"], + export_system_include_dirs: ["myinclude"], target: { android: { - export_include_dirs: ["include-android"], + export_include_dirs: ["myinclude-android"], }, host: { - export_include_dirs: ["include-host"], + export_include_dirs: ["myinclude-host"], }, }, } `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_headers { - name: "mysdk_mynativeheaders@current", - sdk_member_name: "mynativeheaders", + name: "mynativeheaders", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, stl: "none", compile_multilib: "both", - export_system_include_dirs: ["common_os/include/include"], + export_system_include_dirs: ["common_os/include/myinclude"], target: { host: { enabled: false, }, android: { - export_include_dirs: ["android/include/include-android"], + export_include_dirs: ["android/include/myinclude-android"], }, linux_glibc: { - export_include_dirs: ["linux_glibc/include/include-host"], + export_include_dirs: ["linux_glibc/include/myinclude-host"], }, linux_glibc_x86_64: { enabled: true, @@ -2174,25 +2111,29 @@ cc_prebuilt_library_headers { }, }, } +`), + // Verifi + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_headers { - name: "mynativeheaders", - prefer: false, + name: "mysdk_mynativeheaders@current", + sdk_member_name: "mynativeheaders", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, stl: "none", compile_multilib: "both", - export_system_include_dirs: ["common_os/include/include"], + export_system_include_dirs: ["common_os/include/myinclude"], target: { host: { enabled: false, }, android: { - export_include_dirs: ["android/include/include-android"], + export_include_dirs: ["android/include/myinclude-android"], }, linux_glibc: { - export_include_dirs: ["linux_glibc/include/include-host"], + export_include_dirs: ["linux_glibc/include/myinclude-host"], }, linux_glibc_x86_64: { enabled: true, @@ -2222,9 +2163,9 @@ sdk_snapshot { } `), checkAllCopyRules(` -include/Test.h -> common_os/include/include/Test.h -include-android/AndroidTest.h -> android/include/include-android/AndroidTest.h -include-host/HostTest.h -> linux_glibc/include/include-host/HostTest.h +myinclude/Test.h -> common_os/include/myinclude/Test.h +myinclude-android/AndroidTest.h -> android/include/myinclude-android/AndroidTest.h +myinclude-host/HostTest.h -> linux_glibc/include/myinclude-host/HostTest.h `), ) } @@ -2253,27 +2194,10 @@ func TestSystemSharedLibPropagation(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_sslnil@current", - sdk_member_name: "sslnil", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - compile_multilib: "both", - arch: { - arm64: { - srcs: ["arm64/lib/sslnil.so"], - }, - arm: { - srcs: ["arm/lib/sslnil.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "sslnil", prefer: false, visibility: ["//visibility:public"], @@ -2290,24 +2214,6 @@ cc_prebuilt_library_shared { } cc_prebuilt_library_shared { - name: "mysdk_sslempty@current", - sdk_member_name: "sslempty", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - compile_multilib: "both", - system_shared_libs: [], - arch: { - arm64: { - srcs: ["arm64/lib/sslempty.so"], - }, - arm: { - srcs: ["arm/lib/sslempty.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "sslempty", prefer: false, visibility: ["//visibility:public"], @@ -2325,24 +2231,6 @@ cc_prebuilt_library_shared { } cc_prebuilt_library_shared { - name: "mysdk_sslnonempty@current", - sdk_member_name: "sslnonempty", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - compile_multilib: "both", - system_shared_libs: ["mysdk_sslnil@current"], - arch: { - arm64: { - srcs: ["arm64/lib/sslnonempty.so"], - }, - arm: { - srcs: ["arm/lib/sslnonempty.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "sslnonempty", prefer: false, visibility: ["//visibility:public"], @@ -2358,16 +2246,6 @@ cc_prebuilt_library_shared { }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: [ - "mysdk_sslnil@current", - "mysdk_sslempty@current", - "mysdk_sslnonempty@current", - ], -} `)) result = testSdkWithCc(t, ` @@ -2389,16 +2267,15 @@ sdk_snapshot { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_sslvariants@current", - sdk_member_name: "sslvariants", + name: "sslvariants", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, - installable: false, compile_multilib: "both", target: { host: { @@ -2423,13 +2300,17 @@ cc_prebuilt_library_shared { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "sslvariants", - prefer: false, + name: "mysdk_sslvariants@current", + sdk_member_name: "sslvariants", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, + installable: false, compile_multilib: "both", target: { host: { @@ -2497,34 +2378,10 @@ func TestStubsLibrary(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_stubslib@current", - sdk_member_name: "stubslib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - compile_multilib: "both", - stubs: { - versions: [ - "1", - "2", - "3", - ], - }, - arch: { - arm64: { - srcs: ["arm64/lib/stubslib.so"], - }, - arm: { - srcs: ["arm/lib/stubslib.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "stubslib", prefer: false, visibility: ["//visibility:public"], @@ -2546,12 +2403,6 @@ cc_prebuilt_library_shared { }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: ["mysdk_stubslib@current"], -} `)) } @@ -2580,16 +2431,15 @@ func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_stubslib@current", - sdk_member_name: "stubslib", + name: "stubslib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, - installable: false, compile_multilib: "both", stubs: { versions: [ @@ -2618,13 +2468,17 @@ cc_prebuilt_library_shared { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "stubslib", - prefer: false, + name: "mysdk_stubslib@current", + sdk_member_name: "stubslib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, + installable: false, compile_multilib: "both", stubs: { versions: [ @@ -2690,16 +2544,15 @@ func TestUniqueHostSoname(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mylib@current", - sdk_member_name: "mylib", + name: "mylib", + prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, - installable: false, unique_host_soname: true, compile_multilib: "both", target: { @@ -2722,13 +2575,17 @@ cc_prebuilt_library_shared { }, }, } +`), + checkVersionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mylib", - prefer: false, + name: "mysdk_mylib@current", + sdk_member_name: "mylib", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], host_supported: true, + installable: false, unique_host_soname: true, compile_multilib: "both", target: { @@ -2789,7 +2646,7 @@ func TestNoSanitizerMembers(t *testing.T) { cc_library_shared { name: "mynativelib", srcs: ["Test.cpp"], - export_include_dirs: ["include"], + export_include_dirs: ["myinclude"], arch: { arm64: { export_system_include_dirs: ["arm64/include"], @@ -2802,34 +2659,16 @@ func TestNoSanitizerMembers(t *testing.T) { `) result.CheckSnapshot("mysdk", "", - checkAndroidBpContents(` + checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. cc_prebuilt_library_shared { - name: "mysdk_mynativelib@current", - sdk_member_name: "mynativelib", - visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], - installable: false, - compile_multilib: "both", - export_include_dirs: ["include/include"], - arch: { - arm64: { - export_system_include_dirs: ["arm64/include/arm64/include"], - }, - arm: { - srcs: ["arm/lib/mynativelib.so"], - }, - }, -} - -cc_prebuilt_library_shared { name: "mynativelib", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], compile_multilib: "both", - export_include_dirs: ["include/include"], + export_include_dirs: ["include/myinclude"], arch: { arm64: { export_system_include_dirs: ["arm64/include/arm64/include"], @@ -2839,15 +2678,9 @@ cc_prebuilt_library_shared { }, }, } - -sdk_snapshot { - name: "mysdk@current", - visibility: ["//visibility:public"], - native_shared_libs: ["mysdk_mynativelib@current"], -} `), checkAllCopyRules(` -include/Test.h -> include/include/Test.h +myinclude/Test.h -> include/myinclude/Test.h arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`), ) diff --git a/sdk/exports.go b/sdk/exports.go index d3130574e..9a0ba4e32 100644 --- a/sdk/exports.go +++ b/sdk/exports.go @@ -17,8 +17,12 @@ package sdk import "android/soong/android" func init() { - android.RegisterModuleType("module_exports", ModuleExportsFactory) - android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory) + registerModuleExportsBuildComponents(android.InitRegistrationContext) +} + +func registerModuleExportsBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("module_exports", ModuleExportsFactory) + ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory) } // module_exports defines the exports of a mainline module. The exports are Soong modules diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 17a6ca9b8..111b22c68 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -211,6 +211,7 @@ func TestBasicSdkWithJavaLibrary(t *testing.T) { uses_sdks: ["mysdk@1"], key: "myapex.key", certificate: ":myapex.cert", + updatable: false, } apex { @@ -219,6 +220,7 @@ func TestBasicSdkWithJavaLibrary(t *testing.T) { uses_sdks: ["mysdk@2"], key: "myapex.key", certificate: ":myapex.cert", + updatable: false, } `) diff --git a/sdk/sdk.go b/sdk/sdk.go index f3d075022..2c84a2e88 100644 --- a/sdk/sdk.go +++ b/sdk/sdk.go @@ -33,10 +33,14 @@ func init() { pctx.Import("android/soong/android") pctx.Import("android/soong/java/config") - android.RegisterModuleType("sdk", SdkModuleFactory) - android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory) - android.PreDepsMutators(RegisterPreDepsMutators) - android.PostDepsMutators(RegisterPostDepsMutators) + registerSdkBuildComponents(android.InitRegistrationContext) +} + +func registerSdkBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("sdk", SdkModuleFactory) + ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory) + ctx.PreDepsMutators(RegisterPreDepsMutators) + ctx.PostDepsMutators(RegisterPostDepsMutators) } type sdk struct { diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index c4dc41beb..a7acd0c5f 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -309,16 +309,13 @@ func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) { `) } -func TestSDkInstall(t *testing.T) { +func TestSdkInstall(t *testing.T) { sdk := ` sdk { name: "mysdk", } ` - result := testSdkWithFs(t, ``, - map[string][]byte{ - "Android.bp": []byte(sdk), - }) + result := testSdkWithFs(t, sdk, nil) result.CheckSnapshot("mysdk", "", checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`), @@ -390,7 +387,7 @@ func TestCommonValueOptimization(t *testing.T) { extractor := newCommonValueExtractor(common) - h := TestHelper{t} + h := android.TestHelper{t} err := extractor.extractCommonProperties(common, structs) h.AssertDeepEquals("unexpected error", nil, err) @@ -465,7 +462,7 @@ func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) { extractor := newCommonValueExtractor(common) - h := TestHelper{t} + h := android.TestHelper{t} err := extractor.extractCommonProperties(common, structs) h.AssertErrorMessageEquals("unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties: diff --git a/sdk/testing.go b/sdk/testing.go index 1ac873b4f..e291bdbdf 100644 --- a/sdk/testing.go +++ b/sdk/testing.go @@ -19,13 +19,13 @@ import ( "io/ioutil" "os" "path/filepath" - "reflect" "strings" "testing" "android/soong/android" "android/soong/apex" "android/soong/cc" + "android/soong/genrule" "android/soong/java" ) @@ -109,6 +109,9 @@ func testSdkContext(bp string, fs map[string][]byte, extraOsTypes []android.OsTy // from java package java.RegisterRequiredBuildComponentsForTest(ctx) + // from genrule package + genrule.RegisterGenruleBuildComponents(ctx) + // from cc package cc.RegisterRequiredBuildComponentsForTest(ctx) @@ -118,12 +121,8 @@ func testSdkContext(bp string, fs map[string][]byte, extraOsTypes []android.OsTy ctx.PostDepsMutators(apex.RegisterPostDepsMutators) // from this package - ctx.RegisterModuleType("sdk", SdkModuleFactory) - ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory) - ctx.RegisterModuleType("module_exports", ModuleExportsFactory) - ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory) - ctx.PreDepsMutators(RegisterPreDepsMutators) - ctx.PostDepsMutators(RegisterPostDepsMutators) + registerModuleExportsBuildComponents(ctx) + registerSdkBuildComponents(ctx) ctx.Register() @@ -137,7 +136,7 @@ func runTests(t *testing.T, ctx *android.TestContext, config android.Config) *te _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) return &testSdkResult{ - TestHelper: TestHelper{t: t}, + TestHelper: android.TestHelper{T: t}, ctx: ctx, config: config, } @@ -181,59 +180,10 @@ func pathsToStrings(paths android.Paths) []string { return ret } -// Provides general test support. -type TestHelper struct { - t *testing.T -} - -func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) { - h.t.Helper() - if actual != expected { - h.t.Errorf("%s: expected %s, actual %s", message, expected, actual) - } -} - -func (h *TestHelper) AssertErrorMessageEquals(message string, expected string, actual error) { - h.t.Helper() - if actual == nil { - h.t.Errorf("Expected error but was nil") - } else if actual.Error() != expected { - h.t.Errorf("%s: expected %s, actual %s", message, expected, actual.Error()) - } -} - -func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) { - h.t.Helper() - h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual)) -} - -func (h *TestHelper) AssertDeepEquals(message string, expected interface{}, actual interface{}) { - h.t.Helper() - if !reflect.DeepEqual(actual, expected) { - h.t.Errorf("%s: expected %#v, actual %#v", message, expected, actual) - } -} - -func (h *TestHelper) AssertPanic(message string, funcThatShouldPanic func()) { - h.t.Helper() - panicked := false - func() { - defer func() { - if x := recover(); x != nil { - panicked = true - } - }() - funcThatShouldPanic() - }() - if !panicked { - h.t.Error(message) - } -} - // Encapsulates result of processing an SDK definition. Provides support for // checking the state of the build structures. type testSdkResult struct { - TestHelper + android.TestHelper ctx *android.TestContext config android.Config } @@ -243,11 +193,11 @@ type testSdkResult struct { // e.g. find the src/dest pairs from each cp command, the various zip files // generated, etc. func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo { - androidBpContents := sdk.GetAndroidBpContentsForTests() - info := &snapshotBuildInfo{ - r: r, - androidBpContents: androidBpContents, + r: r, + androidBpContents: sdk.GetAndroidBpContentsForTests(), + androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(), + androidVersionedBpContents: sdk.GetVersionedAndroidBpContentsForTests(), } buildParams := sdk.BuildParamsForTests() @@ -287,7 +237,7 @@ func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo { info.intermediateZip = info.outputZip mergeInput := android.NormalizePathForTesting(bp.Input) if info.intermediateZip != mergeInput { - r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead", + r.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead", info.intermediateZip, mergeInput) } @@ -320,7 +270,7 @@ func (r *testSdkResult) ModuleForTests(name string, variant string) android.Test // Allows each test to customize what is checked without duplicating lots of code // or proliferating check methods of different flavors. func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snapshotBuildInfoChecker) { - r.t.Helper() + r.Helper() // The sdk CommonOS variant is always responsible for generating the snapshot. variant := android.CommonOS.Name @@ -350,7 +300,7 @@ func (r *testSdkResult) CheckSnapshot(name string, dir string, checkers ...snaps } // Process the generated bp file to make sure it is valid. - testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs) + testSdkWithFs(r.T, snapshotBuildInfo.androidBpContents, fs) } type snapshotBuildInfoChecker func(info *snapshotBuildInfo) @@ -360,11 +310,38 @@ type snapshotBuildInfoChecker func(info *snapshotBuildInfo) // Both the expected and actual string are both trimmed before comparing. func checkAndroidBpContents(expected string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { - info.r.t.Helper() + info.r.Helper() info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents) } } +// Check that the snapshot's unversioned generated Android.bp is correct. +// +// This func should be used to check the general snapshot generation code. +// +// Both the expected and actual string are both trimmed before comparing. +func checkUnversionedAndroidBpContents(expected string) snapshotBuildInfoChecker { + return func(info *snapshotBuildInfo) { + info.r.Helper() + info.r.AssertTrimmedStringEquals("unversioned Android.bp contents do not match", expected, info.androidUnversionedBpContents) + } +} + +// Check that the snapshot's versioned generated Android.bp is correct. +// +// This func should only be used to check the version specific snapshot generation code, +// i.e. the encoding of version into module names and the generation of the _snapshot module. The +// general snapshot generation code should be checked using the checkUnversionedAndroidBpContents() +// func. +// +// Both the expected and actual string are both trimmed before comparing. +func checkVersionedAndroidBpContents(expected string) snapshotBuildInfoChecker { + return func(info *snapshotBuildInfo) { + info.r.Helper() + info.r.AssertTrimmedStringEquals("versioned Android.bp contents do not match", expected, info.androidVersionedBpContents) + } +} + // Check that the snapshot's copy rules are correct. // // The copy rules are formatted as <src> -> <dest>, one per line and then compared @@ -372,14 +349,14 @@ func checkAndroidBpContents(expected string) snapshotBuildInfoChecker { // before comparing. func checkAllCopyRules(expected string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { - info.r.t.Helper() + info.r.Helper() info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules) } } func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { - info.r.t.Helper() + info.r.Helper() info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.otherCopyRules) } } @@ -387,9 +364,9 @@ func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker { // Check that the specified paths match the list of zips to merge with the intermediate zip. func checkMergeZips(expected ...string) snapshotBuildInfoChecker { return func(info *snapshotBuildInfo) { - info.r.t.Helper() + info.r.Helper() if info.intermediateZip == "" { - info.r.t.Errorf("No intermediate zip file was created") + info.r.Errorf("No intermediate zip file was created") } info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips) @@ -407,6 +384,12 @@ type snapshotBuildInfo struct { // The contents of the generated Android.bp file androidBpContents string + // The contents of the unversioned Android.bp file + androidUnversionedBpContents string + + // The contents of the versioned Android.bp file + androidVersionedBpContents string + // The paths, relative to the snapshot root, of all files and directories copied into the // snapshot. snapshotContents []string diff --git a/sdk/update.go b/sdk/update.go index 377aaae76..828c7b67d 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -572,12 +572,20 @@ func (t pruneEmptySetTransformer) transformPropertySetAfterContents(name string, } func generateBpContents(contents *generatedContents, bpFile *bpFile) { + generateFilteredBpContents(contents, bpFile, func(*bpModule) bool { + return true + }) +} + +func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) { contents.Printfln("// This is auto-generated. DO NOT EDIT.") for _, bpModule := range bpFile.order { - contents.Printfln("") - contents.Printfln("%s {", bpModule.moduleType) - outputPropertySet(contents, bpModule.bpPropertySet) - contents.Printfln("}") + if moduleFilter(bpModule) { + contents.Printfln("") + contents.Printfln("%s {", bpModule.moduleType) + outputPropertySet(contents, bpModule.bpPropertySet) + contents.Printfln("}") + } } } @@ -639,6 +647,22 @@ func (s *sdk) GetAndroidBpContentsForTests() string { return contents.content.String() } +func (s *sdk) GetUnversionedAndroidBpContentsForTests() string { + contents := &generatedContents{} + generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool { + return !strings.Contains(module.properties["name"].(string), "@") + }) + return contents.content.String() +} + +func (s *sdk) GetVersionedAndroidBpContentsForTests() string { + contents := &generatedContents{} + generateFilteredBpContents(contents, s.builderForTests.bpFile, func(module *bpModule) bool { + return strings.Contains(module.properties["name"].(string), "@") + }) + return contents.content.String() +} + type snapshotBuilder struct { ctx android.ModuleContext sdk *sdk diff --git a/sh/sh_binary.go b/sh/sh_binary.go index 58f8cf69e..031cd4717 100644 --- a/sh/sh_binary.go +++ b/sh/sh_binary.go @@ -84,9 +84,6 @@ type shBinaryProperties struct { // Make this module available when building for recovery. Recovery_available *bool - - // Properties for Bazel migration purposes. - bazel.Properties } type TestProperties struct { @@ -132,6 +129,7 @@ type TestProperties struct { type ShBinary struct { android.ModuleBase + android.BazelModuleBase properties shBinaryProperties @@ -427,6 +425,7 @@ func (s *ShTest) AndroidMkEntries() []android.AndroidMkEntries { func InitShBinaryModule(s *ShBinary) { s.AddProperties(&s.properties) + android.InitBazelModule(s) } // sh_binary is for a shell script or batch file to be installed as an @@ -504,7 +503,7 @@ func BazelShBinaryFactory() android.Module { func ShBinaryBp2Build(ctx android.TopDownMutatorContext) { m, ok := ctx.Module().(*ShBinary) - if !ok || !m.properties.Bazel_module.Bp2build_available { + if !ok || !m.ConvertWithBp2build() { return } @@ -514,9 +513,11 @@ func ShBinaryBp2Build(ctx android.TopDownMutatorContext) { Srcs: srcs, } - props := bazel.NewBazelTargetModuleProperties(m.Name(), "sh_binary", "") + props := bazel.BazelTargetModuleProperties{ + Rule_class: "sh_binary", + } - ctx.CreateBazelTargetModule(BazelShBinaryFactory, props, attrs) + ctx.CreateBazelTargetModule(BazelShBinaryFactory, m.Name(), props, attrs) } func (m *bazelShBinary) Name() string { diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go index fb7ab1358..f48f7fb29 100644 --- a/sh/sh_binary_test.go +++ b/sh/sh_binary_test.go @@ -3,6 +3,7 @@ package sh import ( "io/ioutil" "os" + "path" "path/filepath" "reflect" "testing" @@ -73,7 +74,8 @@ func TestShTestSubDir(t *testing.T) { entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0] - expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo_test" + expectedPath := path.Join(buildDir, + "../target/product/test_device/data/nativetest64/foo_test") actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0] if expectedPath != actualPath { t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath) @@ -97,7 +99,8 @@ func TestShTest(t *testing.T) { entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0] - expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo" + expectedPath := path.Join(buildDir, + "../target/product/test_device/data/nativetest64/foo") actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0] if expectedPath != actualPath { t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath) diff --git a/shared/Android.bp b/shared/Android.bp index 5aa9d54f7..deb17f8f6 100644 --- a/shared/Android.bp +++ b/shared/Android.bp @@ -6,7 +6,12 @@ bootstrap_go_package { name: "soong-shared", pkgPath: "android/soong/shared", srcs: [ + "env.go", "paths.go", + "debug.go", + ], + testSrcs: [ + "paths_test.go", ], deps: [ "soong-bazel", diff --git a/shared/debug.go b/shared/debug.go new file mode 100644 index 000000000..5392f2b79 --- /dev/null +++ b/shared/debug.go @@ -0,0 +1,69 @@ +package shared + +import ( + "fmt" + "os" + "os/exec" + "strings" + "syscall" +) + +var ( + isDebugging bool +) + +// Finds the Delve binary to use. Either uses the SOONG_DELVE_PATH environment +// variable or if that is unset, looks at $PATH. +func ResolveDelveBinary() string { + result := os.Getenv("SOONG_DELVE_PATH") + if result == "" { + result, _ = exec.LookPath("dlv") + } + + return result +} + +// Returns whether the current process is running under Delve due to +// ReexecWithDelveMaybe(). +func IsDebugging() bool { + return isDebugging +} + +// Re-executes the binary in question under the control of Delve when +// delveListen is not the empty string. delvePath gives the path to the Delve. +func ReexecWithDelveMaybe(delveListen, delvePath string) { + isDebugging = os.Getenv("SOONG_DELVE_REEXECUTED") == "true" + if isDebugging || delveListen == "" { + return + } + + if delvePath == "" { + fmt.Fprintln(os.Stderr, "Delve debugging requested but failed to find dlv") + os.Exit(1) + } + + soongDelveEnv := []string{} + for _, env := range os.Environ() { + idx := strings.IndexRune(env, '=') + if idx != -1 { + soongDelveEnv = append(soongDelveEnv, env) + } + } + + soongDelveEnv = append(soongDelveEnv, "SOONG_DELVE_REEXECUTED=true") + + dlvArgv := []string{ + delvePath, + "--listen=:" + delveListen, + "--headless=true", + "--api-version=2", + "exec", + os.Args[0], + "--", + } + + dlvArgv = append(dlvArgv, os.Args[1:]...) + syscall.Exec(delvePath, dlvArgv, soongDelveEnv) + fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve") + os.Exit(1) +} diff --git a/env/env.go b/shared/env.go index 735a38aa4..152729ba0 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)) } @@ -91,6 +91,28 @@ func StaleEnvFile(filepath string) (bool, error) { return false, nil } +// Deserializes and environment serialized by EnvFileContents() and returns it +// as a map[string]string. +func EnvFromFile(envFile string) (map[string]string, error) { + result := make(map[string]string) + data, err := ioutil.ReadFile(envFile) + if err != nil { + return result, err + } + + var contents envFileData + err = json.Unmarshal(data, &contents) + if err != nil { + return result, err + } + + for _, entry := range contents { + result[entry.Key] = entry.Value + } + + return result, nil +} + // Implements sort.Interface so that we can use sort.Sort on envFileData arrays. func (e envFileData) Len() int { return len(e) diff --git a/shared/paths.go b/shared/paths.go index 1b9ff6098..fca8b4c15 100644 --- a/shared/paths.go +++ b/shared/paths.go @@ -30,6 +30,21 @@ type SharedPaths interface { BazelMetricsDir() string } +// Joins the path strings in the argument list, taking absolute paths into +// account. That is, if one of the strings is an absolute path, the ones before +// are ignored. +func JoinPath(base string, rest ...string) string { + result := base + for _, next := range rest { + if filepath.IsAbs(next) { + result = next + } else { + result = filepath.Join(result, next) + } + } + return result +} + // Given the out directory, returns the root of the temp directory (to be cleared at the start of each execution of Soong) func TempDirForOutDir(outDir string) (tempPath string) { return filepath.Join(outDir, ".temp") diff --git a/cmd/soong_env/Android.bp b/shared/paths_test.go index ad717d0b1..018d55f34 100644 --- a/cmd/soong_env/Android.bp +++ b/shared/paths_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2021 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. @@ -12,17 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], +package shared + +import ( + "testing" +) + +func assertEqual(t *testing.T, expected, actual string) { + t.Helper() + if expected != actual { + t.Errorf("expected %q != got %q", expected, actual) + } } -bootstrap_go_binary { - name: "soong_env", - deps: [ - "soong-env", - ], - srcs: [ - "soong_env.go", - ], - default: true, +func TestJoinPath(t *testing.T) { + assertEqual(t, "/a/b", JoinPath("c/d", "/a/b")) + assertEqual(t, "a/b", JoinPath("a", "b")) + assertEqual(t, "/a/b", JoinPath("x", "/a", "b")) } diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go index 2ccce1524..892a16c8d 100644 --- a/sysprop/sysprop_library.go +++ b/sysprop/sysprop_library.go @@ -37,9 +37,10 @@ type dependencyTag struct { } type syspropGenProperties struct { - Srcs []string `android:"path"` - Scope string - Name *string + Srcs []string `android:"path"` + Scope string + Name *string + Check_api *string } type syspropJavaGenRule struct { @@ -68,10 +69,6 @@ var ( func init() { pctx.HostBinToolVariable("soongZipCmd", "soong_zip") pctx.HostBinToolVariable("syspropJavaCmd", "sysprop_java") - - android.PreArchMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel() - }) } // syspropJavaGenRule module generates srcjar containing generated java APIs. @@ -103,6 +100,12 @@ func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleConte } } +func (g *syspropJavaGenRule) DepsMutator(ctx android.BottomUpMutatorContext) { + // Add a dependency from the stubs to sysprop library so that the generator rule can depend on + // the check API rule of the sysprop library. + ctx.AddFarVariationDependencies(nil, nil, proptools.String(g.properties.Check_api)) +} + func (g *syspropJavaGenRule) OutputFiles(tag string) (android.Paths, error) { switch tag { case "": @@ -157,14 +160,17 @@ type syspropLibraryProperties struct { // If set to true, build a variant of the module for the host. Defaults to false. Host_supported *bool - // Whether public stub exists or not. - Public_stub *bool `blueprint:"mutated"` - Cpp struct { // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). // Forwarded to cc_library.min_sdk_version Min_sdk_version *string } + + Java struct { + // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). + // Forwarded to java_library.min_sdk_version + Min_sdk_version *string + } } var ( @@ -202,11 +208,8 @@ func (m *syspropLibrary) CcImplementationModuleName() string { return "lib" + m.BaseModuleName() } -func (m *syspropLibrary) JavaPublicStubName() string { - if proptools.Bool(m.properties.Public_stub) { - return m.BaseModuleName() + "_public" - } - return "" +func (m *syspropLibrary) javaPublicStubName() string { + return m.BaseModuleName() + "_public" } func (m *syspropLibrary) javaGenModuleName() string { @@ -221,10 +224,6 @@ func (m *syspropLibrary) BaseModuleName() string { return m.ModuleBase.Name() } -func (m *syspropLibrary) HasPublicStub() bool { - return proptools.Bool(m.properties.Public_stub) -} - func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath { return m.currentApiFile } @@ -399,16 +398,19 @@ type ccLibraryProperties struct { } type javaLibraryProperties struct { - Name *string - Srcs []string - Soc_specific *bool - Device_specific *bool - Product_specific *bool - Required []string - Sdk_version *string - Installable *bool - Libs []string - Stem *string + Name *string + Srcs []string + Soc_specific *bool + Device_specific *bool + Product_specific *bool + Required []string + Sdk_version *string + Installable *bool + Libs []string + Stem *string + SyspropPublicStub string + Apex_available []string + Min_sdk_version *string } func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { @@ -490,35 +492,44 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { // Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed // to Java implementation library. ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ - Srcs: m.properties.Srcs, - Scope: scope, - Name: proptools.StringPtr(m.javaGenModuleName()), - }) - - ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ - Name: proptools.StringPtr(m.BaseModuleName()), - Srcs: []string{":" + m.javaGenModuleName()}, - Soc_specific: proptools.BoolPtr(ctx.SocSpecific()), - Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()), - Product_specific: proptools.BoolPtr(ctx.ProductSpecific()), - Installable: m.properties.Installable, - Sdk_version: proptools.StringPtr("core_current"), - Libs: []string{javaSyspropStub}, + Srcs: m.properties.Srcs, + Scope: scope, + Name: proptools.StringPtr(m.javaGenModuleName()), + Check_api: proptools.StringPtr(ctx.ModuleName()), }) // if platform sysprop_library is installed in /system or /system-ext, we regard it as an API // and allow any modules (even from different partition) to link against the sysprop_library. // To do that, we create a public stub and expose it to modules with sdk_version: system_*. + var publicStub string if isOwnerPlatform && installedInSystem { - m.properties.Public_stub = proptools.BoolPtr(true) + publicStub = m.javaPublicStubName() + } + + ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ + Name: proptools.StringPtr(m.BaseModuleName()), + Srcs: []string{":" + m.javaGenModuleName()}, + Soc_specific: proptools.BoolPtr(ctx.SocSpecific()), + Device_specific: proptools.BoolPtr(ctx.DeviceSpecific()), + Product_specific: proptools.BoolPtr(ctx.ProductSpecific()), + Installable: m.properties.Installable, + Sdk_version: proptools.StringPtr("core_current"), + Libs: []string{javaSyspropStub}, + SyspropPublicStub: publicStub, + Apex_available: m.ApexProperties.Apex_available, + Min_sdk_version: m.properties.Java.Min_sdk_version, + }) + + if publicStub != "" { ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{ - Srcs: m.properties.Srcs, - Scope: "public", - Name: proptools.StringPtr(m.javaGenPublicStubName()), + Srcs: m.properties.Srcs, + Scope: "public", + Name: proptools.StringPtr(m.javaGenPublicStubName()), + Check_api: proptools.StringPtr(ctx.ModuleName()), }) ctx.CreateModule(java.LibraryFactory, &javaLibraryProperties{ - Name: proptools.StringPtr(m.JavaPublicStubName()), + Name: proptools.StringPtr(publicStub), Srcs: []string{":" + m.javaGenPublicStubName()}, Installable: proptools.BoolPtr(false), Sdk_version: proptools.StringPtr("core_current"), @@ -537,15 +548,3 @@ func syspropLibraryHook(ctx android.LoadHookContext, m *syspropLibrary) { *libraries = append(*libraries, "//"+ctx.ModuleDir()+":"+ctx.ModuleName()) } } - -// syspropDepsMutator adds dependencies from java implementation library to sysprop library. -// java implementation library then depends on check API rule of sysprop library. -func syspropDepsMutator(ctx android.BottomUpMutatorContext) { - if m, ok := ctx.Module().(*syspropLibrary); ok { - ctx.AddReverseDependency(m, nil, m.javaGenModuleName()) - - if proptools.Bool(m.properties.Public_stub) { - ctx.AddReverseDependency(m, nil, m.javaGenPublicStubName()) - } - } -} diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go index 5cb9e64ee..fde41d67e 100644 --- a/sysprop/sysprop_test.go +++ b/sysprop/sysprop_test.go @@ -26,7 +26,6 @@ import ( "strings" "testing" - "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -61,16 +60,10 @@ func testContext(config android.Config) *android.TestContext { java.RegisterRequiredBuildComponentsForTest(ctx) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) - ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("sysprop_deps", syspropDepsMutator).Parallel() - }) android.RegisterPrebuiltMutators(ctx) cc.RegisterRequiredBuildComponentsForTest(ctx) - ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("sysprop_java", java.SyspropMutator).Parallel() - }) ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory) @@ -88,6 +81,51 @@ func run(t *testing.T, ctx *android.TestContext, config android.Config) { } func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config { + bp += ` + cc_library { + name: "libbase", + host_supported: true, + } + + cc_library_headers { + name: "libbase_headers", + vendor_available: true, + recovery_available: true, + } + + cc_library { + name: "liblog", + no_libcrt: true, + nocrt: true, + system_shared_libs: [], + recovery_available: true, + host_supported: true, + llndk_stubs: "liblog.llndk", + } + + llndk_library { + name: "liblog.llndk", + symbol_file: "", + } + + java_library { + name: "sysprop-library-stub-platform", + sdk_version: "core_current", + } + + java_library { + name: "sysprop-library-stub-vendor", + soc_specific: true, + sdk_version: "core_current", + } + + java_library { + name: "sysprop-library-stub-product", + product_specific: true, + sdk_version: "core_current", + } + ` + bp += cc.GatherRequiredDepsForTest(android.Android) mockFS := map[string][]byte{ @@ -257,54 +295,11 @@ func TestSyspropLibrary(t *testing.T) { static_libs: ["sysprop-platform", "sysprop-vendor"], } - cc_library { - name: "libbase", - host_supported: true, - } - - cc_library_headers { - name: "libbase_headers", - vendor_available: true, - recovery_available: true, - } - - cc_library { - name: "liblog", - no_libcrt: true, - nocrt: true, - system_shared_libs: [], - recovery_available: true, - host_supported: true, - llndk_stubs: "liblog.llndk", - } - cc_binary_host { name: "hostbin", static_libs: ["sysprop-platform"], } - - llndk_library { - name: "liblog.llndk", - symbol_file: "", - } - - java_library { - name: "sysprop-library-stub-platform", - sdk_version: "core_current", - } - - java_library { - name: "sysprop-library-stub-vendor", - soc_specific: true, - sdk_version: "core_current", - } - - java_library { - name: "sysprop-library-stub-product", - product_specific: true, - sdk_version: "core_current", - } - `) + `) // Check for generated cc_library for _, variant := range []string{ @@ -392,15 +387,68 @@ func TestSyspropLibrary(t *testing.T) { } // Java modules linking against system API should use public stub - javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common") - publicStubFound := false - ctx.VisitDirectDeps(javaSystemApiClient.Module(), func(dep blueprint.Module) { - if dep.Name() == "sysprop-platform_public" { - publicStubFound = true + javaSystemApiClient := ctx.ModuleForTests("java-platform", "android_common").Rule("javac") + syspropPlatformPublic := ctx.ModuleForTests("sysprop-platform_public", "android_common").Description("for turbine") + if g, w := javaSystemApiClient.Implicits.Strings(), syspropPlatformPublic.Output.String(); !android.InList(w, g) { + t.Errorf("system api client should use public stub %q, got %q", w, g) + } +} + +func TestApexAvailabilityIsForwarded(t *testing.T) { + ctx := test(t, ` + sysprop_library { + name: "sysprop-platform", + apex_available: ["//apex_available:platform"], + srcs: ["android/sysprop/PlatformProperties.sysprop"], + api_packages: ["android.sysprop"], + property_owner: "Platform", + } + `) + + expected := []string{"//apex_available:platform"} + + ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) + propFromCc := ccModule.ApexProperties.Apex_available + if !reflect.DeepEqual(propFromCc, expected) { + t.Errorf("apex_available not forwarded to cc module. expected %#v, got %#v", + expected, propFromCc) + } + + javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) + propFromJava := javaModule.ApexProperties.Apex_available + if !reflect.DeepEqual(propFromJava, expected) { + t.Errorf("apex_available not forwarded to java module. expected %#v, got %#v", + expected, propFromJava) + } +} + +func TestMinSdkVersionIsForwarded(t *testing.T) { + ctx := test(t, ` + sysprop_library { + name: "sysprop-platform", + srcs: ["android/sysprop/PlatformProperties.sysprop"], + api_packages: ["android.sysprop"], + property_owner: "Platform", + cpp: { + min_sdk_version: "29", + }, + java: { + min_sdk_version: "30", + }, } - }) - if !publicStubFound { - t.Errorf("system api client should use public stub") + `) + + ccModule := ctx.ModuleForTests("libsysprop-platform", "android_arm64_armv8-a_shared").Module().(*cc.Module) + propFromCc := proptools.String(ccModule.Properties.Min_sdk_version) + if propFromCc != "29" { + t.Errorf("min_sdk_version not forwarded to cc module. expected %#v, got %#v", + "29", propFromCc) } + javaModule := ctx.ModuleForTests("sysprop-platform", "android_common").Module().(*java.Library) + propFromJava := javaModule.MinSdkVersion() + if propFromJava != "30" { + t.Errorf("min_sdk_version not forwarded to java module. expected %#v, got %#v", + "30", propFromJava) + } } diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go index b1f8551ed..6ba497c3f 100644 --- a/ui/build/cleanbuild.go +++ b/ui/build/cleanbuild.go @@ -124,7 +124,7 @@ func installClean(ctx Context, config Config) { productOut("obj/PACKAGING"), productOut("ramdisk"), productOut("debug_ramdisk"), - productOut("vendor-ramdisk"), + productOut("vendor_ramdisk"), productOut("vendor_debug_ramdisk"), productOut("test_harness_ramdisk"), productOut("recovery"), diff --git a/ui/build/environment.go b/ui/build/environment.go index 6d8a28fdf..50d059f8d 100644 --- a/ui/build/environment.go +++ b/ui/build/environment.go @@ -33,6 +33,19 @@ func OsEnvironment() *Environment { return &env } +// Returns a copy of the environment as a map[string]string. +func (e *Environment) AsMap() map[string]string { + result := make(map[string]string) + + for _, envVar := range *e { + if k, v, ok := decodeKeyValue(envVar); ok { + result[k] = v + } + } + + return result +} + // Get returns the value associated with the key, and whether it exists. // It's equivalent to the os.LookupEnv function, but with this copy of the // Environment. 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..5f4a203f7 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -19,7 +19,8 @@ import ( "os" "path/filepath" "strconv" - "strings" + + "android/soong/shared" soong_metrics_proto "android/soong/ui/metrics/metrics_proto" @@ -30,6 +31,15 @@ import ( "android/soong/ui/status" ) +func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error { + data, err := shared.EnvFileContents(envDeps) + if err != nil { + return err + } + + return ioutil.WriteFile(envFile, data, 0644) +} + // This uses Android.bp files and various tools to generate <builddir>/build.ninja. // // However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build() @@ -47,6 +57,12 @@ func runSoong(ctx Context, config Config) { ctx.BeginTrace(metrics.RunSoong, "soong") defer ctx.EndTrace() + // We have two environment files: .available is the one with every variable, + // .used with the ones that were actually used. The latter is used to + // determine whether Soong needs to be re-run since why re-run it if only + // unused variables were changed? + envFile := filepath.Join(config.SoongOutDir(), "soong.environment.available") + // Use an anonymous inline function for tracing purposes (this pattern is used several times below). func() { ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap") @@ -61,6 +77,7 @@ func runSoong(ctx Context, config Config) { } cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...) + cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint") cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash") cmd.Environment.Set("BUILDDIR", config.SoongOutDir()) @@ -74,34 +91,36 @@ func runSoong(ctx Context, config Config) { cmd.RunAndPrintOrFatal() }() + soongBuildEnv := config.Environment().Copy() + soongBuildEnv.Set("TOP", os.Getenv("TOP")) + // These two dependencies are read from bootstrap.go, but also need to be here + // so that soong_build can declare a dependency on them + soongBuildEnv.Set("SOONG_DELVE", os.Getenv("SOONG_DELVE")) + soongBuildEnv.Set("SOONG_DELVE_PATH", os.Getenv("SOONG_DELVE_PATH")) + soongBuildEnv.Set("SOONG_OUTDIR", config.SoongOutDir()) + // For Bazel mixed builds. + soongBuildEnv.Set("BAZEL_PATH", "./tools/bazel") + soongBuildEnv.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome")) + soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output")) + soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, ".")) + soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir()) + + err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap()) + if err != nil { + ctx.Fatalf("failed to write environment file %s: %s", envFile, err) + } + func() { ctx.BeginTrace(metrics.RunSoong, "environment check") 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) + envFile := filepath.Join(config.SoongOutDir(), "soong.environment.used") + getenv := func(k string) string { + v, _ := config.Environment().Get(k) + return v + } + if stale, _ := shared.StaleEnvFile(envFile, getenv); stale { + os.Remove(envFile) } }() @@ -151,14 +170,28 @@ func runSoong(ctx Context, config Config) { "--frontend_file", fifo, "-f", filepath.Join(config.SoongOutDir(), file)) - // For Bazel mixed builds. - cmd.Environment.Set("BAZEL_PATH", "./tools/bazel") - cmd.Environment.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome")) - cmd.Environment.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output")) - cmd.Environment.Set("BAZEL_WORKSPACE", absPath(ctx, ".")) - cmd.Environment.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir()) + var ninjaEnv Environment + + // This is currently how the command line to invoke soong_build finds the + // root of the source tree and the output root + ninjaEnv.Set("TOP", os.Getenv("TOP")) + ninjaEnv.Set("SOONG_OUTDIR", config.SoongOutDir()) + + // Needed for NonHermeticHostSystemTool() and that, only in tests. We should + // probably find a better way of running tests other than making $PATH + // available also to production builds. Note that this is not get same as + // os.Getenv("PATH"): config.Environment() contains the $PATH that redirects + // every binary through the path interposer. + configPath, _ := config.Environment().Get("PATH") + ninjaEnv.Set("PATH", configPath) + + // For debugging + if os.Getenv("SOONG_DELVE") != "" { + // SOONG_DELVE is already in cmd.Environment + ninjaEnv.Set("SOONG_DELVE_PATH", shared.ResolveDelveBinary()) + } - cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true") + cmd.Environment = &ninjaEnv cmd.Sandbox = soongSandbox cmd.RunAndStreamOrFatal() } diff --git a/vnames.go.json b/vnames.go.json deleted file mode 100644 index 7ce2d4b49..000000000 --- a/vnames.go.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "pattern": "(.*)", - "vname": { - "corpus": "android.googlesource.com/platform/superproject", - "path": "@1@" - } - } -] diff --git a/vnames.json b/vnames.json index f9d3adc8b..096260f6a 100644 --- a/vnames.json +++ b/vnames.json @@ -2,7 +2,6 @@ { "pattern": "out/(.*)", "vname": { - "corpus": "android.googlesource.com/platform/superproject", "root": "out", "path": "@1@" } @@ -10,9 +9,7 @@ { "pattern": "(.*)", "vname": { - "corpus": "android.googlesource.com/platform/superproject", "path": "@1@" } } ] - |