diff options
51 files changed, 1865 insertions, 754 deletions
diff --git a/android/bazel.go b/android/bazel.go index b9d707090..992d8aa27 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -218,11 +218,7 @@ var ( // Per-module denylist to opt modules out of mixed builds. Such modules will // still be generated via bp2build. - mixedBuildsDisabledList = []string{ - "libc", // b/190211183, missing libbionic_Slibdl_Sliblibdl_Ubp2build_Ucc_Ulibrary_Ushared.so - "libdl", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so - "libdl_android", // b/190211183, missing libbionic_Slinker_Slibld-android_Ubp2build_Ucc_Ulibrary_Ushared.so - } + mixedBuildsDisabledList = []string{} // Used for quicker lookups bp2buildModuleDoNotConvert = map[string]bool{} diff --git a/android/bazel_handler.go b/android/bazel_handler.go index f906c8add..b11b4743e 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -542,10 +542,13 @@ def get_arch(target): platform_name = build_options(target)["//command_line_option:platforms"][0].name if platform_name == "host": return "HOST" - elif not platform_name.startswith("android_"): - fail("expected platform name of the form 'android_<arch>', but was " + str(platforms)) + elif platform_name.startswith("android_"): + return platform_name[len("android_"):] + elif platform_name.startswith("linux_"): + return platform_name[len("linux_"):] + else: + fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms)) return "UNKNOWN" - return platform_name[len("android_"):] def format(target): id_string = str(target.label) + "|" + get_arch(target) @@ -742,8 +745,17 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { } rule := NewRuleBuilder(pctx, ctx) cmd := rule.Command() - cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s", - ctx.Config().BazelContext.OutputBase(), buildStatement.Command)) + + // cd into Bazel's execution root, which is the action cwd. + cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && ", ctx.Config().BazelContext.OutputBase())) + + for _, pair := range buildStatement.Env { + // Set per-action env variables, if any. + cmd.Flag(pair.Key + "=" + pair.Value) + } + + // The actual Bazel action. + cmd.Text(" " + buildStatement.Command) for _, outputPath := range buildStatement.OutputPaths { cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath)) @@ -756,6 +768,10 @@ func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) { cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile)) } + for _, symlinkPath := range buildStatement.SymlinkPaths { + cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath)) + } + // This is required to silence warnings pertaining to unexpected timestamps. Particularly, // some Bazel builtins (such as files in the bazel_tools directory) have far-future // timestamps. Without restat, Ninja would emit warnings that the input files of a diff --git a/android/bazel_paths.go b/android/bazel_paths.go index f93fe2b5e..f74fed13f 100644 --- a/android/bazel_paths.go +++ b/android/bazel_paths.go @@ -83,6 +83,36 @@ type BazelConversionPathContext interface { // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the // module within the given ctx. func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { + return bazelLabelForModuleDeps(ctx, modules, false) +} + +// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>" +// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the +// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so +// they can be handled as whole static libraries. +func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList { + return bazelLabelForModuleDeps(ctx, modules, true) +} + +// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the +// list), and excludes (modules to exclude from the list). Both of these should contain references +// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which +// corresponds to dependencies on the module within the given ctx, and the excluded dependencies. +func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { + return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false) +} + +// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in +// the list), and excludes (modules to exclude from the list). Both of these should contain +// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label +// list which corresponds to dependencies on the module within the given ctx, and the excluded +// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as +// whole static libraries. +func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { + return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true) +} + +func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList { var labels bazel.LabelList for _, module := range modules { bpText := module @@ -90,7 +120,7 @@ func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) b module = ":" + module } if m, t := SrcIsModuleWithTag(module); m != "" { - l := getOtherModuleLabel(ctx, m, t) + l := getOtherModuleLabel(ctx, m, t, isWholeLibs) l.OriginalModuleName = bpText labels.Includes = append(labels.Includes, l) } else { @@ -100,16 +130,12 @@ func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) b return labels } -// BazelLabelForModuleDeps expects two lists: modules (containing modules to include in the list), -// and excludes (modules to exclude from the list). Both of these should contain references to other -// modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which corresponds -// to dependencies on the module within the given ctx, and the excluded dependencies. -func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList { - moduleLabels := BazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes)) +func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList { + moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs) if len(excludes) == 0 { return moduleLabels } - excludeLabels := BazelLabelForModuleDeps(ctx, excludes) + excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs) return bazel.LabelList{ Includes: moduleLabels.Includes, Excludes: excludeLabels.Includes, @@ -273,7 +299,7 @@ func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes for _, p := range paths { if m, tag := SrcIsModuleWithTag(p); m != "" { - l := getOtherModuleLabel(ctx, m, tag) + l := getOtherModuleLabel(ctx, m, tag, false) if !InList(l.Label, expandedExcludes) { l.OriginalModuleName = fmt.Sprintf(":%s", m) labels.Includes = append(labels.Includes, l) @@ -304,7 +330,7 @@ func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes // getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the // module. The label will be relative to the current directory if appropriate. The dependency must // already be resolved by either deps mutator or path deps mutator. -func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label { +func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label { m, _ := ctx.GetDirectDep(dep) if m == nil { panic(fmt.Errorf(`Cannot get direct dep %q of %q. @@ -313,6 +339,11 @@ func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel. } otherLabel := bazelModuleLabel(ctx, m, tag) label := bazelModuleLabel(ctx, ctx.Module(), "") + if isWholeLibs { + if m, ok := m.(Module); ok && IsModulePrebuilt(m) { + otherLabel += "_alwayslink" + } + } if samePackage(label, otherLabel) { otherLabel = bazelShortLabel(otherLabel) } diff --git a/android/config.go b/android/config.go index da78c7a12..ed90c3181 100644 --- a/android/config.go +++ b/android/config.go @@ -1690,6 +1690,16 @@ func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) Writable return paths } +// BuildPathsByModule returns a map from module name to build paths based on the given directory +// prefix. +func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath { + paths := map[string]WritablePath{} + for _, jar := range l.jars { + paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar") + } + return paths +} + // UnmarshalJSON converts JSON configuration from raw bytes into a // ConfiguredJarList structure. func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error { diff --git a/android/image.go b/android/image.go index 66101be02..bc6b8cd86 100644 --- a/android/image.go +++ b/android/image.go @@ -43,10 +43,9 @@ type ImageInterface interface { // its variation. ExtraImageVariations(ctx BaseModuleContext) []string - // SetImageVariation will be passed a newly created recovery variant of the module. ModuleBase implements - // SetImageVariation, most module types will not need to override it, and those that do must call the - // overridden method. Implementors of SetImageVariation must be careful to modify the module argument - // and not the receiver. + // SetImageVariation is called for each newly created image variant. The receiver is the original + // module, "variation" is the name of the newly created variant and "module" is the newly created + // variant itself. SetImageVariation(ctx BaseModuleContext, variation string, module Module) } diff --git a/android/testing.go b/android/testing.go index 191cb8d02..b36f62cce 100644 --- a/android/testing.go +++ b/android/testing.go @@ -713,9 +713,11 @@ func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) Testing func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) { var searchedRules []string - for _, p := range b.provider.BuildParamsForTests() { - searchedRules = append(searchedRules, p.Rule.String()) - if strings.Contains(p.Rule.String(), rule) { + buildParams := b.provider.BuildParamsForTests() + for _, p := range buildParams { + ruleAsString := p.Rule.String() + searchedRules = append(searchedRules, ruleAsString) + if strings.Contains(ruleAsString, rule) { return b.newTestingBuildParams(p), searchedRules } } @@ -725,7 +727,7 @@ func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuil func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams { p, searchRules := b.maybeBuildParamsFromRule(rule) if p.Rule == nil { - panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules)) + panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n"))) } return p } diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go index 5316d7b32..08616a90e 100644 --- a/androidmk/androidmk/android.go +++ b/androidmk/androidmk/android.go @@ -104,6 +104,7 @@ func init() { "LOCAL_NDK_STL_VARIANT": "stl", "LOCAL_JAR_MANIFEST": "manifest", "LOCAL_CERTIFICATE": "certificate", + "LOCAL_CERTIFICATE_LINEAGE": "lineage", "LOCAL_PACKAGE_NAME": "name", "LOCAL_MODULE_RELATIVE_PATH": "relative_install_path", "LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type", diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go index 439f45d0a..067dcba35 100644 --- a/androidmk/androidmk/androidmk_test.go +++ b/androidmk/androidmk/androidmk_test.go @@ -1463,6 +1463,22 @@ android_app { } `, }, + { + desc: "LOCAL_CERTIFICATE_LINEAGE", + in: ` +include $(CLEAR_VARS) +LOCAL_MODULE := foo +LOCAL_MODULE_TAGS := tests +LOCAL_CERTIFICATE_LINEAGE := lineage +include $(BUILD_PACKAGE) +`, + expected: ` +android_test { + name: "foo", + lineage: "lineage", +} +`, + }, } func TestEndToEnd(t *testing.T) { diff --git a/apex/apex.go b/apex/apex.go index 926085b4c..33b83c0bc 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -170,9 +170,10 @@ type apexBundleProperties struct { // Default is false. Ignore_system_library_special_case *bool - // Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only - // used in tests. - Test_only_no_hashtree *bool + // Whenever apex_payload.img of the APEX should include dm-verity hashtree. + // Default value is false. + // TODO(b/190621617): change default value to true. + Generate_hashtree *bool // Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only // used in tests. @@ -1317,9 +1318,9 @@ func (a *apexBundle) installable() bool { return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable)) } -// See the test_only_no_hashtree property -func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool { - return proptools.Bool(a.properties.Test_only_no_hashtree) +// See the generate_hashtree property +func (a *apexBundle) shouldGenerateHashtree() bool { + return proptools.BoolDefault(a.properties.Generate_hashtree, false) } // See the test_only_unsigned_payload property diff --git a/apex/apex_test.go b/apex/apex_test.go index 6a7c35c88..1bfe7e9ed 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4769,7 +4769,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { // prebuilt_apex module always depends on the prebuilt, and so it doesn't // find the dex boot jar in it. We either need to disable the source libfoo // or make the prebuilt libfoo preferred. - testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", preparer) + testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer) }) t.Run("prebuilt library preferred with source", func(t *testing.T) { diff --git a/apex/builder.go b/apex/builder.go index 021e499ed..24c049bcd 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -630,11 +630,7 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String())) } - if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() { - ctx.PropertyErrorf("test_only_no_hashtree", "not available") - return - } - if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled { + if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled { // Apexes which are supposed to be installed in builtin dirs(/system, etc) // don't need hashtree for activation. Therefore, by removing hashtree from // apex bundle (filesystem image in it, to be specific), we can save storage. diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index ce12f4635..729792663 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -15,6 +15,8 @@ package apex import ( + "fmt" + "strings" "testing" "android/soong/android" @@ -31,6 +33,139 @@ var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers( PrepareForTestWithApexBuildComponents, ) +func TestPlatformBootclasspath_Fragments(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithMyapex, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo"), + java.FixtureConfigureBootJars("myapex:bar"), + android.FixtureWithRootAndroidBp(` + platform_bootclasspath { + name: "platform-bootclasspath", + fragments: [ + { + apex: "myapex", + module:"bar-fragment", + }, + ], + hidden_api: { + unsupported: [ + "unsupported.txt", + ], + removed: [ + "removed.txt", + ], + max_target_r_low_priority: [ + "max-target-r-low-priority.txt", + ], + max_target_q: [ + "max-target-q.txt", + ], + max_target_p: [ + "max-target-p.txt", + ], + max_target_o_low_priority: [ + "max-target-o-low-priority.txt", + ], + blocked: [ + "blocked.txt", + ], + unsupported_packages: [ + "unsupported-packages.txt", + ], + }, + } + + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "bar-fragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + bootclasspath_fragment { + name: "bar-fragment", + contents: ["bar"], + apex_available: ["myapex"], + api: { + stub_libs: ["foo"], + }, + hidden_api: { + unsupported: [ + "bar-unsupported.txt", + ], + removed: [ + "bar-removed.txt", + ], + max_target_r_low_priority: [ + "bar-max-target-r-low-priority.txt", + ], + max_target_q: [ + "bar-max-target-q.txt", + ], + max_target_p: [ + "bar-max-target-p.txt", + ], + max_target_o_low_priority: [ + "bar-max-target-o-low-priority.txt", + ], + blocked: [ + "bar-blocked.txt", + ], + unsupported_packages: [ + "bar-unsupported-packages.txt", + ], + }, + } + + java_library { + name: "bar", + apex_available: ["myapex"], + srcs: ["a.java"], + system_modules: "none", + sdk_version: "none", + compile_dex: true, + permitted_packages: ["bar"], + } + + java_sdk_library { + name: "foo", + srcs: ["a.java"], + public: { + enabled: true, + }, + compile_dex: true, + } + `), + ).RunTest(t) + + pbcp := result.Module("platform-bootclasspath", "android_common") + info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo) + + for _, category := range java.HiddenAPIFlagFileCategories { + name := category.PropertyName + message := fmt.Sprintf("category %s", name) + filename := strings.ReplaceAll(name, "_", "-") + expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)} + android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category]) + } + + android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths) + android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths) + android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths) + android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths) + android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths) +} + func TestPlatformBootclasspathDependencies(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithPlatformBootclasspath, diff --git a/bazel/aquery.go b/bazel/aquery.go index d1119cb43..8741afbc5 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -73,12 +73,13 @@ type actionGraphContainer struct { // BuildStatement contains information to register a build statement corresponding (one to one) // with a Bazel action from Bazel's action graph. type BuildStatement struct { - Command string - Depfile *string - OutputPaths []string - InputPaths []string - Env []KeyValuePair - Mnemonic string + Command string + Depfile *string + OutputPaths []string + InputPaths []string + SymlinkPaths []string + Env []KeyValuePair + Mnemonic string } // A helper type for aquery processing which facilitates retrieval of path IDs from their @@ -234,10 +235,21 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { OutputPaths: outputPaths, InputPaths: inputPaths, Env: actionEntry.EnvironmentVariables, - Mnemonic: actionEntry.Mnemonic} - if len(actionEntry.Arguments) < 1 { + Mnemonic: actionEntry.Mnemonic, + } + + if isSymlinkAction(actionEntry) { + if len(inputPaths) != 1 || len(outputPaths) != 1 { + return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths) + } + out := outputPaths[0] + outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out)) + out = proptools.ShellEscapeIncludingSpaces(out) + in := proptools.ShellEscapeIncludingSpaces(inputPaths[0]) + buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -rsf %[3]s %[2]s", outDir, out, in) + buildStatement.SymlinkPaths = outputPaths[:] + } else if len(actionEntry.Arguments) < 1 { return nil, fmt.Errorf("received action with no command: [%v]", buildStatement) - continue } buildStatements = append(buildStatements, buildStatement) } @@ -245,9 +257,13 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { return buildStatements, nil } +func isSymlinkAction(a action) bool { + return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" +} + func shouldSkipAction(a action) bool { - // TODO(b/180945121): Handle symlink actions. - if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SolibSymlink" { + // TODO(b/180945121): Handle complex symlink actions. + if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" { return true } // Middleman actions are not handled like other actions; they are handled separately as a @@ -278,6 +294,9 @@ func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, return "", fmt.Errorf("undefined path fragment id %d", currId) } labels = append([]string{currFragment.Label}, labels...) + if currId == currFragment.ParentId { + return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment) + } currId = currFragment.ParentId } return filepath.Join(labels...), nil diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go index 7b40dcdda..43e41554e 100644 --- a/bazel/aquery_test.go +++ b/bazel/aquery_test.go @@ -805,17 +805,229 @@ func TestMiddlemenAction(t *testing.T) { } } +func TestSimpleSymlink(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 3 + }, { + "id": 2, + "pathFragmentId": 5 + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "Symlink", + "inputDepSetIds": [1], + "outputIds": [2], + "primaryOutputId": 2 + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1] + }], + "pathFragments": [{ + "id": 1, + "label": "one" + }, { + "id": 2, + "label": "file_subdir", + "parentId": 1 + }, { + "id": 3, + "label": "file", + "parentId": 2 + }, { + "id": 4, + "label": "symlink_subdir", + "parentId": 1 + }, { + "id": 5, + "label": "symlink", + "parentId": 4 + }] +}` + + actual, err := AqueryBuildStatements([]byte(inputString)) + + if err != nil { + t.Errorf("Unexpected error %q", err) + } + + expectedBuildStatements := []BuildStatement{ + BuildStatement{ + Command: "mkdir -p one/symlink_subdir && " + + "rm -f one/symlink_subdir/symlink && " + + "ln -rsf one/file_subdir/file one/symlink_subdir/symlink", + InputPaths: []string{"one/file_subdir/file"}, + OutputPaths: []string{"one/symlink_subdir/symlink"}, + SymlinkPaths: []string{"one/symlink_subdir/symlink"}, + Mnemonic: "Symlink", + }, + } + assertBuildStatements(t, actual, expectedBuildStatements) +} + +func TestSymlinkQuotesPaths(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 3 + }, { + "id": 2, + "pathFragmentId": 5 + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "SolibSymlink", + "inputDepSetIds": [1], + "outputIds": [2], + "primaryOutputId": 2 + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1] + }], + "pathFragments": [{ + "id": 1, + "label": "one" + }, { + "id": 2, + "label": "file subdir", + "parentId": 1 + }, { + "id": 3, + "label": "file", + "parentId": 2 + }, { + "id": 4, + "label": "symlink subdir", + "parentId": 1 + }, { + "id": 5, + "label": "symlink", + "parentId": 4 + }] +}` + + actual, err := AqueryBuildStatements([]byte(inputString)) + + if err != nil { + t.Errorf("Unexpected error %q", err) + } + + expectedBuildStatements := []BuildStatement{ + BuildStatement{ + Command: "mkdir -p 'one/symlink subdir' && " + + "rm -f 'one/symlink subdir/symlink' && " + + "ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'", + InputPaths: []string{"one/file subdir/file"}, + OutputPaths: []string{"one/symlink subdir/symlink"}, + SymlinkPaths: []string{"one/symlink subdir/symlink"}, + Mnemonic: "SolibSymlink", + }, + } + assertBuildStatements(t, actual, expectedBuildStatements) +} + +func TestSymlinkMultipleInputs(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 1 + }, { + "id": 2, + "pathFragmentId": 2 + }, { + "id": 3, + "pathFragmentId": 3 + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "Symlink", + "inputDepSetIds": [1], + "outputIds": [3], + "primaryOutputId": 3 + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1,2] + }], + "pathFragments": [{ + "id": 1, + "label": "file" + }, { + "id": 2, + "label": "other_file" + }, { + "id": 3, + "label": "symlink" + }] +}` + + _, err := AqueryBuildStatements([]byte(inputString)) + assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`) +} + +func TestSymlinkMultipleOutputs(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 1 + }, { + "id": 2, + "pathFragmentId": 2 + }, { + "id": 3, + "pathFragmentId": 3 + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "Symlink", + "inputDepSetIds": [1], + "outputIds": [2,3], + "primaryOutputId": 2 + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1] + }], + "pathFragments": [{ + "id": 1, + "label": "file" + }, { + "id": 2, + "label": "symlink" + }, { + "id": 3, + "label": "other_symlink" + }] +}` + + _, err := AqueryBuildStatements([]byte(inputString)) + assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`) +} + func assertError(t *testing.T, err error, expected string) { + t.Helper() if err == nil { t.Errorf("expected error '%s', but got no error", expected) } else if err.Error() != expected { - t.Errorf("expected error '%s', but got: %s", expected, err.Error()) + t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error()) } } // Asserts that the given actual build statements match the given expected build statements. // Build statement equivalence is determined using buildStatementEquals. func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) { + t.Helper() if len(expected) != len(actual) { t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v", len(expected), len(actual), expected, actual) @@ -852,6 +1064,12 @@ func buildStatementEquals(first BuildStatement, second BuildStatement) bool { if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) { return false } + if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) { + return false + } + if first.Depfile != second.Depfile { + return false + } return true } diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go index 92135c64a..52c6c2fd9 100644 --- a/bazel/cquery/request_type.go +++ b/bazel/cquery/request_type.go @@ -20,6 +20,10 @@ type CcInfo struct { // be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles, // but general cc_library will also have dynamic libraries in output files). RootStaticArchives []string + // Dynamic libraries (.so files) created by the current target. These will + // be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles, + // but general cc_library will also have dynamic libraries in output files). + RootDynamicLibraries []string } type getOutputFilesRequestType struct{} @@ -86,13 +90,21 @@ for linker_input in linker_inputs: if linker_input.owner == target.label: rootStaticArchives.append(library.static_library.path) +rootDynamicLibraries = [] + +if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target): + shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"] + for lib in shared_info.linker_input.libraries: + rootDynamicLibraries += [lib.dynamic_library.path] + returns = [ outputFiles, staticLibraries, ccObjectFiles, includes, system_includes, - rootStaticArchives + rootStaticArchives, + rootDynamicLibraries ] return "|".join([", ".join(r) for r in returns])` @@ -106,7 +118,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { var ccObjects []string splitString := strings.Split(rawString, "|") - if expectedLen := 6; len(splitString) != expectedLen { + if expectedLen := 7; len(splitString) != expectedLen { return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString) } outputFilesString := splitString[0] @@ -118,6 +130,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { includes := splitOrEmpty(splitString[3], ", ") systemIncludes := splitOrEmpty(splitString[4], ", ") rootStaticArchives := splitOrEmpty(splitString[5], ", ") + rootDynamicLibraries := splitOrEmpty(splitString[6], ", ") return CcInfo{ OutputFiles: outputFiles, CcObjectFiles: ccObjects, @@ -125,6 +138,7 @@ func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) { Includes: includes, SystemIncludes: systemIncludes, RootStaticArchives: rootStaticArchives, + RootDynamicLibraries: rootDynamicLibraries, }, nil } diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go index 602849e7f..035544e9f 100644 --- a/bazel/cquery/request_type_test.go +++ b/bazel/cquery/request_type_test.go @@ -46,7 +46,7 @@ func TestGetCcInfoParseResults(t *testing.T) { }{ { description: "no result", - input: "|||||", + input: "||||||", expectedOutput: CcInfo{ OutputFiles: []string{}, CcObjectFiles: []string{}, @@ -54,11 +54,12 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{}, SystemIncludes: []string{}, RootStaticArchives: []string{}, + RootDynamicLibraries: []string{}, }, }, { description: "only output", - input: "test|||||", + input: "test||||||", expectedOutput: CcInfo{ OutputFiles: []string{"test"}, CcObjectFiles: []string{}, @@ -66,11 +67,12 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{}, SystemIncludes: []string{}, RootStaticArchives: []string{}, + RootDynamicLibraries: []string{}, }, }, { description: "all items set", - input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1", + input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1", expectedOutput: CcInfo{ OutputFiles: []string{"out1", "out2"}, CcObjectFiles: []string{"object1", "object2"}, @@ -78,19 +80,20 @@ func TestGetCcInfoParseResults(t *testing.T) { Includes: []string{".", "dir/subdir"}, SystemIncludes: []string{"system/dir", "system/other/dir"}, RootStaticArchives: []string{"rootstaticarchive1"}, + RootDynamicLibraries: []string{"rootdynamiclibrary1"}, }, }, { description: "too few result splits", input: "|", expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, []string{"", ""}), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}), }, { description: "too many result splits", input: strings.Repeat("|", 8), expectedOutput: CcInfo{}, - expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, make([]string, 9)), + expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)), }, } for _, tc := range testCases { diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index 4de5aae18..a1e04248e 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -488,6 +488,9 @@ func prettyPrint(propertyValue reflect.Value, indent int) (string, error) { ret = "{\n" // Sort and print the struct props by the key. structProps := extractStructProperties(propertyValue, indent) + if len(structProps) == 0 { + return "", nil + } for _, k := range android.SortedStringKeys(structProps) { ret += makeIndent(indent + 1) ret += fmt.Sprintf("%q: %s,\n", k, structProps[k]) diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index c464cec87..285677a1c 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -49,6 +49,7 @@ func registerCcLibraryModuleTypes(ctx android.RegistrationContext) { cc.RegisterCCBuildComponents(ctx) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) + ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory) ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) } @@ -384,19 +385,69 @@ cc_library { name: "shared_dep_for_both" } "-I$(BINDIR)/foo/bar", ], dynamic_deps = [":shared_dep_for_both"], - dynamic_deps_for_shared = [":shared_dep_for_shared"], - dynamic_deps_for_static = [":shared_dep_for_static"], implementation_deps = [":static_dep_for_both"], - shared_copts = ["sharedflag"], - shared_srcs = ["sharedonly.cpp"], + shared = { + "copts": ["sharedflag"], + "dynamic_deps": [":shared_dep_for_shared"], + "srcs": ["sharedonly.cpp"], + "static_deps": [":static_dep_for_shared"], + "whole_archive_deps": [":whole_static_lib_for_shared"], + }, srcs = ["both.cpp"], - static_copts = ["staticflag"], - static_deps_for_shared = [":static_dep_for_shared"], - static_deps_for_static = [":static_dep_for_static"], - static_srcs = ["staticonly.cpp"], + static = { + "copts": ["staticflag"], + "dynamic_deps": [":shared_dep_for_static"], + "srcs": ["staticonly.cpp"], + "static_deps": [":static_dep_for_static"], + "whole_archive_deps": [":whole_static_lib_for_static"], + }, whole_archive_deps = [":whole_static_lib_for_both"], - whole_archive_deps_for_shared = [":whole_static_lib_for_shared"], - whole_archive_deps_for_static = [":whole_static_lib_for_static"], +)`}, + }) +} + +func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` +cc_library { + name: "a", + whole_static_libs: ["whole_static_lib_for_both"], + static: { + whole_static_libs: ["whole_static_lib_for_static"], + }, + shared: { + whole_static_libs: ["whole_static_lib_for_shared"], + }, + bazel_module: { bp2build_available: true }, +} + +cc_prebuilt_library_static { name: "whole_static_lib_for_shared" } + +cc_prebuilt_library_static { name: "whole_static_lib_for_static" } + +cc_prebuilt_library_static { name: "whole_static_lib_for_both" } +`, + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( + name = "a", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + shared = { + "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"], + }, + static = { + "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"], + }, + whole_archive_deps = [":whole_static_lib_for_both_alwayslink"], )`}, }) } @@ -486,52 +537,56 @@ cc_library_static { name: "android_dep_for_shared" } "-Ifoo/bar", "-I$(BINDIR)/foo/bar", ], - dynamic_deps_for_shared = select({ - "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"], - "//conditions:default": [], - }), implementation_deps = [":static_dep_for_both"], - shared_copts = ["sharedflag"] + select({ - "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"], - "//conditions:default": [], - }), - shared_srcs = ["sharedonly.cpp"] + select({ - "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": ["android_shared.cpp"], - "//conditions:default": [], - }), + shared = { + "copts": ["sharedflag"] + select({ + "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"], + "//conditions:default": [], + }) + select({ + "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"], + "//conditions:default": [], + }) + select({ + "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"], + "//conditions:default": [], + }), + "dynamic_deps": select({ + "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"], + "//conditions:default": [], + }), + "srcs": ["sharedonly.cpp"] + select({ + "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"], + "//conditions:default": [], + }) + select({ + "//build/bazel/platforms/os:android": ["android_shared.cpp"], + "//conditions:default": [], + }), + "static_deps": [":static_dep_for_shared"] + select({ + "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"], + "//conditions:default": [], + }) + select({ + "//build/bazel/platforms/os:android": [":android_dep_for_shared"], + "//conditions:default": [], + }), + "whole_archive_deps": select({ + "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"], + "//conditions:default": [], + }), + }, srcs = ["both.cpp"], - static_copts = ["staticflag"] + select({ - "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"], - "//conditions:default": [], - }), - static_deps_for_shared = [":static_dep_for_shared"] + select({ - "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"], - "//conditions:default": [], - }) + select({ - "//build/bazel/platforms/os:android": [":android_dep_for_shared"], - "//conditions:default": [], - }), - static_deps_for_static = [":static_dep_for_static"] + select({ - "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"], - "//conditions:default": [], - }), - static_srcs = ["staticonly.cpp"] + select({ - "//build/bazel/platforms/arch:x86": ["x86_static.cpp"], - "//conditions:default": [], - }), - whole_archive_deps_for_shared = select({ - "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"], - "//conditions:default": [], - }), + static = { + "copts": ["staticflag"] + select({ + "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"], + "//conditions:default": [], + }), + "srcs": ["staticonly.cpp"] + select({ + "//build/bazel/platforms/arch:x86": ["x86_static.cpp"], + "//conditions:default": [], + }), + "static_deps": [":static_dep_for_static"] + select({ + "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"], + "//conditions:default": [], + }), + }, )`}, }) } @@ -623,20 +678,22 @@ filegroup { "-Ifoo/bar", "-I$(BINDIR)/foo/bar", ], - shared_srcs = [ - ":shared_filegroup_cpp_srcs", - "shared_source.cc", - "shared_source.cpp", - ], - shared_srcs_as = [ - "shared_source.s", - "shared_source.S", - ":shared_filegroup_as_srcs", - ], - shared_srcs_c = [ - "shared_source.c", - ":shared_filegroup_c_srcs", - ], + shared = { + "srcs": [ + ":shared_filegroup_cpp_srcs", + "shared_source.cc", + "shared_source.cpp", + ], + "srcs_as": [ + "shared_source.s", + "shared_source.S", + ":shared_filegroup_as_srcs", + ], + "srcs_c": [ + "shared_source.c", + ":shared_filegroup_c_srcs", + ], + }, srcs = [ ":both_filegroup_cpp_srcs", "both_source.cc", @@ -651,20 +708,22 @@ filegroup { "both_source.c", ":both_filegroup_c_srcs", ], - static_srcs = [ - ":static_filegroup_cpp_srcs", - "static_source.cc", - "static_source.cpp", - ], - static_srcs_as = [ - "static_source.s", - "static_source.S", - ":static_filegroup_as_srcs", - ], - static_srcs_c = [ - "static_source.c", - ":static_filegroup_c_srcs", - ], + static = { + "srcs": [ + ":static_filegroup_cpp_srcs", + "static_source.cc", + "static_source.cpp", + ], + "srcs_as": [ + "static_source.s", + "static_source.S", + ":static_filegroup_as_srcs", + ], + "srcs_c": [ + "static_source.c", + ":static_filegroup_c_srcs", + ], + }, )`}, }) } @@ -1231,3 +1290,173 @@ cc_library { }), )`}}) } + +func TestCcLibraryStrip(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library strip args", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` +cc_library { + name: "nothing", + bazel_module: { bp2build_available: true }, +} +cc_library { + name: "keep_symbols", + bazel_module: { bp2build_available: true }, + strip: { + keep_symbols: true, + } +} +cc_library { + name: "keep_symbols_and_debug_frame", + bazel_module: { bp2build_available: true }, + strip: { + keep_symbols_and_debug_frame: true, + } +} +cc_library { + name: "none", + bazel_module: { bp2build_available: true }, + strip: { + none: true, + } +} +cc_library { + name: "keep_symbols_list", + bazel_module: { bp2build_available: true }, + strip: { + keep_symbols_list: ["symbol"], + } +} +cc_library { + name: "all", + bazel_module: { bp2build_available: true }, + strip: { + all: true, + } +} +`, + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( + name = "all", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "all": True, + }, +)`, `cc_library( + name = "keep_symbols", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "keep_symbols": True, + }, +)`, `cc_library( + name = "keep_symbols_and_debug_frame", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "keep_symbols_and_debug_frame": True, + }, +)`, `cc_library( + name = "keep_symbols_list", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "keep_symbols_list": ["symbol"], + }, +)`, `cc_library( + name = "none", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "none": True, + }, +)`, `cc_library( + name = "nothing", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], +)`}, + }) +} + +func TestCcLibraryStripWithArch(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library strip args", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` +cc_library { + name: "multi-arch", + bazel_module: { bp2build_available: true }, + target: { + darwin: { + strip: { + keep_symbols_list: ["foo", "bar"] + } + }, + }, + arch: { + arm: { + strip: { + keep_symbols_and_debug_frame: true, + }, + }, + arm64: { + strip: { + keep_symbols: true, + }, + }, + } +} +`, + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( + name = "multi-arch", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + strip = { + "keep_symbols": select({ + "//build/bazel/platforms/arch:arm64": True, + "//conditions:default": None, + }), + "keep_symbols_and_debug_frame": select({ + "//build/bazel/platforms/arch:arm": True, + "//conditions:default": None, + }), + "keep_symbols_list": select({ + "//build/bazel/platforms/os:darwin": [ + "foo", + "bar", + ], + "//conditions:default": [], + }), + }, +)`}, + }) +} diff --git a/cc/bp2build.go b/cc/bp2build.go index 211fe5e05..76c5f3be9 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -142,15 +142,14 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties -- // properties which apply to either the shared or static version of a cc_library module. type staticOrSharedAttributes struct { - srcs bazel.LabelListAttribute - srcs_c bazel.LabelListAttribute - srcs_as bazel.LabelListAttribute - - copts bazel.StringListAttribute - - staticDeps bazel.LabelListAttribute - dynamicDeps bazel.LabelListAttribute - wholeArchiveDeps bazel.LabelListAttribute + Srcs bazel.LabelListAttribute + Srcs_c bazel.LabelListAttribute + Srcs_as bazel.LabelListAttribute + Copts bazel.StringListAttribute + + Static_deps bazel.LabelListAttribute + Dynamic_deps bazel.LabelListAttribute + Whole_archive_deps bazel.LabelListAttribute } func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) { @@ -251,19 +250,19 @@ func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module } attrs := staticOrSharedAttributes{ - copts: bazel.StringListAttribute{Value: props.Cflags}, - srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)), - staticDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)), - dynamicDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)), - wholeArchiveDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)), + Copts: bazel.StringListAttribute{Value: props.Cflags}, + Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)), + Static_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)), + Dynamic_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)), + Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)), } setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) { - attrs.copts.SetSelectValue(axis, config, props.Cflags) - attrs.srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs)) - attrs.staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs)) - attrs.dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs)) - attrs.wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)) + attrs.Copts.SetSelectValue(axis, config, props.Cflags) + attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs)) + attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs)) + attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs)) + attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)) } if isStatic { @@ -284,10 +283,10 @@ func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module } } - cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.srcs) - attrs.srcs = cppSrcs - attrs.srcs_c = cSrcs - attrs.srcs_as = asSrcs + cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs) + attrs.Srcs = cppSrcs + attrs.Srcs_c = cSrcs + attrs.Srcs_as = asSrcs return attrs } @@ -485,13 +484,18 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // Convenience struct to hold all attributes parsed from linker properties. type linkerAttributes struct { - deps bazel.LabelListAttribute - dynamicDeps bazel.LabelListAttribute - wholeArchiveDeps bazel.LabelListAttribute - exportedDeps bazel.LabelListAttribute - useLibcrt bazel.BoolAttribute - linkopts bazel.StringListAttribute - versionScript bazel.LabelAttribute + deps bazel.LabelListAttribute + dynamicDeps bazel.LabelListAttribute + wholeArchiveDeps bazel.LabelListAttribute + exportedDeps bazel.LabelListAttribute + useLibcrt bazel.BoolAttribute + linkopts bazel.StringListAttribute + versionScript bazel.LabelAttribute + stripKeepSymbols bazel.BoolAttribute + stripKeepSymbolsAndDebugFrame bazel.BoolAttribute + stripKeepSymbolsList bazel.StringListAttribute + stripAll bazel.BoolAttribute + stripNone bazel.BoolAttribute } // FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here @@ -515,6 +519,33 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) var versionScript bazel.LabelAttribute var useLibcrt bazel.BoolAttribute + var stripKeepSymbols bazel.BoolAttribute + var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute + var stripKeepSymbolsList bazel.StringListAttribute + var stripAll bazel.BoolAttribute + var stripNone bazel.BoolAttribute + + if libraryDecorator, ok := module.linker.(*libraryDecorator); ok { + stripProperties := libraryDecorator.stripper.StripProperties + stripKeepSymbols.Value = stripProperties.Strip.Keep_symbols + stripKeepSymbolsList.Value = stripProperties.Strip.Keep_symbols_list + stripKeepSymbolsAndDebugFrame.Value = stripProperties.Strip.Keep_symbols_and_debug_frame + stripAll.Value = stripProperties.Strip.All + stripNone.Value = stripProperties.Strip.None + } + + for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) { + for config, props := range configToProps { + if stripProperties, ok := props.(*StripProperties); ok { + stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols) + stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list) + stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame) + stripAll.SetSelectValue(axis, config, stripProperties.Strip.All) + stripNone.SetSelectValue(axis, config, stripProperties.Strip.None) + } + } + } + for _, linkerProps := range module.linker.linkerProps() { if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok { // Excludes to parallel Soong: @@ -522,7 +553,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs) staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs) wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) - wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) + wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) @@ -549,7 +580,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs) staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)) wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) - wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) + wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) @@ -572,13 +603,15 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) excludesField string // reference to the bazel attribute that should be set for the given product variable config attribute *bazel.LabelListAttribute + + depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList } productVarToDepFields := map[string]productVarDep{ // product variables do not support exclude_shared_libs - "Shared_libs": productVarDep{attribute: &dynamicDeps}, - "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps}, - "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps}, + "Shared_libs": productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes}, + "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes}, + "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes}, } productVariableProps := android.ProductVariableProperties(ctx) @@ -612,7 +645,8 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok { ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField) } - dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, android.BazelLabelForModuleDepsExcludes(ctx, android.FirstUniqueStrings(includes), excludes)) + + dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes)) } } @@ -630,6 +664,13 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkopts: linkopts, useLibcrt: useLibcrt, versionScript: versionScript, + + // Strip properties + stripKeepSymbols: stripKeepSymbols, + stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame, + stripKeepSymbolsList: stripKeepSymbolsList, + stripAll: stripAll, + stripNone: stripNone, } } @@ -290,9 +290,15 @@ type BaseProperties struct { // Set by DepsMutator. AndroidMkSystemSharedLibs []string `blueprint:"mutated"` + // The name of the image this module is built for, suffixed with a '.' ImageVariationPrefix string `blueprint:"mutated"` - VndkVersion string `blueprint:"mutated"` - SubName string `blueprint:"mutated"` + + // The VNDK version this module is built against. If empty, the module is not + // build against the VNDK. + VndkVersion string `blueprint:"mutated"` + + // Suffix for the name of Android.mk entries generated by this module + SubName string `blueprint:"mutated"` // *.logtags files, to combine together in order to generate the /system/etc/event-log-tags // file @@ -315,12 +321,15 @@ type BaseProperties struct { // Make this module available when building for recovery Recovery_available *bool - // Set by imageMutator - CoreVariantNeeded bool `blueprint:"mutated"` - RamdiskVariantNeeded bool `blueprint:"mutated"` - VendorRamdiskVariantNeeded bool `blueprint:"mutated"` - RecoveryVariantNeeded bool `blueprint:"mutated"` - ExtraVariants []string `blueprint:"mutated"` + // Used by imageMutator, set by ImageMutatorBegin() + CoreVariantNeeded bool `blueprint:"mutated"` + RamdiskVariantNeeded bool `blueprint:"mutated"` + VendorRamdiskVariantNeeded bool `blueprint:"mutated"` + RecoveryVariantNeeded bool `blueprint:"mutated"` + + // A list of variations for the "image" mutator of the form + //<image name> '.' <version char>, for example, 'vendor.S' + ExtraVersionedImageVariations []string `blueprint:"mutated"` // Allows this module to use non-APEX version of libraries. Useful // for building binaries that are started before APEXes are activated. @@ -1673,10 +1682,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { c.makeLinkType = GetMakeLinkType(actx, c) - if c.maybeGenerateBazelActions(actx) { - return - } - ctx := &moduleContext{ ModuleContext: actx, moduleContextImpl: moduleContextImpl{ @@ -1685,6 +1690,11 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } ctx.ctx = ctx + if c.maybeGenerateBazelActions(actx) { + c.maybeInstall(ctx, apexInfo) + return + } + deps := c.depsToPaths(ctx) if ctx.Failed() { return @@ -1772,19 +1782,7 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } c.outputFile = android.OptionalPathForPath(outputFile) - // If a lib is directly included in any of the APEXes or is not available to the - // platform (which is often the case when the stub is provided as a prebuilt), - // unhide the stubs variant having the latest version gets visible to make. In - // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to - // force anything in the make world to link against the stubs library. (unless it - // is explicitly referenced via .bootstrap suffix or the module is marked with - // 'bootstrap: true'). - if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() && - !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() && - c.IsStubs() && !c.InVendorRamdisk() { - c.Properties.HideFromMake = false // unhide - // Note: this is still non-installable - } + c.maybeUnhideFromMake() // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or // RECOVERY_SNAPSHOT_VERSION is current. @@ -1795,6 +1793,26 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } } + c.maybeInstall(ctx, apexInfo) +} + +func (c *Module) maybeUnhideFromMake() { + // If a lib is directly included in any of the APEXes or is not available to the + // platform (which is often the case when the stub is provided as a prebuilt), + // unhide the stubs variant having the latest version gets visible to make. In + // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to + // force anything in the make world to link against the stubs library. (unless it + // is explicitly referenced via .bootstrap suffix or the module is marked with + // 'bootstrap: true'). + if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() && + !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() && + c.IsStubs() && !c.InVendorRamdisk() { + c.Properties.HideFromMake = false // unhide + // Note: this is still non-installable + } +} + +func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) { if !proptools.BoolDefault(c.Properties.Installable, true) { // If the module has been specifically configure to not be installed then // hide from make as otherwise it will break when running inside make diff --git a/cc/image.go b/cc/image.go index c9c0e63a6..15ec1c867 100644 --- a/cc/image.go +++ b/cc/image.go @@ -341,11 +341,11 @@ func (m *Module) RecoveryAvailable() bool { } func (m *Module) ExtraVariants() []string { - return m.Properties.ExtraVariants + return m.Properties.ExtraVersionedImageVariations } func (m *Module) AppendExtraVariant(extraVariant string) { - m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant) + m.Properties.ExtraVersionedImageVariations = append(m.Properties.ExtraVersionedImageVariations, extraVariant) } func (m *Module) SetRamdiskVariantNeeded(b bool) { @@ -629,7 +629,7 @@ func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { } func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string { - return c.Properties.ExtraVariants + return c.Properties.ExtraVersionedImageVariations } func squashVendorSrcs(m *Module) { diff --git a/cc/library.go b/cc/library.go index 9bff77889..b9a2b0676 100644 --- a/cc/library.go +++ b/cc/library.go @@ -28,6 +28,7 @@ import ( "android/soong/android" "android/soong/bazel" + "android/soong/bazel/cquery" "android/soong/cc/config" ) @@ -236,29 +237,22 @@ type bazelCcLibraryAttributes struct { Linkopts bazel.StringListAttribute Use_libcrt bazel.BoolAttribute - // Attributes pertaining to shared variant. - Shared_srcs bazel.LabelListAttribute - Shared_srcs_c bazel.LabelListAttribute - Shared_srcs_as bazel.LabelListAttribute - Shared_copts bazel.StringListAttribute + // This is shared only. + Version_script bazel.LabelAttribute - Exported_deps_for_shared bazel.LabelListAttribute - Static_deps_for_shared bazel.LabelListAttribute - Dynamic_deps_for_shared bazel.LabelListAttribute - Whole_archive_deps_for_shared bazel.LabelListAttribute - User_link_flags bazel.StringListAttribute - Version_script bazel.LabelAttribute + // Common properties shared between both shared and static variants. + Shared staticOrSharedAttributes + Static staticOrSharedAttributes - // Attributes pertaining to static variant. - Static_srcs bazel.LabelListAttribute - Static_srcs_c bazel.LabelListAttribute - Static_srcs_as bazel.LabelListAttribute - Static_copts bazel.StringListAttribute + Strip stripAttributes +} - Exported_deps_for_static bazel.LabelListAttribute - Static_deps_for_static bazel.LabelListAttribute - Dynamic_deps_for_static bazel.LabelListAttribute - Whole_archive_deps_for_static bazel.LabelListAttribute +type stripAttributes struct { + Keep_symbols bazel.BoolAttribute + Keep_symbols_and_debug_frame bazel.BoolAttribute + Keep_symbols_list bazel.StringListAttribute + All bazel.BoolAttribute + None bazel.BoolAttribute } type bazelCcLibrary struct { @@ -323,22 +317,19 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) { Linkopts: linkerAttrs.linkopts, Use_libcrt: linkerAttrs.useLibcrt, - Shared_srcs: sharedAttrs.srcs, - Shared_srcs_c: sharedAttrs.srcs_c, - Shared_srcs_as: sharedAttrs.srcs_as, - Shared_copts: sharedAttrs.copts, - Static_deps_for_shared: sharedAttrs.staticDeps, - Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps, - Dynamic_deps_for_shared: sharedAttrs.dynamicDeps, - Version_script: linkerAttrs.versionScript, + Version_script: linkerAttrs.versionScript, + + Strip: stripAttributes{ + Keep_symbols: linkerAttrs.stripKeepSymbols, + Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame, + Keep_symbols_list: linkerAttrs.stripKeepSymbolsList, + All: linkerAttrs.stripAll, + None: linkerAttrs.stripNone, + }, + + Shared: sharedAttrs, - Static_srcs: staticAttrs.srcs, - Static_srcs_c: staticAttrs.srcs_c, - Static_srcs_as: staticAttrs.srcs_as, - Static_copts: staticAttrs.copts, - Static_deps_for_static: staticAttrs.staticDeps, - Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps, - Dynamic_deps_for_static: staticAttrs.dynamicDeps, + Static: staticAttrs, } props := bazel.BazelTargetModuleProperties{ @@ -561,22 +552,10 @@ type ccLibraryBazelHandler struct { module *Module } -func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { - if !handler.module.static() { - // TODO(cparsons): Support shared libraries. - return false - } - bazelCtx := ctx.Config().BazelContext - ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType) - if err != nil { - ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) - return false - } - if !ok { - return ok - } +// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong +// provider from a Bazel shared library's CcInfo provider. +func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { rootStaticArchives := ccInfo.RootStaticArchives - objPaths := ccInfo.CcObjectFiles if len(rootStaticArchives) != 1 { ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives) return false @@ -584,6 +563,7 @@ func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.Modu outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0]) handler.module.outputFile = android.OptionalPathForPath(outputFilePath) + objPaths := ccInfo.CcObjectFiles objFiles := make(android.Paths, len(objPaths)) for i, objPath := range objPaths { objFiles[i] = android.PathForBazelOut(ctx, objPath) @@ -597,26 +577,109 @@ func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.Modu ReuseObjects: objects, Objects: objects, - // TODO(cparsons): Include transitive static libraries in this provider to support + // TODO(b/190524881): Include transitive static libraries in this provider to support // static libraries with deps. TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL). Direct(outputFilePath). Build(), }) - ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo)) + return true +} + +// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong +// provider from a Bazel shared library's CcInfo provider. +func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool { + rootDynamicLibraries := ccInfo.RootDynamicLibraries + + if len(rootDynamicLibraries) != 1 { + ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries) + return false + } + outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0]) + handler.module.outputFile = android.OptionalPathForPath(outputFilePath) + + handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath + + tocFile := getTocFile(ctx, label, ccInfo.OutputFiles) + handler.module.linker.(*libraryDecorator).tocFile = tocFile + + ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{ + TableOfContents: tocFile, + SharedLibrary: outputFilePath, + Target: ctx.Target(), + // TODO(b/190524881): Include transitive static libraries in this provider to support + // static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering. + }) + return true +} + +// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file +// contains the table of contents of all symbols of a shared object. +func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath { + var tocFile string + for _, file := range outputFiles { + if strings.HasSuffix(file, ".so.toc") { + if tocFile != "" { + ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label) + } + tocFile = file + // Don't break to validate that there are no multiple .toc files per .so. + } + } + if tocFile == "" { + return android.OptionalPath{} + } + return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile)) +} + +func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool { + bazelCtx := ctx.Config().BazelContext + ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType) + if err != nil { + ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err) + return false + } + if !ok { + return ok + } + + if handler.module.static() { + if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok { + return false + } + } else if handler.module.Shared() { + if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok { + return false + } + } else { + return false + } + + handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo) + handler.module.maybeUnhideFromMake() + if i, ok := handler.module.linker.(snapshotLibraryInterface); ok { // Dependencies on this library will expect collectedSnapshotHeaders to // be set, otherwise validation will fail. For now, set this to an empty // list. - // TODO(cparsons): More closely mirror the collectHeadersForSnapshot + // TODO(b/190533363): More closely mirror the collectHeadersForSnapshot // implementation. i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{} } - return ok } +func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) { + flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo) + // flag exporters consolidates properties like includes, flags, dependencies that should be + // exported from this module to other modules + ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo) + // Store flag info to be passed along to androidmk + // TODO(b/184387147): Androidmk should be done in Bazel, not Soong. + library.flagExporterInfo = &flagExporterInfo +} + func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths { ret := android.Paths{} diff --git a/cc/library_headers.go b/cc/library_headers.go index 20659292d..d6b45290e 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -73,13 +73,7 @@ func (h *libraryHeaderBazelHander) generateBazelBuildActions(ctx android.ModuleC // HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{}) - flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo) - // Store flag info to be passed along to androimk - // TODO(b/184387147): Androidmk should be done in Bazel, not Soong. - h.library.flagExporterInfo = &flagExporterInfo - // flag exporters consolidates properties like includes, flags, dependencies that should be - // exported from this module to other modules - ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo) + h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo) // Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise // validation will fail. For now, set this to an empty list. diff --git a/cc/library_test.go b/cc/library_test.go index 797527547..ba372a8e2 100644 --- a/cc/library_test.go +++ b/cc/library_test.go @@ -19,6 +19,7 @@ import ( "testing" "android/soong/android" + "android/soong/bazel/cquery" ) func TestLibraryReuse(t *testing.T) { @@ -240,3 +241,48 @@ func TestStubsVersions_ParseError(t *testing.T) { testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp) } + +func TestCcLibraryWithBazel(t *testing.T) { + bp := ` +cc_library { + name: "foo", + srcs: ["foo.cc"], + bazel_module: { label: "//foo/bar:bar" }, +}` + config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) + config.BazelContext = android.MockBazelContext{ + OutputBaseDir: "outputbase", + LabelToCcInfo: map[string]cquery.CcInfo{ + "//foo/bar:bar": cquery.CcInfo{ + CcObjectFiles: []string{"foo.o"}, + Includes: []string{"include"}, + SystemIncludes: []string{"system_include"}, + RootStaticArchives: []string{"foo.a"}, + RootDynamicLibraries: []string{"foo.so"}, + }, + }, + } + ctx := testCcWithConfig(t, config) + + staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module() + outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + + expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) + + sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module() + outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("") + if err != nil { + t.Errorf("Unexpected error getting cc_object outputfiles %s", err) + } + expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"} + android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings()) + + entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0] + expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"} + gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"] + android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags) +} diff --git a/cc/linker.go b/cc/linker.go index 895931acb..d9ee0cfde 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -122,7 +122,7 @@ type BaseLinkerProperties struct { // version script for vendor or product variant Version_script *string `android:"arch_variant"` - } + } `android:"arch_variant"` Recovery struct { // list of shared libs that only should be used to build the recovery // variant of the C/C++ module. @@ -182,7 +182,7 @@ type BaseLinkerProperties struct { // variant of the C/C++ module. Exclude_static_libs []string } - } + } `android:"arch_variant"` // make android::build:GetBuildNumber() available containing the build ID. Use_version_lib *bool `android:"arch_variant"` diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go index 4a9601bca..2726b1a3f 100644 --- a/cc/ndk_sysroot.go +++ b/cc/ndk_sysroot.go @@ -144,13 +144,13 @@ func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) { Inputs: licensePaths, }) - baseDepPaths := append(installPaths, combinedLicense, - getNdkAbiDiffTimestampFile(ctx)) + baseDepPaths := append(installPaths, combinedLicense) ctx.Build(pctx, android.BuildParams{ - Rule: android.Touch, - Output: getNdkBaseTimestampFile(ctx), - Implicits: baseDepPaths, + Rule: android.Touch, + Output: getNdkBaseTimestampFile(ctx), + Implicits: baseDepPaths, + Validation: getNdkAbiDiffTimestampFile(ctx), }) fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx)) diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py index 2387e697d..5e6b8f509 100755 --- a/cc/ndkstubgen/__init__.py +++ b/cc/ndkstubgen/__init__.py @@ -108,7 +108,14 @@ def parse_args() -> argparse.Namespace: parser.add_argument( '--llndk', action='store_true', help='Use the LLNDK variant.') parser.add_argument( - '--apex', action='store_true', help='Use the APEX variant.') + '--apex', + action='store_true', + help='Use the APEX variant. Note: equivalent to --system-api.') + parser.add_argument( + '--system-api', + action='store_true', + dest='apex', + help='Use the SystemAPI variant. Note: equivalent to --apex.') parser.add_argument('--api-map', type=resolved_path, diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py index 3dbab6180..09551eabb 100755 --- a/cc/ndkstubgen/test_ndkstubgen.py +++ b/cc/ndkstubgen/test_ndkstubgen.py @@ -19,9 +19,10 @@ import io import textwrap import unittest -import ndkstubgen import symbolfile -from symbolfile import Arch, Tag +from symbolfile import Arch, Tags + +import ndkstubgen # pylint: disable=missing-docstring @@ -38,23 +39,25 @@ class GeneratorTest(unittest.TestCase): version_file, symbol_list_file, Arch('arm'), 9, False, False) - version = symbolfile.Version('VERSION_PRIVATE', None, [], [ - symbolfile.Symbol('foo', []), + version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [ + symbolfile.Symbol('foo', Tags()), ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) - version = symbolfile.Version('VERSION', None, [Tag('x86')], [ - symbolfile.Symbol('foo', []), - ]) + version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']), + [ + symbolfile.Symbol('foo', Tags()), + ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) - version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [ - symbolfile.Symbol('foo', []), - ]) + version = symbolfile.Version('VERSION', None, + Tags.from_strs(['introduced=14']), [ + symbolfile.Symbol('foo', Tags()), + ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) @@ -69,29 +72,29 @@ class GeneratorTest(unittest.TestCase): version_file, symbol_list_file, Arch('arm'), 9, False, False) - version = symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', [Tag('x86')]), + version = symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags.from_strs(['x86'])), ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) - version = symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', [Tag('introduced=14')]), + version = symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])), ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) - version = symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', [Tag('llndk')]), + version = symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags.from_strs(['llndk'])), ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) self.assertEqual('', version_file.getvalue()) - version = symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', [Tag('apex')]), + version = symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags.from_strs(['apex'])), ]) generator.write_version(version) self.assertEqual('', src_file.getvalue()) @@ -106,18 +109,17 @@ class GeneratorTest(unittest.TestCase): Arch('arm'), 9, False, False) versions = [ - symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', []), - symbolfile.Symbol('bar', [Tag('var')]), - symbolfile.Symbol('woodly', [Tag('weak')]), - symbolfile.Symbol('doodly', - [Tag('weak'), Tag('var')]), + symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags()), + symbolfile.Symbol('bar', Tags.from_strs(['var'])), + symbolfile.Symbol('woodly', Tags.from_strs(['weak'])), + symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])), ]), - symbolfile.Version('VERSION_2', 'VERSION_1', [], [ - symbolfile.Symbol('baz', []), + symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [ + symbolfile.Symbol('baz', Tags()), ]), - symbolfile.Version('VERSION_3', 'VERSION_1', [], [ - symbolfile.Symbol('qux', [Tag('versioned=14')]), + symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [ + symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])), ]), ] diff --git a/cc/strip.go b/cc/strip.go index b1f34bb89..c60e13530 100644 --- a/cc/strip.go +++ b/cc/strip.go @@ -59,6 +59,7 @@ func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool { return !forceDisable && (forceEnable || defaultEnable) } +// Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl. func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath, flags StripFlags, isStaticLib bool) { if actx.Darwin() { diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py index 5678e7d83..31c444385 100644 --- a/cc/symbolfile/__init__.py +++ b/cc/symbolfile/__init__.py @@ -14,18 +14,22 @@ # limitations under the License. # """Parser for Android's version script information.""" -from dataclasses import dataclass +from __future__ import annotations + +from dataclasses import dataclass, field import logging import re from typing import ( Dict, Iterable, + Iterator, List, Mapping, NewType, Optional, TextIO, Tuple, + Union, ) @@ -52,11 +56,52 @@ def logger() -> logging.Logger: @dataclass +class Tags: + """Container class for the tags attached to a symbol or version.""" + + tags: tuple[Tag, ...] = field(default_factory=tuple) + + @classmethod + def from_strs(cls, strs: Iterable[str]) -> Tags: + """Constructs tags from a collection of strings. + + Does not decode API levels. + """ + return Tags(tuple(Tag(s) for s in strs)) + + def __contains__(self, tag: Union[Tag, str]) -> bool: + return tag in self.tags + + def __iter__(self) -> Iterator[Tag]: + yield from self.tags + + @property + def has_mode_tags(self) -> bool: + """Returns True if any mode tags (apex, llndk, etc) are set.""" + return self.has_apex_tags or self.has_llndk_tags + + @property + def has_apex_tags(self) -> bool: + """Returns True if any APEX tags are set.""" + return 'apex' in self.tags or 'systemapi' in self.tags + + @property + def has_llndk_tags(self) -> bool: + """Returns True if any LL-NDK tags are set.""" + return 'llndk' in self.tags + + @property + def has_platform_only_tags(self) -> bool: + """Returns True if any platform-only tags are set.""" + return 'platform-only' in self.tags + + +@dataclass class Symbol: """A symbol definition from a symbol file.""" name: str - tags: List[Tag] + tags: Tags @dataclass @@ -65,14 +110,22 @@ class Version: name: str base: Optional[str] - tags: List[Tag] + tags: Tags symbols: List[Symbol] + @property + def is_private(self) -> bool: + """Returns True if this version block is private (platform only).""" + return self.name.endswith('_PRIVATE') or self.name.endswith('_PLATFORM') + -def get_tags(line: str) -> List[Tag]: +def get_tags(line: str, api_map: ApiMap) -> Tags: """Returns a list of all tags on this line.""" _, _, all_tags = line.strip().partition('#') - return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()] + return Tags(tuple( + decode_api_level_tag(Tag(e), api_map) + for e in re.split(r'\s+', all_tags) if e.strip() + )) def is_api_level_tag(tag: Tag) -> bool: @@ -104,24 +157,21 @@ def decode_api_level(api: str, api_map: ApiMap) -> int: return api_map[api] -def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]: - """Decodes API level code names in a list of tags. +def decode_api_level_tag(tag: Tag, api_map: ApiMap) -> Tag: + """Decodes API level code name in a tag. Raises: ParseError: An unknown version name was found in a tag. """ - decoded_tags = list(tags) - for idx, tag in enumerate(tags): - if not is_api_level_tag(tag): - continue - name, value = split_tag(tag) + if not is_api_level_tag(tag): + return tag - try: - decoded = str(decode_api_level(value, api_map)) - decoded_tags[idx] = Tag('='.join([name, decoded])) - except KeyError: - raise ParseError(f'Unknown version name in tag: {tag}') - return decoded_tags + name, value = split_tag(tag) + try: + decoded = str(decode_api_level(value, api_map)) + return Tag(f'{name}={decoded}') + except KeyError as ex: + raise ParseError(f'Unknown version name in tag: {tag}') from ex def split_tag(tag: Tag) -> Tuple[str, str]: @@ -149,55 +199,52 @@ def get_tag_value(tag: Tag) -> str: return split_tag(tag)[1] -def version_is_private(version: str) -> bool: - """Returns True if the version name should be treated as private.""" - return version.endswith('_PRIVATE') or version.endswith('_PLATFORM') +def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool, + apex: bool) -> bool: + """Returns True if the tagged object should be omitted. + + This defines the rules shared between version tagging and symbol tagging. + """ + # The apex and llndk tags will only exclude APIs from other modes. If in + # APEX or LLNDK mode and neither tag is provided, we fall back to the + # default behavior because all NDK symbols are implicitly available to APEX + # and LLNDK. + if tags.has_mode_tags: + if not apex and not llndk: + return True + if apex and not tags.has_apex_tags: + return True + if llndk and not tags.has_llndk_tags: + return True + if not symbol_in_arch(tags, arch): + return True + if not symbol_in_api(tags, arch, api): + return True + return False def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool, apex: bool) -> bool: - """Returns True if the version section should be ommitted. + """Returns True if the version section should be omitted. We want to omit any sections that do not have any symbols we'll have in the stub library. Sections that contain entirely future symbols or only symbols for certain architectures. """ - if version_is_private(version.name): - return True - if 'platform-only' in version.tags: + if version.is_private: return True - - no_llndk_no_apex = ('llndk' not in version.tags - and 'apex' not in version.tags) - keep = no_llndk_no_apex or \ - ('llndk' in version.tags and llndk) or \ - ('apex' in version.tags and apex) - if not keep: - return True - if not symbol_in_arch(version.tags, arch): + if version.tags.has_platform_only_tags: return True - if not symbol_in_api(version.tags, arch, api): - return True - return False + return _should_omit_tags(version.tags, arch, api, llndk, apex) def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool, apex: bool) -> bool: """Returns True if the symbol should be omitted.""" - no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags - keep = no_llndk_no_apex or \ - ('llndk' in symbol.tags and llndk) or \ - ('apex' in symbol.tags and apex) - if not keep: - return True - if not symbol_in_arch(symbol.tags, arch): - return True - if not symbol_in_api(symbol.tags, arch, api): - return True - return False + return _should_omit_tags(symbol.tags, arch, api, llndk, apex) -def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool: +def symbol_in_arch(tags: Tags, arch: Arch) -> bool: """Returns true if the symbol is present for the given architecture.""" has_arch_tags = False for tag in tags: @@ -325,8 +372,7 @@ class SymbolFileParser: """Parses a single version section and returns a Version object.""" assert self.current_line is not None name = self.current_line.split('{')[0].strip() - tags = get_tags(self.current_line) - tags = decode_api_level_tags(tags, self.api_map) + tags = get_tags(self.current_line, self.api_map) symbols: List[Symbol] = [] global_scope = True cpp_symbols = False @@ -373,8 +419,7 @@ class SymbolFileParser: 'Wildcard global symbols are not permitted.') # Line is now in the format "<symbol-name>; # tags" name, _, _ = self.current_line.strip().partition(';') - tags = get_tags(self.current_line) - tags = decode_api_level_tags(tags, self.api_map) + tags = get_tags(self.current_line, self.api_map) return Symbol(name, tags) def next_line(self) -> str: diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py index 92b13999e..c1e821904 100644 --- a/cc/symbolfile/test_symbolfile.py +++ b/cc/symbolfile/test_symbolfile.py @@ -19,7 +19,7 @@ import textwrap import unittest import symbolfile -from symbolfile import Arch, Tag +from symbolfile import Arch, Tag, Tags, Version # pylint: disable=missing-docstring @@ -35,12 +35,14 @@ class DecodeApiLevelTest(unittest.TestCase): class TagsTest(unittest.TestCase): def test_get_tags_no_tags(self) -> None: - self.assertEqual([], symbolfile.get_tags('')) - self.assertEqual([], symbolfile.get_tags('foo bar baz')) + self.assertEqual(Tags(), symbolfile.get_tags('', {})) + self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {})) def test_get_tags(self) -> None: - self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar')) - self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz')) + self.assertEqual(Tags.from_strs(['foo', 'bar']), + symbolfile.get_tags('# foo bar', {})) + self.assertEqual(Tags.from_strs(['bar', 'baz']), + symbolfile.get_tags('foo # bar baz', {})) def test_split_tag(self) -> None: self.assertTupleEqual(('foo', 'bar'), @@ -77,12 +79,14 @@ class TagsTest(unittest.TestCase): } tags = [ - Tag('introduced=9'), - Tag('introduced-arm=14'), - Tag('versioned=16'), - Tag('arm'), - Tag('introduced=O'), - Tag('introduced=P'), + symbolfile.decode_api_level_tag(t, api_map) for t in ( + Tag('introduced=9'), + Tag('introduced-arm=14'), + Tag('versioned=16'), + Tag('arm'), + Tag('introduced=O'), + Tag('introduced=P'), + ) ] expected_tags = [ Tag('introduced=9'), @@ -92,33 +96,37 @@ class TagsTest(unittest.TestCase): Tag('introduced=9000'), Tag('introduced=9001'), ] - self.assertListEqual( - expected_tags, symbolfile.decode_api_level_tags(tags, api_map)) + self.assertListEqual(expected_tags, tags) with self.assertRaises(symbolfile.ParseError): - symbolfile.decode_api_level_tags([Tag('introduced=O')], {}) + symbolfile.decode_api_level_tag(Tag('introduced=O'), {}) class PrivateVersionTest(unittest.TestCase): def test_version_is_private(self) -> None: - self.assertFalse(symbolfile.version_is_private('foo')) - self.assertFalse(symbolfile.version_is_private('PRIVATE')) - self.assertFalse(symbolfile.version_is_private('PLATFORM')) - self.assertFalse(symbolfile.version_is_private('foo_private')) - self.assertFalse(symbolfile.version_is_private('foo_platform')) - self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_')) - self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_')) + def mock_version(name: str) -> Version: + return Version(name, base=None, tags=Tags(), symbols=[]) + + self.assertFalse(mock_version('foo').is_private) + self.assertFalse(mock_version('PRIVATE').is_private) + self.assertFalse(mock_version('PLATFORM').is_private) + self.assertFalse(mock_version('foo_private').is_private) + self.assertFalse(mock_version('foo_platform').is_private) + self.assertFalse(mock_version('foo_PRIVATE_').is_private) + self.assertFalse(mock_version('foo_PLATFORM_').is_private) - self.assertTrue(symbolfile.version_is_private('foo_PRIVATE')) - self.assertTrue(symbolfile.version_is_private('foo_PLATFORM')) + self.assertTrue(mock_version('foo_PRIVATE').is_private) + self.assertTrue(mock_version('foo_PLATFORM').is_private) class SymbolPresenceTest(unittest.TestCase): def test_symbol_in_arch(self) -> None: - self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm'))) - self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm'))) + self.assertTrue(symbolfile.symbol_in_arch(Tags(), Arch('arm'))) + self.assertTrue( + symbolfile.symbol_in_arch(Tags.from_strs(['arm']), Arch('arm'))) - self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm'))) + self.assertFalse( + symbolfile.symbol_in_arch(Tags.from_strs(['x86']), Arch('arm'))) def test_symbol_in_api(self) -> None: self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9)) @@ -197,81 +205,99 @@ class OmitVersionTest(unittest.TestCase): def test_omit_private(self) -> None: self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, - False)) + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + False, False)) self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'), - 9, False, False)) + symbolfile.Version('foo_PRIVATE', None, Tags(), []), + Arch('arm'), 9, False, False)) self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'), - 9, False, False)) + symbolfile.Version('foo_PLATFORM', None, Tags(), []), + Arch('arm'), 9, False, False)) self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('platform-only')], []), + symbolfile.Version('foo', None, + Tags.from_strs(['platform-only']), []), Arch('arm'), 9, False, False)) def test_omit_llndk(self) -> None: self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('llndk')], []), + symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []), Arch('arm'), 9, False, False)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True, - False)) + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + True, False)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('llndk')], []), + symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []), Arch('arm'), 9, True, False)) def test_omit_apex(self) -> None: self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('apex')], []), + symbolfile.Version('foo', None, Tags.from_strs(['apex']), []), Arch('arm'), 9, False, False)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, - True)) + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + False, True)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('apex')], []), + symbolfile.Version('foo', None, Tags.from_strs(['apex']), []), Arch('arm'), 9, False, True)) + def test_omit_systemapi(self) -> None: + self.assertTrue( + symbolfile.should_omit_version( + symbolfile.Version('foo', None, Tags.from_strs(['systemapi']), + []), Arch('arm'), 9, False, False)) + + self.assertFalse( + symbolfile.should_omit_version( + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + False, True)) + self.assertFalse( + symbolfile.should_omit_version( + symbolfile.Version('foo', None, Tags.from_strs(['systemapi']), + []), Arch('arm'), 9, False, True)) + def test_omit_arch(self) -> None: self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, - False)) + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + False, False)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'), - 9, False, False)) + symbolfile.Version('foo', None, Tags.from_strs(['arm']), []), + Arch('arm'), 9, False, False)) self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'), - 9, False, False)) + symbolfile.Version('foo', None, Tags.from_strs(['x86']), []), + Arch('arm'), 9, False, False)) def test_omit_api(self) -> None: self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False, - False)) + symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9, + False, False)) self.assertFalse( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('introduced=9')], []), + symbolfile.Version('foo', None, + Tags.from_strs(['introduced=9']), []), Arch('arm'), 9, False, False)) self.assertTrue( symbolfile.should_omit_version( - symbolfile.Version('foo', None, [Tag('introduced=14')], []), + symbolfile.Version('foo', None, + Tags.from_strs(['introduced=14']), []), Arch('arm'), 9, False, False)) @@ -279,58 +305,72 @@ class OmitSymbolTest(unittest.TestCase): def test_omit_llndk(self) -> None: self.assertTrue( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, - False, False)) + symbolfile.Symbol('foo', Tags.from_strs(['llndk'])), + Arch('arm'), 9, False, False)) self.assertFalse( - symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), + symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()), Arch('arm'), 9, True, False)) self.assertFalse( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True, - False)) + symbolfile.Symbol('foo', Tags.from_strs(['llndk'])), + Arch('arm'), 9, True, False)) def test_omit_apex(self) -> None: self.assertTrue( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False, - False)) + symbolfile.Symbol('foo', Tags.from_strs(['apex'])), + Arch('arm'), 9, False, False)) + + self.assertFalse( + symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()), + Arch('arm'), 9, False, True)) + self.assertFalse( + symbolfile.should_omit_symbol( + symbolfile.Symbol('foo', Tags.from_strs(['apex'])), + Arch('arm'), 9, False, True)) + + def test_omit_systemapi(self) -> None: + self.assertTrue( + symbolfile.should_omit_symbol( + symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])), + Arch('arm'), 9, False, False)) self.assertFalse( - symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), + symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()), Arch('arm'), 9, False, True)) self.assertFalse( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False, - True)) + symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])), + Arch('arm'), 9, False, True)) def test_omit_arch(self) -> None: self.assertFalse( - symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), + symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()), Arch('arm'), 9, False, False)) self.assertFalse( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False, - False)) + symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'), + 9, False, False)) self.assertTrue( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False, - False)) + symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'), + 9, False, False)) def test_omit_api(self) -> None: self.assertFalse( - symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), + symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()), Arch('arm'), 9, False, False)) self.assertFalse( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'), - 9, False, False)) + symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])), + Arch('arm'), 9, False, False)) self.assertTrue( symbolfile.should_omit_symbol( - symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'), - 9, False, False)) + symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])), + Arch('arm'), 9, False, False)) class SymbolFileParseTest(unittest.TestCase): @@ -376,11 +416,11 @@ class SymbolFileParseTest(unittest.TestCase): version = parser.parse_version() self.assertEqual('VERSION_1', version.name) self.assertIsNone(version.base) - self.assertEqual(['foo', 'bar'], version.tags) + self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags) expected_symbols = [ - symbolfile.Symbol('baz', []), - symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]), + symbolfile.Symbol('baz', Tags()), + symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])), ] self.assertEqual(expected_symbols, version.symbols) @@ -388,7 +428,7 @@ class SymbolFileParseTest(unittest.TestCase): version = parser.parse_version() self.assertEqual('VERSION_2', version.name) self.assertEqual('VERSION_1', version.base) - self.assertEqual([], version.tags) + self.assertEqual(Tags(), version.tags) def test_parse_version_eof(self) -> None: input_file = io.StringIO(textwrap.dedent("""\ @@ -423,12 +463,12 @@ class SymbolFileParseTest(unittest.TestCase): parser.next_line() symbol = parser.parse_symbol() self.assertEqual('foo', symbol.name) - self.assertEqual([], symbol.tags) + self.assertEqual(Tags(), symbol.tags) parser.next_line() symbol = parser.parse_symbol() self.assertEqual('bar', symbol.name) - self.assertEqual(['baz', 'qux'], symbol.tags) + self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags) def test_wildcard_symbol_global(self) -> None: input_file = io.StringIO(textwrap.dedent("""\ @@ -497,14 +537,15 @@ class SymbolFileParseTest(unittest.TestCase): versions = parser.parse() expected = [ - symbolfile.Version('VERSION_1', None, [], [ - symbolfile.Symbol('foo', []), - symbolfile.Symbol('bar', [Tag('baz')]), - ]), - symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [ - symbolfile.Symbol('woodly', []), - symbolfile.Symbol('doodly', [Tag('asdf')]), + symbolfile.Version('VERSION_1', None, Tags(), [ + symbolfile.Symbol('foo', Tags()), + symbolfile.Symbol('bar', Tags.from_strs(['baz'])), ]), + symbolfile.Version( + 'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [ + symbolfile.Symbol('woodly', Tags()), + symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])), + ]), ] self.assertEqual(expected, versions) @@ -527,10 +568,10 @@ class SymbolFileParseTest(unittest.TestCase): self.assertIsNone(version.base) expected_symbols = [ - symbolfile.Symbol('foo', []), - symbolfile.Symbol('bar', [Tag('llndk')]), - symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]), - symbolfile.Symbol('qux', [Tag('apex')]), + symbolfile.Symbol('foo', Tags()), + symbolfile.Symbol('bar', Tags.from_strs(['llndk'])), + symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])), + symbolfile.Symbol('qux', Tags.from_strs(['apex'])), ] self.assertEqual(expected_symbols, version.symbols) diff --git a/docs/map_files.md b/docs/map_files.md index 192530fb6..13880591a 100644 --- a/docs/map_files.md +++ b/docs/map_files.md @@ -70,9 +70,11 @@ the same line. The supported tags are: ### apex -Indicates that the version or symbol is to be exposed in the APEX stubs rather -than the NDK. May be used in combination with `llndk` if the symbol is exposed -to both APEX and the LL-NDK. +Indicates that the version or symbol is to be exposed by an APEX rather than the +NDK. For APIs exposed by the platform *for* APEX, use `systemapi`. + +May be used in combination with `llndk` if the symbol is exposed to both APEX +and the LL-NDK. ### future @@ -144,6 +146,12 @@ for use by the NDK, LL-NDK, or APEX (similar to Java's `@SystemAPI`). It is preferable to keep such APIs in an entirely separate library to protect them from access via `dlsym`, but this is not always possible. +### systemapi + +This is a synonym of the `apex` tag. It should be used to clarify that the API +is an API exposed by the system for an APEX, whereas `apex` should be used for +APIs exposed by an APEX to the platform or another APEX. + ### var Used to define a public global variable. By default all symbols are exposed as diff --git a/java/base.go b/java/base.go index 440b00478..1daa10876 100644 --- a/java/base.go +++ b/java/base.go @@ -155,6 +155,11 @@ type CommonProperties struct { // List of java_plugin modules that provide extra errorprone checks. Extra_check_modules []string + + // Whether to run errorprone on a normal build. If this is false, errorprone + // will still be run if the RUN_ERROR_PRONE environment variable is true. + // Default false. + Enabled *bool } Proto struct { @@ -701,7 +706,7 @@ func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaB // javaVersion flag. flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j)) - if ctx.Config().RunErrorProne() { + if ctx.Config().RunErrorProne() || Bool(j.properties.Errorprone.Enabled) { if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil { ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?") } @@ -972,14 +977,23 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 { var extraJarDeps android.Paths - if ctx.Config().RunErrorProne() { - // If error-prone is enabled, add an additional rule to compile the java files into - // a separate set of classes (so that they don't overwrite the normal ones and require - // a rebuild when error-prone is turned off). - // TODO(ccross): Once we always compile with javac9 we may be able to conditionally - // enable error-prone without affecting the output class files. + if Bool(j.properties.Errorprone.Enabled) { + // If error-prone is enabled, enable errorprone flags on the regular + // build. + flags = enableErrorproneFlags(flags) + } else if ctx.Config().RunErrorProne() { + // Otherwise, if the RUN_ERROR_PRONE environment variable is set, create + // a new jar file just for compiling with the errorprone compiler to. + // This is because we don't want to cause the java files to get completely + // rebuilt every time the state of the RUN_ERROR_PRONE variable changes. + // We also don't want to run this if errorprone is enabled by default for + // this module, or else we could have duplicated errorprone messages. + errorproneFlags := enableErrorproneFlags(flags) errorprone := android.PathForModuleOut(ctx, "errorprone", jarName) - RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags) + + transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil, + "errorprone", "errorprone") + extraJarDeps = append(extraJarDeps, errorprone) } @@ -1303,6 +1317,21 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.outputFile = outputFile.WithoutRel() } +// Returns a copy of the supplied flags, but with all the errorprone-related +// fields copied to the regular build's fields. +func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags { + flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) + + if len(flags.errorProneExtraJavacFlags) > 0 { + if len(flags.javacFlags) > 0 { + flags.javacFlags += " " + flags.errorProneExtraJavacFlags + } else { + flags.javacFlags = flags.errorProneExtraJavacFlags + } + } + return flags +} + func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int, srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 1c7ad78ec..fc8f5571e 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -140,7 +140,7 @@ type commonBootclasspathFragment interface { // produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files. // // Updates the supplied hiddenAPIInfo with the paths to the generated files set. - produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput + produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput } var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil) @@ -465,7 +465,7 @@ func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android if unencodedDex == nil { // This is an error. Sometimes Soong will report the error directly, other times it will // defer the error reporting to happen only when trying to use the missing file in ninja. - // Either way it is handled by extractBootDexJarsFromHiddenAPIModules which must have been + // Either way it is handled by extractBootDexJarsFromModules which must have been // called before this as it generates the flags that are used to encode these files. continue } @@ -561,12 +561,9 @@ func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android. // TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment // modules have been updated to support it. if input.canPerformHiddenAPIProcessing(ctx, b.properties) { - // Get the content modules that contribute to the hidden API processing. - hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents) - // Delegate the production of the hidden API all-flags.csv file to a module type specific method. common := ctx.Module().(commonBootclasspathFragment) - output = common.produceHiddenAPIAllFlagsFile(ctx, hiddenAPIModules, input) + output = common.produceHiddenAPIAllFlagsFile(ctx, contents, input) } // Initialize a HiddenAPIInfo structure. @@ -620,7 +617,7 @@ func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.Modul // produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files) // for the fragment. -func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { +func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { // Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the // paths to the created files. return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input) @@ -648,7 +645,8 @@ func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android. } // Copy the dex jars of this fragment's content modules to their predefined locations. - copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths) + bootDexJarByModule := extractEncodedDexJarsFromModules(ctx, contents) + copyBootJarsToPredefinedLocations(ctx, bootDexJarByModule, imageConfig.dexPathsByModule) // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) @@ -763,7 +761,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. // Copy manually curated flag files specified on the bootclasspath_fragment. if b.Flag_files_by_category != nil { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := b.Flag_files_by_category[category] if len(paths) > 0 { dests := []string{} @@ -772,7 +770,7 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. builder.CopyToSnapshot(p, dest) dests = append(dests, dest) } - hiddenAPISet.AddProperty(category.propertyName, dests) + hiddenAPISet.AddProperty(category.PropertyName, dests) } } } @@ -840,7 +838,7 @@ func (module *prebuiltBootclasspathFragmentModule) Name() string { // produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is // specified. -func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput { +func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []android.Module, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput { pathForOptionalSrc := func(src *string) android.Path { if src == nil { // TODO(b/179354495): Fail if this is not provided once prebuilts have been updated. diff --git a/java/builder.go b/java/builder.go index cde87310f..ea011b8e1 100644 --- a/java/builder.go +++ b/java/builder.go @@ -279,23 +279,6 @@ func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.Writab transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc) } -func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath, - srcFiles, srcJars android.Paths, flags javaBuilderFlags) { - - flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) - - if len(flags.errorProneExtraJavacFlags) > 0 { - if len(flags.javacFlags) > 0 { - flags.javacFlags += " " + flags.errorProneExtraJavacFlags - } else { - flags.javacFlags = flags.errorProneExtraJavacFlags - } - } - - transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil, - "errorprone", "errorprone") -} - // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars // to compile with given set of builder flags, etc. func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int, diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index e1a36507a..dc8df5ec1 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -15,7 +15,6 @@ package java import ( - "fmt" "path/filepath" "sort" "strings" @@ -254,6 +253,9 @@ type bootImageConfig struct { dexPaths android.WritablePaths // for this image dexPathsDeps android.WritablePaths // for the dependency images and in this image + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + // File path to a zip archive with all image files (or nil, if not needed). zip android.WritablePath @@ -276,13 +278,24 @@ type bootImageVariant struct { dexLocationsDeps []string // for the dependency images and in this image // Paths to image files. - imagePathOnHost android.OutputPath // first image file path on host - imagePathOnDevice string // first image file path on device - imagesDeps android.OutputPaths // all files + imagePathOnHost android.OutputPath // first image file path on host + imagePathOnDevice string // first image file path on device + + // All the files that constitute this image variant, i.e. .art, .oat and .vdex files. + imagesDeps android.OutputPaths - // Only for extensions, paths to the primary boot images. + // The path to the primary image variant's imagePathOnHost field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. primaryImages android.OutputPath + // The paths to the primary image variant's imagesDeps field, where primary image variant + // means the image variant that this extends. + // + // This is only set for a variant of an image that extends another image. + primaryImagesDeps android.Paths + // Rules which should be used in make to install the outputs. installs android.RuleBuilderInstalls vdexInstalls android.RuleBuilderInstalls @@ -450,53 +463,27 @@ func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig return true } -// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to -// predefined paths in the global config. -func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) { - jarPaths := make(android.Paths, bootjars.Len()) - for i, module := range bootModules { - if module != nil { - bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() - jarPaths[i] = bootDexJar - - name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module)) - if bootjars.Jar(i) != name { - ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name) - } - } - } - - // The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction - // time, before the boot images are built (these paths are used in dexpreopt rule generation for - // Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined - // paths. - for i := range jarPaths { - input := jarPaths[i] - output := jarPathsPredefined[i] - module := bootjars.Jar(i) - if input == nil { - if ctx.Config().AllowMissingDependencies() { - apex := bootjars.Apex(i) - - // Create an error rule that pretends to create the output file but will actually fail if it - // is run. - ctx.Build(pctx, android.BuildParams{ - Rule: android.ErrorRule, - Output: output, - Args: map[string]string{ - "error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex), - }, - }) - } else { - ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+ - ", note that some jars may be filtered out by module constraints", module) - } - +// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined +// paths in the global config. +func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) { + // Create the super set of module names. + names := []string{} + names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...) + names = append(names, android.SortedStringKeys(dstBootJarsByModule)...) + names = android.SortedUniqueStrings(names) + for _, name := range names { + src := srcBootDexJarsByModule[name] + dst := dstBootJarsByModule[name] + + if src == nil { + ctx.ModuleErrorf("module %s does not provide a dex boot jar", name) + } else if dst == nil { + ctx.ModuleErrorf("module %s is not part of the boot configuration", name) } else { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, - Input: input, - Output: output, + Input: src, + Output: dst, }) } } @@ -588,7 +575,15 @@ func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, p cmd. Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":"). - FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage) + // Add the path to the first file in the boot image with the arch specific directory removed, + // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path + // to the file cannot be passed to the command make sure to add the actual path as an Implicit + // dependency to ensure that it is built before the command runs. + FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage). + // Similarly, the dex2oat tool will automatically find the paths to other files in the base + // boot image so make sure to add them as implicit dependencies to ensure that they are built + // before this command is run. + Implicits(image.primaryImagesDeps) } else { // It is a primary image, so it needs a base address. cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()) diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 39a3e11a5..b13955fba 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -100,6 +100,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { // TODO(b/143682396): use module dependencies instead inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input") c.dexPaths = c.modules.BuildPaths(ctx, inputDir) + c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir) c.dexPathsDeps = c.dexPaths // Create target-specific variants. @@ -125,6 +126,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...) for i := range targets { frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost + frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths() frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...) } @@ -152,6 +154,9 @@ type updatableBootConfig struct { // later on a singleton adds commands to copy actual jars to the predefined paths. dexPaths android.WritablePaths + // Map from module name (without prebuilt_ prefix) to the predefined build path. + dexPathsByModule map[string]android.WritablePath + // A list of dex locations (a.k.a. on-device paths) to the boot jars. dexLocations []string } @@ -165,10 +170,11 @@ func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig { dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars") dexPaths := updatableBootJars.BuildPaths(ctx, dir) + dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir) dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android) - return updatableBootConfig{updatableBootJars, dexPaths, dexLocations} + return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations} }).(updatableBootConfig) } diff --git a/java/hiddenapi.go b/java/hiddenapi.go index e9693c68e..f901434a0 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -219,7 +219,6 @@ func buildRuleToGenerateIndex(ctx android.ModuleContext, desc string, classesJar BuiltTool("merge_csv"). Flag("--zip_input"). Flag("--key_field signature"). - FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). FlagWithOutput("--output=", indexCSV). Inputs(classesJars) rule.Build(desc, desc) diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index f2649d3c0..643c5cba2 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -248,8 +248,8 @@ type HiddenAPIFlagFileProperties struct { } type hiddenAPIFlagFileCategory struct { - // propertyName is the name of the property for this category. - propertyName string + // PropertyName is the name of the property for this category. + PropertyName string // propertyValueReader retrieves the value of the property for this category from the set of // properties. @@ -262,12 +262,12 @@ type hiddenAPIFlagFileCategory struct { // The flag file category for removed members of the API. // -// This is extracted from hiddenAPIFlagFileCategories as it is needed to add the dex signatures +// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures // list of removed API members that are generated automatically from the removed.txt files provided // by API stubs. var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Removed - propertyName: "removed", + PropertyName: "removed", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Removed }, @@ -276,10 +276,10 @@ var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ }, } -var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ +var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Unsupported { - propertyName: "unsupported", + PropertyName: "unsupported", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Unsupported }, @@ -290,7 +290,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ hiddenAPIRemovedFlagFileCategory, // See HiddenAPIFlagFileProperties.Max_target_r_low_priority { - propertyName: "max_target_r_low_priority", + PropertyName: "max_target_r_low_priority", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_r_low_priority }, @@ -300,7 +300,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_q { - propertyName: "max_target_q", + PropertyName: "max_target_q", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_q }, @@ -310,7 +310,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_p { - propertyName: "max_target_p", + PropertyName: "max_target_p", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_p }, @@ -320,7 +320,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Max_target_o_low_priority { - propertyName: "max_target_o_low_priority", + PropertyName: "max_target_o_low_priority", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Max_target_o_low_priority }, @@ -330,7 +330,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Blocked { - propertyName: "blocked", + PropertyName: "blocked", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Blocked }, @@ -340,7 +340,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, // See HiddenAPIFlagFileProperties.Unsupported_packages { - propertyName: "unsupported_packages", + PropertyName: "unsupported_packages", propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { return properties.Unsupported_packages }, @@ -355,7 +355,7 @@ type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths // append appends the supplied flags files to the corresponding category in this map. func (s FlagFilesByCategory) append(other FlagFilesByCategory) { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { s[category] = append(s[category], other[category]...) } } @@ -540,7 +540,7 @@ func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, conten // extractFlagFilesFromProperties extracts the paths to flag files that are specified in the // supplied properties and stores them in this struct. func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) { - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p)) i.FlagFilesByCategory[category] = paths } @@ -571,6 +571,15 @@ type HiddenAPIFlagOutput struct { AllFlagsPath android.Path } +// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex +// path. +type bootDexJarByModule map[string]android.Path + +// addPath adds the path for a module to the map. +func (b bootDexJarByModule) addPath(module android.Module, path android.Path) { + b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path +} + // pathForValidation creates a path of the same type as the supplied type but with a name of // <path>.valid. // @@ -630,7 +639,7 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st FlagWithOutput("--output ", tempPath) // Add the options for the different categories of flag files. - for _, category := range hiddenAPIFlagFileCategories { + for _, category := range HiddenAPIFlagFileCategories { paths := flagFilesByCategory[category] for _, path := range paths { category.commandMutator(command, path) @@ -670,11 +679,11 @@ func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc st // * metadata.csv // * index.csv // * all-flags.csv -func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { +func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { hiddenApiSubDir := "modular-hiddenapi" // Gather the dex files for the boot libraries provided by this fragment. - bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, contents) + bootDexJars := extractBootDexJarsFromModules(ctx, contents) // Generate the stub-flags.csv. stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv") @@ -682,7 +691,7 @@ func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags") // Extract the classes jars from the contents. - classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents) + classesJars := extractClassesJarsFromModules(contents) // Generate the set of flags from the annotations in the source code. annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv") @@ -737,29 +746,18 @@ func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedT return android.OptionalPathForPath(output) } -// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents. -func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule { - hiddenAPIModules := []hiddenAPIModule{} - for _, module := range contents { - if hiddenAPI, ok := module.(hiddenAPIModule); ok { - hiddenAPIModules = append(hiddenAPIModules, hiddenAPI) - } else if _, ok := module.(*DexImport); ok { - // Ignore this for the purposes of hidden API processing - } else { - ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module) - } - } - return hiddenAPIModules -} - -// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules. -func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { +// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules. +func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) android.Paths { bootDexJars := android.Paths{} for _, module := range contents { - bootDexJar := module.bootDexJar() + hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module) + if hiddenAPIModule == nil { + continue + } + bootDexJar := hiddenAPIModule.bootDexJar() if bootDexJar == nil { if ctx.Config().AlwaysUsePrebuiltSdks() { - // TODO(b/179354495): Remove this work around when it is unnecessary. + // TODO(b/179354495): Remove this workaround when it is unnecessary. // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, // create a fake one that will cause a build error only if it is used. fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name()) @@ -784,11 +782,142 @@ func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents return bootDexJars } -// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules. -func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { +func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule { + if hiddenAPIModule, ok := module.(hiddenAPIModule); ok { + return hiddenAPIModule + } else if _, ok := module.(*DexImport); ok { + // Ignore this for the purposes of hidden API processing + } else { + ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module) + } + + return nil +} + +// extractClassesJarsFromModules extracts the class jars from the supplied modules. +func extractClassesJarsFromModules(contents []android.Module) android.Paths { classesJars := android.Paths{} for _, module := range contents { - classesJars = append(classesJars, module.classesJars()...) + classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...) } return classesJars } + +// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module. +func retrieveClassesJarsFromModule(module android.Module) android.Paths { + if hiddenAPIModule, ok := module.(hiddenAPIModule); ok { + return hiddenAPIModule.classesJars() + } + + return nil +} + +// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by +// Soong but should instead only be reported in ninja if the file is actually built. +func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool { + // TODO(b/179354495): Remove this workaround when it is unnecessary. + // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, + // create a fake one that will cause a build error only if it is used. + if ctx.Config().AlwaysUsePrebuiltSdks() { + return true + } + + // This is called for both platform_bootclasspath and bootclasspath_fragment modules. + // + // A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules. + // Ideally, a bootclasspath_fragment module should never have a platform variant created for it + // but unfortunately, due to b/187910671 it does. + // + // That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module + // used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e. + // has an APEX variant not a platform variant. + // + // There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot + // provide a boot dex jar: + // 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it + // does not have an APEX variant and only has a platform variant and neither do its content + // modules. + // 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all + // java_sdk_library_import modules to be treated as preferred and as many of them are not part + // of an apex they cannot provide a boot dex jar. + // + // The first case causes problems when the affected prebuilt modules are preferred but that is an + // invalid configuration and it is ok for it to fail as the work to enable that is not yet + // complete. The second case is used for building targets that do not use boot dex jars and so + // deferring error reporting to ninja is fine as the affected ninja targets should never be built. + // That is handled above. + // + // A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike + // the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it + // can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed + // that if the library can be part of an APEX then it is the APEX variant that is used. + // + // This check handles the slightly different requirements of the bootclasspath_fragment and + // platform_bootclasspath modules by only deferring error reporting for the platform variant of + // a prebuilt modules that has other variants which are part of an APEX. + // + // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily. + if android.IsModulePrebuilt(module) { + if am, ok := module.(android.ApexModule); ok && am.InAnyApex() { + apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + return true + } + } + } + + // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there + // is no equivalently versioned prebuilt APEX file from which it can be obtained. However, + // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build + // failures missing boot dex jars need to be deferred. + if android.IsModuleInVersionedSdk(ctx.Module()) { + return true + } + + return false +} + +// handleMissingDexBootFile will either log a warning or create an error rule to create the fake +// file depending on the value returned from deferReportingMissingBootDexJar. +func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) { + if deferReportingMissingBootDexJar(ctx, module) { + // Create an error rule that pretends to create the output file but will actually fail if it + // is run. + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: fake, + Args: map[string]string{ + "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + }, + }) + } else { + ctx.ModuleErrorf("module %s does not provide a dex jar", module) + } +} + +// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's +// DexJarBuildPath() method. +// +// The returned path will usually be to a dex jar file that has been encoded with hidden API flags. +// However, under certain conditions, e.g. errors, or special build configurations it will return +// a path to a fake file. +func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path { + bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() + if bootDexJar == nil { + fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name())) + bootDexJar = fake + + handleMissingDexBootFile(ctx, module, fake) + } + return bootDexJar +} + +// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules. +func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule { + encodedDexJarsByModuleName := bootDexJarByModule{} + for _, module := range contents { + path := retrieveEncodedBootDexJarFromModule(ctx, module) + encodedDexJarsByModuleName.addPath(module, path) + } + return encodedDexJarsByModuleName +} diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go index a6bf8c705..edf42351f 100644 --- a/java/hiddenapi_monolithic.go +++ b/java/hiddenapi_monolithic.go @@ -99,4 +99,4 @@ func (i *MonolithicHiddenAPIInfo) dedup() { i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths) } -var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) +var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) diff --git a/java/java_test.go b/java/java_test.go index bd373c177..78d9ab4c7 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1392,3 +1392,31 @@ func TestDefaultInstallable(t *testing.T) { assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true), module.properties.Installable) } + +func TestErrorproneEnabled(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "foo", + srcs: ["a.java"], + errorprone: { + enabled: true, + }, + } + `) + + javac := ctx.ModuleForTests("foo", "android_common").Description("javac") + + // Test that the errorprone plugins are passed to javac + expectedSubstring := "-Xplugin:ErrorProne" + if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) { + t.Errorf("expected javacFlags to conain %q, got %q", expectedSubstring, javac.Args["javacFlags"]) + } + + // Modules with errorprone { enabled: true } will include errorprone checks + // in the main javac build rule. Only when RUN_ERROR_PRONE is true will + // the explicit errorprone build rule be created. + errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone") + if errorprone.RuleParams.Description != "" { + t.Errorf("expected errorprone build rule to not exist, but it did") + } +} diff --git a/java/kotlin_test.go b/java/kotlin_test.go index 1c146a192..fd2f3ca61 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -185,7 +185,6 @@ func TestKapt(t *testing.T) { buildOS := android.BuildOs.String() kapt := result.ModuleForTests("foo", "android_common").Rule("kapt") - //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc") javac := result.ModuleForTests("foo", "android_common").Description("javac") errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone") diff --git a/java/lint.go b/java/lint.go index 9f769dfe3..1511cfe26 100644 --- a/java/lint.go +++ b/java/lint.go @@ -361,10 +361,7 @@ func (l *linter) lint(ctx android.ModuleContext) { Labels: map[string]string{"type": "tool", "name": "lint"}, ExecStrategy: lintRBEExecStrategy(ctx), ToolchainInputs: []string{config.JavaCmd(ctx).String()}, - EnvironmentVariables: []string{ - "LANG", - }, - Platform: map[string]string{remoteexec.PoolKey: pool}, + Platform: map[string]string{remoteexec.PoolKey: pool}, }) } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 87c695cb5..fba73d0c1 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -280,7 +280,6 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. } monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments) - // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile input := newHiddenAPIFlagInput() @@ -291,16 +290,14 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. // Use the flag files from this module and all the fragments. input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory - hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules) - // Generate the monolithic stub-flags.csv file. - bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules) + bootDexJars := extractBootDexJarsFromModules(ctx, modules) stubFlags := hiddenAPISingletonPaths(ctx).stubFlags rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input) rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags") // Extract the classes jars from the contents. - classesJars := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules) + classesJars := extractClassesJarsFromModules(modules) // Generate the annotation-flags.csv file from all the module annotations. annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv") @@ -342,7 +339,7 @@ func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ct monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments) // Store the information for testing. - ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo) + ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo) return monolithicInfo } @@ -390,11 +387,13 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules) // Copy non-updatable module dex jars to their predefined locations. - copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths) + nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules) + copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule) // Copy updatable module dex jars to their predefined locations. config := GetUpdatableBootConfig(ctx) - copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths) + updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules) + copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule) // Build a profile for the image config and then use that to build the boot image. profile := bootImageProfileRule(ctx, imageConfig) diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index ed5549d35..0318a07d4 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -15,8 +15,6 @@ package java import ( - "fmt" - "strings" "testing" "android/soong/android" @@ -152,116 +150,6 @@ func TestPlatformBootclasspath(t *testing.T) { }) } -func TestPlatformBootclasspath_Fragments(t *testing.T) { - result := android.GroupFixturePreparers( - prepareForTestWithPlatformBootclasspath, - PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("foo"), - android.FixtureWithRootAndroidBp(` - platform_bootclasspath { - name: "platform-bootclasspath", - fragments: [ - {module:"bar-fragment"}, - ], - hidden_api: { - unsupported: [ - "unsupported.txt", - ], - removed: [ - "removed.txt", - ], - max_target_r_low_priority: [ - "max-target-r-low-priority.txt", - ], - max_target_q: [ - "max-target-q.txt", - ], - max_target_p: [ - "max-target-p.txt", - ], - max_target_o_low_priority: [ - "max-target-o-low-priority.txt", - ], - blocked: [ - "blocked.txt", - ], - unsupported_packages: [ - "unsupported-packages.txt", - ], - }, - } - - bootclasspath_fragment { - name: "bar-fragment", - contents: ["bar"], - api: { - stub_libs: ["foo"], - }, - hidden_api: { - unsupported: [ - "bar-unsupported.txt", - ], - removed: [ - "bar-removed.txt", - ], - max_target_r_low_priority: [ - "bar-max-target-r-low-priority.txt", - ], - max_target_q: [ - "bar-max-target-q.txt", - ], - max_target_p: [ - "bar-max-target-p.txt", - ], - max_target_o_low_priority: [ - "bar-max-target-o-low-priority.txt", - ], - blocked: [ - "bar-blocked.txt", - ], - unsupported_packages: [ - "bar-unsupported-packages.txt", - ], - }, - } - - java_library { - name: "bar", - srcs: ["a.java"], - system_modules: "none", - sdk_version: "none", - compile_dex: true, - } - - java_sdk_library { - name: "foo", - srcs: ["a.java"], - public: { - enabled: true, - }, - compile_dex: true, - } - `), - ).RunTest(t) - - pbcp := result.Module("platform-bootclasspath", "android_common") - info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo) - - for _, category := range hiddenAPIFlagFileCategories { - name := category.propertyName - message := fmt.Sprintf("category %s", name) - filename := strings.ReplaceAll(name, "_", "-") - expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)} - android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category]) - } - - android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths) - android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths) - android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/metadata.csv"}, info.MetadataPaths) - android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/index.csv"}, info.IndexPaths) - android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths) -} - func TestPlatformBootclasspathVariant(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithPlatformBootclasspath, diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go index ef4672a73..9e7a0f1e3 100644 --- a/remoteexec/remoteexec.go +++ b/remoteexec/remoteexec.go @@ -50,8 +50,14 @@ const ( ) var ( - defaultLabels = map[string]string{"type": "tool"} - defaultExecStrategy = LocalExecStrategy + defaultLabels = map[string]string{"type": "tool"} + defaultExecStrategy = LocalExecStrategy + defaultEnvironmentVariables = []string{ + // This is a subset of the allowlist in ui/build/ninja.go that makes sense remotely. + "LANG", + "LC_MESSAGES", + "PYTHONDONTWRITEBYTECODE", + } ) // REParams holds information pertinent to the remote execution of a rule. @@ -149,8 +155,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, ",") + envVarAllowlist := append(r.EnvironmentVariables, defaultEnvironmentVariables...) + + if len(envVarAllowlist) > 0 { + args += " --env_var_allowlist=" + strings.Join(envVarAllowlist, ",") } return args + " -- " diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go index b117b8915..368631609 100644 --- a/remoteexec/remoteexec_test.go +++ b/remoteexec/remoteexec_test.go @@ -36,7 +36,7 @@ func TestTemplate(t *testing.T) { PoolKey: "default", }, }, - want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage), + want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage), }, { name: "all params", @@ -52,7 +52,7 @@ func TestTemplate(t *testing.T) { PoolKey: "default", }, }, - want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage), + want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage), }, } for _, test := range tests { @@ -74,7 +74,7 @@ func TestNoVarTemplate(t *testing.T) { PoolKey: "default", }, } - want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage) + want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage) if got := params.NoVarTemplate(DefaultWrapperPath); got != want { t.Errorf("NoVarTemplate() returned\n%s\nwant\n%s", got, want) } @@ -90,7 +90,7 @@ func TestTemplateDeterminism(t *testing.T) { PoolKey: "default", }, } - want := fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage) + want := fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage) for i := 0; i < 1000; i++ { if got := r.Template(); got != want { t.Fatalf("Template() returned\n%s\nwant\n%s", got, want) diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py index b047aab71..a65326c51 100755 --- a/scripts/hiddenapi/merge_csv.py +++ b/scripts/hiddenapi/merge_csv.py @@ -55,14 +55,15 @@ else: if entry.endswith('.uau'): csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r')))) -headers = set() if args.header: fieldnames = args.header.split(',') else: + headers = {} # Build union of all columns from source files: for reader in csv_readers: - headers = headers.union(reader.fieldnames) - fieldnames = sorted(headers) + for fieldname in reader.fieldnames: + headers[fieldname] = "" + fieldnames = list(headers.keys()) # By default chain the csv readers together so that the resulting output is # the concatenation of the rows from each of them: diff --git a/scripts/rbc-run b/scripts/rbc-run new file mode 100755 index 000000000..e2fa6d1be --- /dev/null +++ b/scripts/rbc-run @@ -0,0 +1,16 @@ +#! /bin/bash +# Convert and run one configuration +# Args: <product>-<variant> +[[ $# -eq 1 && "$1" =~ ^(.*)-(.*)$ ]] || { echo Usage: ${0##*/} PRODUCT-VARIANT >&2; exit 1; } +declare -r product="${BASH_REMATCH[1]:-aosp_arm}" +declare -r variant="${BASH_REMATCH[2]:-eng}" +set -eu +declare -r output_root=${OUT_DIR:-out} +declare -r runner="$output_root/soong/.bootstrap/bin/rbcrun" +declare -r converter="$output_root/soong/.bootstrap/bin/mk2rbc" +declare -r launcher=$output_root/launchers/run.rbc +$converter -mode=write -r --outdir $output_root --launcher=$launcher $product +printf "#TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant\n" +env TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant \ + $runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $launcher + diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index d9fe2816f..f2ab6a198 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -15,12 +15,53 @@ package sdk import ( + "fmt" + "path/filepath" "testing" "android/soong/android" "android/soong/java" ) +// fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that +// references the bootclasspath fragment. +func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer { + return android.GroupFixturePreparers( + // Add a platform_bootclasspath module. + android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(` + platform_bootclasspath { + name: "platform-bootclasspath", + fragments: [ + { + apex: "%s", + module: "%s", + }, + ], + } + `, apex, fragment)), + android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil), + ) +} + +// fixtureAddPrebuiltApexForBootclasspathFragment adds a prebuilt_apex that exports the fragment. +func fixtureAddPrebuiltApexForBootclasspathFragment(apex, fragment string) android.FixturePreparer { + apexFile := fmt.Sprintf("%s.apex", apex) + dir := "prebuilts/apex" + return android.GroupFixturePreparers( + // A preparer to add a prebuilt apex to the test fixture. + android.FixtureAddTextFile(filepath.Join(dir, "Android.bp"), fmt.Sprintf(` + prebuilt_apex { + name: "%s", + src: "%s", + exported_bootclasspath_fragments: [ + "%s", + ], + } + `, apex, apexFile, fragment)), + android.FixtureAddFile(filepath.Join(dir, apexFile), nil), + ) +} + func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) { result := android.GroupFixturePreparers( prepareForSdkTestWithJava, @@ -34,20 +75,8 @@ func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) { "system/sepolicy/apex/com.android.art-file_contexts": nil, }), - // platform_bootclasspath that depends on the fragment. - android.FixtureAddTextFile("frameworks/base/boot/Android.bp", ` - platform_bootclasspath { - name: "platform-bootclasspath", - fragments: [ - { - apex: "com.android.art", - module: "mybootclasspathfragment", - }, - ], - } - `), - // Needed for platform_bootclasspath - android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil), + // Add a platform_bootclasspath that depends on the fragment. + fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"), java.FixtureConfigureBootJars("com.android.art:mybootlib"), android.FixtureWithRootAndroidBp(` @@ -89,19 +118,8 @@ func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) { `), ).RunTest(t) - // A preparer to add a prebuilt apex to the test fixture. - prepareWithPrebuiltApex := android.GroupFixturePreparers( - android.FixtureAddTextFile("prebuilts/apex/Android.bp", ` - prebuilt_apex { - name: "com.android.art", - src: "art.apex", - exported_bootclasspath_fragments: [ - "mybootclasspathfragment", - ], - } - `), - android.FixtureAddFile("prebuilts/apex/art.apex", nil), - ) + // A preparer to update the test fixture used when processing an unpackage snapshot. + preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment") CheckSnapshot(t, result, "mysdk", "", checkUnversionedAndroidBpContents(` @@ -154,9 +172,9 @@ sdk_snapshot { checkAllCopyRules(` .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar `), - snapshotTestPreparer(checkSnapshotWithoutSource, prepareWithPrebuiltApex), - snapshotTestPreparer(checkSnapshotWithSourcePreferred, prepareWithPrebuiltApex), - snapshotTestPreparer(checkSnapshotPreferredWithSource, prepareWithPrebuiltApex), + snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot), ) } @@ -166,6 +184,12 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { java.PrepareForTestWithJavaDefaultModules, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), + java.FixtureConfigureUpdatableBootJars("myapex:mybootlib", "myapex:myothersdklibrary"), + prepareForSdkTestWithApex, + + // Add a platform_bootclasspath that depends on the fragment. + fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"), + android.FixtureWithRootAndroidBp(` sdk { name: "mysdk", @@ -179,8 +203,16 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { ], } + apex { + name: "myapex", + key: "myapex.key", + min_sdk_version: "2", + bootclasspath_fragments: ["mybootclasspathfragment"], + } + bootclasspath_fragment { name: "mybootclasspathfragment", + apex_available: ["myapex"], contents: [ // This should be automatically added to the sdk_snapshot as a java_boot_libs module. "mybootlib", @@ -198,35 +230,48 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { java_library { name: "mybootlib", + apex_available: ["myapex"], srcs: ["Test.java"], system_modules: "none", sdk_version: "none", + min_sdk_version: "2", compile_dex: true, + permitted_packages: ["mybootlib"], } java_sdk_library { name: "mysdklibrary", + apex_available: ["myapex"], srcs: ["Test.java"], shared_library: false, public: {enabled: true}, + min_sdk_version: "2", } java_sdk_library { name: "myothersdklibrary", + apex_available: ["myapex"], srcs: ["Test.java"], shared_library: false, public: {enabled: true}, + min_sdk_version: "2", + permitted_packages: ["myothersdklibrary"], } java_sdk_library { name: "mycoreplatform", + apex_available: ["myapex"], srcs: ["Test.java"], shared_library: false, public: {enabled: true}, + min_sdk_version: "2", } `), ).RunTest(t) + // A preparer to update the test fixture used when processing an unpackage snapshot. + preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment") + CheckSnapshot(t, result, "mysdk", "", checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. @@ -235,7 +280,7 @@ prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], contents: [ "mybootlib", "myothersdklibrary", @@ -259,7 +304,7 @@ java_import { name: "mybootlib", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], jars: ["java/mybootlib.jar"], } @@ -267,7 +312,7 @@ java_sdk_library_import { name: "myothersdklibrary", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], @@ -282,7 +327,7 @@ java_sdk_library_import { name: "mysdklibrary", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/mysdklibrary-stubs.jar"], @@ -297,7 +342,7 @@ java_sdk_library_import { name: "mycoreplatform", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], @@ -315,7 +360,7 @@ prebuilt_bootclasspath_fragment { name: "mysdk_mybootclasspathfragment@current", sdk_member_name: "mybootclasspathfragment", visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], contents: [ "mysdk_mybootlib@current", "mysdk_myothersdklibrary@current", @@ -339,7 +384,7 @@ java_import { name: "mysdk_mybootlib@current", sdk_member_name: "mybootlib", visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], jars: ["java/mybootlib.jar"], } @@ -347,7 +392,7 @@ java_sdk_library_import { name: "mysdk_myothersdklibrary@current", sdk_member_name: "myothersdklibrary", visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], @@ -362,7 +407,7 @@ java_sdk_library_import { name: "mysdk_mysdklibrary@current", sdk_member_name: "mysdklibrary", visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/mysdklibrary-stubs.jar"], @@ -377,7 +422,7 @@ java_sdk_library_import { name: "mysdk_mycoreplatform@current", sdk_member_name: "mycoreplatform", visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], shared_library: false, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], @@ -416,7 +461,11 @@ sdk_snapshot { .intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar .intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt .intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt -`)) +`), + snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot), + ) } // Test that bootclasspath_fragment works with sdk. @@ -482,7 +531,12 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { java.PrepareForTestWithJavaDefaultModules, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("mysdklibrary"), + java.FixtureConfigureUpdatableBootJars("myapex:mybootlib"), prepareForSdkTestWithApex, + + // Add a platform_bootclasspath that depends on the fragment. + fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"), + android.MockFS{ "my-blocked.txt": nil, "my-max-target-o-low-priority.txt": nil, @@ -549,6 +603,7 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { sdk_version: "none", min_sdk_version: "1", compile_dex: true, + permitted_packages: ["mybootlib"], } java_sdk_library { @@ -560,6 +615,9 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { `), ).RunTest(t) + // A preparer to update the test fixture used when processing an unpackage snapshot. + preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment") + CheckSnapshot(t, result, "mysdk", "", checkUnversionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. @@ -633,5 +691,8 @@ my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt `), + snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot), + snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot), ) } diff --git a/ui/build/soong.go b/ui/build/soong.go index 712841465..19a47ae7c 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -99,6 +99,21 @@ func environmentArgs(config Config, suffix string) []string { "--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix), } } + +func writeEmptyGlobFile(ctx Context, path string) { + err := os.MkdirAll(filepath.Dir(path), 0777) + if err != nil { + ctx.Fatalf("Failed to create parent directories of empty ninja glob file '%s': %s", path, err) + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + err = ioutil.WriteFile(path, nil, 0666) + if err != nil { + ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err) + } + } +} + func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) { ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap") defer ctx.EndTrace() @@ -106,8 +121,10 @@ func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) { var args bootstrap.Args mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja") - globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja") bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja") + // .bootstrap/build.ninja "includes" .bootstrap/build-globs.ninja for incremental builds + // generate an empty glob before running any rule in .bootstrap/build.ninja + writeEmptyGlobFile(ctx, bootstrapGlobFile) bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d") args.RunGoTests = !config.skipSoongTests @@ -117,7 +134,10 @@ func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) { args.TopFile = "Android.bp" args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list") args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja") - args.GlobFile = globFile + // The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d) + // Building soong_build does not require a glob file + // Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary + args.GlobFile = "" args.GeneratingPrimaryBuilder = true args.EmptyNinjaFile = config.EmptyNinjaFile() |