diff options
49 files changed, 1035 insertions, 263 deletions
diff --git a/android/bazel.go b/android/bazel.go index 8341e5725..bfd0f909a 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -150,17 +150,23 @@ var ( "build/bazel/platforms":/* recursive = */ true, "build/bazel/product_variables":/* recursive = */ true, "build/bazel_common_rules":/* recursive = */ true, + "build/make/tools":/* recursive = */ true, "build/pesto":/* recursive = */ true, // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed "external/bazelbuild-rules_android":/* recursive = */ true, "external/bazel-skylib":/* recursive = */ true, + "external/guava":/* recursive = */ true, + "external/error_prone":/* recursive = */ true, + "external/jsr305":/* recursive = */ true, + "frameworks/ex/common":/* recursive = */ true, "prebuilts/sdk":/* recursive = */ false, "prebuilts/sdk/tools":/* recursive = */ false, "prebuilts/r8":/* recursive = */ false, "packages/apps/Music":/* recursive = */ true, + "packages/apps/QuickSearchBox":/* recursive = */ true, } // Configure modules in these directories to enable bp2build_available: true or false by default. @@ -259,7 +265,6 @@ var ( // Per-module denylist of cc_library modules to only generate the static // variant if their shared variant isn't ready or buildable by Bazel. bp2buildCcLibraryStaticOnlyList = []string{ - "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno "libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets. } @@ -294,8 +299,8 @@ func init() { } } -func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool { - return bp2buildCcLibraryStaticOnly[ctx.Module().Name()] +func GenerateCcLibraryStaticOnly(moduleName string) bool { + return bp2buildCcLibraryStaticOnly[moduleName] } func ShouldKeepExistingBuildFileForDir(dir string) bool { @@ -325,7 +330,7 @@ func (b *BazelModuleBase) MixedBuildsEnabled(ctx BazelConversionPathContext) boo return false } - if GenerateCcLibraryStaticOnly(ctx) { + if GenerateCcLibraryStaticOnly(ctx.Module().Name()) { // Don't use partially-converted cc_library targets in mixed builds, // since mixed builds would generally rely on both static and shared // variants of a cc_library. diff --git a/android/bazel_paths.go b/android/bazel_paths.go index ccbc156bc..b5746f750 100644 --- a/android/bazel_paths.go +++ b/android/bazel_paths.go @@ -15,11 +15,12 @@ package android import ( - "android/soong/bazel" "fmt" "path/filepath" "strings" + "android/soong/bazel" + "github.com/google/blueprint" "github.com/google/blueprint/pathtools" ) @@ -84,24 +85,8 @@ type BazelConversionPathContext interface { // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>" // 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) +func BazelLabelForModuleDeps(ctx TopDownMutatorContext, modules []string) bazel.LabelList { + return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel) } // BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in @@ -110,11 +95,15 @@ func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, ex // 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 BazelLabelForModuleDepsExcludes(ctx TopDownMutatorContext, modules, excludes []string) bazel.LabelList { + return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel) } -func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList { +// BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>" +// or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label +// which corresponds to dependencies on the module within the given ctx. +func BazelLabelForModuleDepsWithFn(ctx TopDownMutatorContext, modules []string, + moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList { var labels bazel.LabelList // In some cases, a nil string list is different than an explicitly empty list. if len(modules) == 0 && modules != nil { @@ -127,7 +116,7 @@ func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, i module = ":" + module } if m, t := SrcIsModuleWithTag(module); m != "" { - l := getOtherModuleLabel(ctx, m, t, isWholeLibs) + l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn) l.OriginalModuleName = bpText labels.Includes = append(labels.Includes, l) } else { @@ -137,23 +126,29 @@ func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, i return labels } -func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList { - moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs) +// BazelLabelForModuleDepsExcludesWithFn 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 applies moduleToLabelFn to determine and return a +// Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and +// the excluded dependencies. +func BazelLabelForModuleDepsExcludesWithFn(ctx TopDownMutatorContext, modules, excludes []string, + moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList { + moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn) if len(excludes) == 0 { return moduleLabels } - excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs) + excludeLabels := BazelLabelForModuleDepsWithFn(ctx, excludes, moduleToLabelFn) return bazel.LabelList{ Includes: moduleLabels.Includes, Excludes: excludeLabels.Includes, } } -func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label { +func BazelLabelForModuleSrcSingle(ctx TopDownMutatorContext, path string) bazel.Label { return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0] } -func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label { +func BazelLabelForModuleDepSingle(ctx TopDownMutatorContext, path string) bazel.Label { return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0] } @@ -163,7 +158,7 @@ func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) b // relative if within the same package). // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules // will have already been handled by the path_deps mutator. -func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList { +func BazelLabelForModuleSrc(ctx TopDownMutatorContext, paths []string) bazel.LabelList { return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil)) } @@ -173,7 +168,7 @@ func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) baze // (absolute if in a different package or relative if within the same package). // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules // will have already been handled by the path_deps mutator. -func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList { +func BazelLabelForModuleSrcExcludes(ctx TopDownMutatorContext, paths, excludes []string) bazel.LabelList { excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil)) excluded := make([]string, 0, len(excludeLabels.Includes)) for _, e := range excludeLabels.Includes { @@ -293,7 +288,7 @@ func transformSubpackagePaths(ctx BazelConversionPathContext, paths bazel.LabelL // Properties passed as the paths or excludes argument must have been annotated with struct tag // `android:"path"` so that dependencies on other modules will have already been handled by the // path_deps mutator. -func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList { +func expandSrcsForBazel(ctx TopDownMutatorContext, paths, expandedExcludes []string) bazel.LabelList { if paths == nil { return bazel.LabelList{} } @@ -310,7 +305,7 @@ func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes for _, p := range paths { if m, tag := SrcIsModuleWithTag(p); m != "" { - l := getOtherModuleLabel(ctx, m, tag, false) + l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel) if !InList(l.Label, expandedExcludes) { l.OriginalModuleName = fmt.Sprintf(":%s", m) labels.Includes = append(labels.Includes, l) @@ -341,7 +336,8 @@ 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, isWholeLibs bool) bazel.Label { +func getOtherModuleLabel(ctx TopDownMutatorContext, dep, tag string, + labelFromModule func(TopDownMutatorContext, blueprint.Module) string) bazel.Label { m, _ := ctx.ModuleFromName(dep) if m == nil { panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name())) @@ -349,13 +345,11 @@ func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWhol if !convertedToBazel(ctx, m) { ctx.AddUnconvertedBp2buildDep(dep) } - otherLabel := bazelModuleLabel(ctx, m, tag) - label := bazelModuleLabel(ctx, ctx.Module(), "") - if isWholeLibs { - if m, ok := m.(Module); ok && IsModulePrebuilt(m) { - otherLabel += "_alwayslink" - } - } + label := BazelModuleLabel(ctx, ctx.Module()) + otherLabel := labelFromModule(ctx, m) + + // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. + if samePackage(label, otherLabel) { otherLabel = bazelShortLabel(otherLabel) } @@ -365,7 +359,7 @@ func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWhol } } -func bazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module, tag string) string { +func BazelModuleLabel(ctx TopDownMutatorContext, module blueprint.Module) string { // TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets. if !convertedToBazel(ctx, module) { return bp2buildModuleLabel(ctx, module) diff --git a/android/paths.go b/android/paths.go index 763cd7c40..2e378baa5 100644 --- a/android/paths.go +++ b/android/paths.go @@ -263,38 +263,56 @@ func ResPathWithName(ctx ModuleOutPathContext, p Path, name string) ModuleResPat // OptionalPath is a container that may or may not contain a valid Path. type OptionalPath struct { - valid bool - path Path + path Path // nil if invalid. + invalidReason string // Not applicable if path != nil. "" if the reason is unknown. } // OptionalPathForPath returns an OptionalPath containing the path. func OptionalPathForPath(path Path) OptionalPath { - if path == nil { - return OptionalPath{} - } - return OptionalPath{valid: true, path: path} + return OptionalPath{path: path} +} + +// InvalidOptionalPath returns an OptionalPath that is invalid with the given reason. +func InvalidOptionalPath(reason string) OptionalPath { + + return OptionalPath{invalidReason: reason} } // Valid returns whether there is a valid path func (p OptionalPath) Valid() bool { - return p.valid + return p.path != nil } // Path returns the Path embedded in this OptionalPath. You must be sure that // there is a valid path, since this method will panic if there is not. func (p OptionalPath) Path() Path { - if !p.valid { - panic("Requesting an invalid path") + if p.path == nil { + msg := "Requesting an invalid path" + if p.invalidReason != "" { + msg += ": " + p.invalidReason + } + panic(msg) } return p.path } +// InvalidReason returns the reason that the optional path is invalid, or "" if it is valid. +func (p OptionalPath) InvalidReason() string { + if p.path != nil { + return "" + } + if p.invalidReason == "" { + return "unknown" + } + return p.invalidReason +} + // AsPaths converts the OptionalPath into Paths. // // It returns nil if this is not valid, or a single length slice containing the Path embedded in // this OptionalPath. func (p OptionalPath) AsPaths() Paths { - if !p.valid { + if p.path == nil { return nil } return Paths{p.path} @@ -303,7 +321,7 @@ func (p OptionalPath) AsPaths() Paths { // RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the // result of calling Path.RelativeToTop on it. func (p OptionalPath) RelativeToTop() OptionalPath { - if !p.valid { + if p.path == nil { return p } p.path = p.path.RelativeToTop() @@ -312,7 +330,7 @@ func (p OptionalPath) RelativeToTop() OptionalPath { // String returns the string version of the Path, or "" if it isn't valid. func (p OptionalPath) String() string { - if p.valid { + if p.path != nil { return p.path.String() } else { return "" @@ -1077,6 +1095,7 @@ func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPa path, err := pathForSource(ctx, pathComponents...) if err != nil { reportPathError(ctx, err) + // No need to put the error message into the returned path since it has been reported already. return OptionalPath{} } @@ -1091,7 +1110,7 @@ func ExistentPathForSource(ctx PathContext, pathComponents ...string) OptionalPa return OptionalPath{} } if !exists { - return OptionalPath{} + return InvalidOptionalPath(path.String() + " does not exist") } return OptionalPathForPath(path) } @@ -1127,6 +1146,7 @@ func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) Opt relDir = srcPath.path } else { ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path) + // No need to put the error message into the returned path since it has been reported already. return OptionalPath{} } dir := filepath.Join(p.srcDir, p.path, relDir) @@ -1140,7 +1160,7 @@ func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) Opt return OptionalPath{} } if len(paths) == 0 { - return OptionalPath{} + return InvalidOptionalPath(dir + " does not exist") } relPath := Rel(ctx, p.srcDir, paths[0]) return OptionalPathForPath(PathForSource(ctx, relPath)) diff --git a/android/paths_test.go b/android/paths_test.go index f4e4ce16e..3f4625dba 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -137,26 +137,35 @@ func TestValidatePath(t *testing.T) { func TestOptionalPath(t *testing.T) { var path OptionalPath - checkInvalidOptionalPath(t, path) + checkInvalidOptionalPath(t, path, "unknown") path = OptionalPathForPath(nil) - checkInvalidOptionalPath(t, path) + checkInvalidOptionalPath(t, path, "unknown") + + path = InvalidOptionalPath("foo") + checkInvalidOptionalPath(t, path, "foo") + + path = InvalidOptionalPath("") + checkInvalidOptionalPath(t, path, "unknown") path = OptionalPathForPath(PathForTesting("path")) checkValidOptionalPath(t, path, "path") } -func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { +func checkInvalidOptionalPath(t *testing.T, path OptionalPath, expectedInvalidReason string) { t.Helper() if path.Valid() { - t.Errorf("Uninitialized OptionalPath should not be valid") + t.Errorf("Invalid OptionalPath should not be valid") + } + if path.InvalidReason() != expectedInvalidReason { + t.Errorf("Wrong invalid reason: expected %q, got %q", expectedInvalidReason, path.InvalidReason()) } if path.String() != "" { - t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String()) + t.Errorf("Invalid OptionalPath String() should return \"\", not %q", path.String()) } paths := path.AsPaths() if len(paths) != 0 { - t.Errorf("Uninitialized OptionalPath AsPaths() should return empty Paths, not %q", paths) + t.Errorf("Invalid OptionalPath AsPaths() should return empty Paths, not %q", paths) } defer func() { if r := recover(); r == nil { @@ -171,6 +180,9 @@ func checkValidOptionalPath(t *testing.T, path OptionalPath, expectedString stri if !path.Valid() { t.Errorf("Initialized OptionalPath should not be invalid") } + if path.InvalidReason() != "" { + t.Errorf("Initialized OptionalPath should not have an invalid reason, got: %q", path.InvalidReason()) + } if path.String() != expectedString { t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String()) } diff --git a/android/testing.go b/android/testing.go index bd2faa291..b9d8fa878 100644 --- a/android/testing.go +++ b/android/testing.go @@ -491,6 +491,66 @@ func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonF ctx.preSingletons = append(ctx.preSingletons, newPreSingleton(name, factory)) } +// ModuleVariantForTests selects a specific variant of the module with the given +// name by matching the variations map against the variations of each module +// variant. A module variant matches the map if every variation that exists in +// both have the same value. Both the module and the map are allowed to have +// extra variations that the other doesn't have. Panics if not exactly one +// module variant matches. +func (ctx *TestContext) ModuleVariantForTests(name string, matchVariations map[string]string) TestingModule { + modules := []Module{} + ctx.VisitAllModules(func(m blueprint.Module) { + if ctx.ModuleName(m) == name { + am := m.(Module) + amMut := am.base().commonProperties.DebugMutators + amVar := am.base().commonProperties.DebugVariations + matched := true + for i, mut := range amMut { + if wantedVar, found := matchVariations[mut]; found && amVar[i] != wantedVar { + matched = false + break + } + } + if matched { + modules = append(modules, am) + } + } + }) + + if len(modules) == 0 { + // Show all the modules or module variants that do exist. + var allModuleNames []string + var allVariants []string + ctx.VisitAllModules(func(m blueprint.Module) { + allModuleNames = append(allModuleNames, ctx.ModuleName(m)) + if ctx.ModuleName(m) == name { + allVariants = append(allVariants, m.(Module).String()) + } + }) + + if len(allVariants) == 0 { + panic(fmt.Errorf("failed to find module %q. All modules:\n %s", + name, strings.Join(SortedUniqueStrings(allModuleNames), "\n "))) + } else { + sort.Strings(allVariants) + panic(fmt.Errorf("failed to find module %q matching %v. All variants:\n %s", + name, matchVariations, strings.Join(allVariants, "\n "))) + } + } + + if len(modules) > 1 { + moduleStrings := []string{} + for _, m := range modules { + moduleStrings = append(moduleStrings, m.String()) + } + sort.Strings(moduleStrings) + panic(fmt.Errorf("module %q has more than one variant that match %v:\n %s", + name, matchVariations, strings.Join(moduleStrings, "\n "))) + } + + return newTestingModule(ctx.config, modules[0]) +} + func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule { var module Module ctx.VisitAllModules(func(m blueprint.Module) { @@ -748,7 +808,7 @@ func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBui } func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) { - var searchedOutputs []string + searchedOutputs := WritablePaths(nil) for _, p := range b.provider.BuildParamsForTests() { outputs := append(WritablePaths(nil), p.Outputs...) outputs = append(outputs, p.ImplicitOutputs...) @@ -759,10 +819,17 @@ func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBu if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file { return b.newTestingBuildParams(p), nil } - searchedOutputs = append(searchedOutputs, f.Rel()) + searchedOutputs = append(searchedOutputs, f) } } - return TestingBuildParams{}, searchedOutputs + + formattedOutputs := []string{} + for _, f := range searchedOutputs { + formattedOutputs = append(formattedOutputs, + fmt.Sprintf("%s (rel=%s)", PathRelativeToTop(f), f.Rel())) + } + + return TestingBuildParams{}, formattedOutputs } func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams { diff --git a/apex/apex_test.go b/apex/apex_test.go index bcbf1adaf..2a2a1f45f 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -2187,6 +2187,7 @@ func TestJavaStableSdkVersion(t *testing.T) { name string expectedError string bp string + preparer android.FixturePreparer }{ { name: "Non-updatable apex with non-stable dep", @@ -2258,6 +2259,30 @@ func TestJavaStableSdkVersion(t *testing.T) { `, }, { + name: "Updatable apex with non-stable legacy core platform dep", + expectedError: `\Qcannot depend on "myjar-uses-legacy": non stable SDK core_platform_current - uses legacy core platform\E`, + bp: ` + apex { + name: "myapex", + java_libs: ["myjar-uses-legacy"], + key: "myapex.key", + updatable: true, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + java_library { + name: "myjar-uses-legacy", + srcs: ["foo/bar/MyClass.java"], + sdk_version: "core_platform", + apex_available: ["myapex"], + } + `, + preparer: java.FixtureUseLegacyCorePlatformApi("myjar-uses-legacy"), + }, + { name: "Updatable apex with non-stable transitive dep", // This is not actually detecting that the transitive dependency is unstable, rather it is // detecting that the transitive dependency is building against a wider API surface than the @@ -2293,12 +2318,22 @@ func TestJavaStableSdkVersion(t *testing.T) { } for _, test := range testCases { + if test.name != "Updatable apex with non-stable legacy core platform dep" { + continue + } t.Run(test.name, func(t *testing.T) { - if test.expectedError == "" { - testApex(t, test.bp) - } else { - testApexError(t, test.expectedError, test.bp) + errorHandler := android.FixtureExpectsNoErrors + if test.expectedError != "" { + errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) } + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + android.OptionalFixturePreparer(test.preparer), + ). + ExtendWithErrorHandler(errorHandler). + RunTestWithBp(t, test.bp) }) } } diff --git a/bp2build/Android.bp b/bp2build/Android.bp index 1d52a7040..40526a623 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -11,7 +11,6 @@ bootstrap_go_package { "build_conversion.go", "bzl_conversion.go", "configurability.go", - "compatibility.go", "constants.go", "conversion.go", "metrics.go", diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go index 48b294529..45a3cb6fe 100644 --- a/bp2build/bp2build.go +++ b/bp2build/bp2build.go @@ -43,7 +43,7 @@ func Codegen(ctx *CodegenContext) CodegenMetrics { writeFiles(ctx, bp2buildDir, bp2buildFiles) soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName) - writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.compatLayer)) + writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.metrics)) return res.metrics } diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index 2cbb5579e..b1a6e2c73 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -249,7 +249,6 @@ func propsToAttributes(props map[string]string) string { type conversionResults struct { buildFileToTargets map[string]BazelTargets metrics CodegenMetrics - compatLayer CodegenCompatLayer } func (r conversionResults) BuildDirToTargets() map[string]BazelTargets { @@ -262,11 +261,7 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers // Simple metrics tracking for bp2build metrics := CodegenMetrics{ - RuleClassCount: make(map[string]int), - } - - compatLayer := CodegenCompatLayer{ - NameToLabelMap: make(map[string]string), + ruleClassCount: make(map[string]int), } dirs := make(map[string]bool) @@ -282,14 +277,28 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers switch ctx.Mode() { case Bp2Build: + // There are two main ways of converting a Soong module to Bazel: + // 1) Manually handcrafting a Bazel target and associating the module with its label + // 2) Automatically generating with bp2build converters + // + // bp2build converters are used for the majority of modules. if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() { - metrics.handCraftedTargetCount += 1 - metrics.TotalModuleCount += 1 - compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel()) + // Handle modules converted to handcrafted targets. + // + // Since these modules are associated with some handcrafted + // target in a BUILD file, we simply append the entire contents + // of that BUILD file to the generated BUILD file. + // + // The append operation is only done once, even if there are + // multiple modules from the same directory associated to + // targets in the same BUILD file (or package). + + // Log the module. + metrics.AddConvertedModule(m.Name(), Handcrafted) + pathToBuildFile := getBazelPackagePath(b) - // We are using the entire contents of handcrafted build file, so if multiple targets within - // a package have handcrafted targets, we only want to include the contents one time. if _, exists := buildFileToAppend[pathToBuildFile]; exists { + // Append the BUILD file content once per package, at most. return } t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile) @@ -302,26 +311,29 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers // something more targeted based on the rule type and target buildFileToAppend[pathToBuildFile] = true } else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() { + // Handle modules converted to generated targets. + + // Log the module. + metrics.AddConvertedModule(m.Name(), Generated) + + // Handle modules with unconverted deps. By default, emit a warning. if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 { msg := fmt.Sprintf("%q depends on unconverted modules: %s", m.Name(), strings.Join(unconvertedDeps, ", ")) if ctx.unconvertedDepMode == warnUnconvertedDeps { metrics.moduleWithUnconvertedDepsMsgs = append(metrics.moduleWithUnconvertedDepsMsgs, msg) } else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps { - metrics.TotalModuleCount += 1 errs = append(errs, fmt.Errorf(msg)) return } } targets = generateBazelTargets(bpCtx, aModule) for _, t := range targets { - if t.name == m.Name() { - // only add targets that exist in Soong to compatibility layer - compatLayer.AddNameToLabelEntry(m.Name(), t.Label()) - } - metrics.RuleClassCount[t.ruleClass] += 1 + // A module can potentially generate more than 1 Bazel + // target, each of a different rule class. + metrics.IncrementRuleClassCount(t.ruleClass) } } else { - metrics.TotalModuleCount += 1 + metrics.IncrementUnconvertedCount() return } case QueryView: @@ -360,7 +372,6 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers return conversionResults{ buildFileToTargets: buildFileToTargets, metrics: metrics, - compatLayer: compatLayer, }, errs } diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index 371593b5e..b3a10531e 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -1022,18 +1022,18 @@ cc_library { }), implementation_deps = select({ "//build/bazel/platforms/arch:arm": [], - "//conditions:default": [":arm_static_lib_excludes"], + "//conditions:default": [":arm_static_lib_excludes_bp2build_cc_library_static"], }) + select({ "//build/bazel/product_variables:malloc_not_svelte": [], - "//conditions:default": [":malloc_not_svelte_static_lib_excludes"], + "//conditions:default": [":malloc_not_svelte_static_lib_excludes_bp2build_cc_library_static"], }), srcs_c = ["common.c"], whole_archive_deps = select({ "//build/bazel/platforms/arch:arm": [], - "//conditions:default": [":arm_whole_static_lib_excludes"], + "//conditions:default": [":arm_whole_static_lib_excludes_bp2build_cc_library_static"], }) + select({ - "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"], - "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"], + "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib_bp2build_cc_library_static"], + "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"], }), )`, }, diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go deleted file mode 100644 index 01dbdb9df..000000000 --- a/bp2build/compatibility.go +++ /dev/null @@ -1,27 +0,0 @@ -package bp2build - -import ( - "fmt" -) - -// Data from the code generation process that is used to improve compatibility -// between build systems. -type CodegenCompatLayer struct { - // A map from the original module name to the generated/handcrafted Bazel - // label for legacy build systems to be able to build a fully-qualified - // Bazel target from an unique module name. - NameToLabelMap map[string]string -} - -// Log an entry of module name -> Bazel target label. -func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) { - if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok { - panic(fmt.Errorf( - "Module '%s' maps to more than one Bazel target label: %s, %s. "+ - "This shouldn't happen. It probably indicates a bug with the bp2build internals.", - name, - existingLabel, - label)) - } - compatLayer.NameToLabelMap[name] = label -} diff --git a/bp2build/conversion.go b/bp2build/conversion.go index 75bc2b415..354abf6b7 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -16,29 +16,19 @@ type BazelFile struct { Contents string } -func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile { +func CreateSoongInjectionFiles(metrics CodegenMetrics) []BazelFile { var files []BazelFile files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package. files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars())) - files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap))) + files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n"))) return files } -func nameToLabelAliases(nameToLabelMap map[string]string) string { - ret := make([]string, len(nameToLabelMap)) - - for k, v := range nameToLabelMap { - // v is the fully qualified label rooted at '//' - ret = append(ret, fmt.Sprintf( - `alias( - name = "%s", - actual = "@%s", -)`, k, v)) - } - return strings.Join(ret, "\n\n") +func convertedModules(convertedModules []string) string { + return strings.Join(convertedModules, "\n") } func CreateBazelFiles( diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go index 56ea589bb..dfa1a9eb4 100644 --- a/bp2build/conversion_test.go +++ b/bp2build/conversion_test.go @@ -80,7 +80,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { } func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { - files := CreateSoongInjectionFiles(CodegenCompatLayer{}) + files := CreateSoongInjectionFiles(CodegenMetrics{}) expectedFilePaths := []bazelFilepath{ { @@ -92,8 +92,8 @@ func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { basename: "constants.bzl", }, { - dir: "module_name_to_label", - basename: GeneratedBuildFileName, + dir: "metrics", + basename: "converted_modules.txt", }, } @@ -107,9 +107,5 @@ func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename { t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename) } - - if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" { - t.Errorf("Contents of %s unexpected empty.", actualFile) - } } } diff --git a/bp2build/metrics.go b/bp2build/metrics.go index 645ef2d19..1cc4143c5 100644 --- a/bp2build/metrics.go +++ b/bp2build/metrics.go @@ -9,31 +9,69 @@ import ( // Simple metrics struct to collect information about a Blueprint to BUILD // conversion process. type CodegenMetrics struct { - // Total number of Soong/Blueprint modules - TotalModuleCount int + // Total number of Soong modules converted to generated targets + generatedModuleCount int - // Counts of generated Bazel targets per Bazel rule class - RuleClassCount map[string]int + // Total number of Soong modules converted to handcrafted targets + handCraftedModuleCount int + + // Total number of unconverted Soong modules + unconvertedModuleCount int - // Total number of handcrafted targets - handCraftedTargetCount int + // Counts of generated Bazel targets per Bazel rule class + ruleClassCount map[string]int moduleWithUnconvertedDepsMsgs []string + + convertedModules []string } // Print the codegen metrics to stdout. -func (metrics CodegenMetrics) Print() { +func (metrics *CodegenMetrics) Print() { generatedTargetCount := 0 - for _, ruleClass := range android.SortedStringKeys(metrics.RuleClassCount) { - count := metrics.RuleClassCount[ruleClass] + for _, ruleClass := range android.SortedStringKeys(metrics.ruleClassCount) { + count := metrics.ruleClassCount[ruleClass] fmt.Printf("[bp2build] %s: %d targets\n", ruleClass, count) generatedTargetCount += count } fmt.Printf( "[bp2build] Generated %d total BUILD targets and included %d handcrafted BUILD targets from %d Android.bp modules.\n With %d modules with unconverted deps \n\t%s", generatedTargetCount, - metrics.handCraftedTargetCount, - metrics.TotalModuleCount, + metrics.handCraftedModuleCount, + metrics.TotalModuleCount(), len(metrics.moduleWithUnconvertedDepsMsgs), strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t")) } + +func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) { + metrics.ruleClassCount[ruleClass] += 1 +} + +func (metrics *CodegenMetrics) IncrementUnconvertedCount() { + metrics.unconvertedModuleCount += 1 +} + +func (metrics *CodegenMetrics) TotalModuleCount() int { + return metrics.handCraftedModuleCount + + metrics.generatedModuleCount + + metrics.unconvertedModuleCount +} + +type ConversionType int + +const ( + Generated ConversionType = iota + Handcrafted +) + +func (metrics *CodegenMetrics) AddConvertedModule(moduleName string, conversionType ConversionType) { + // Undo prebuilt_ module name prefix modifications + moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName) + metrics.convertedModules = append(metrics.convertedModules, moduleName) + + if conversionType == Handcrafted { + metrics.handCraftedModuleCount += 1 + } else if conversionType == Generated { + metrics.generatedModuleCount += 1 + } +} diff --git a/cc/bp2build.go b/cc/bp2build.go index 67ea70e2c..e48f757dc 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "android/soong/bazel" + "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) @@ -136,10 +137,10 @@ func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module 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.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)) - attrs.System_dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.System_shared_libs)) + attrs.Static_deps.SetSelectValue(axis, config, bazelLabelForStaticDeps(ctx, props.Static_libs)) + attrs.Dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.Shared_libs)) + attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs)) + attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs)) } // system_dynamic_deps distinguishes between nil/empty list behavior: // nil -> use default values @@ -388,9 +389,9 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) // Excludes to parallel Soong: // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0 staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs) - staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)) + staticDeps.SetSelectValue(axis, config, bazelLabelForStaticDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)) wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs) - wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) + wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs)) systemSharedLibs := baseLinkerProps.System_shared_libs // systemSharedLibs distinguishes between nil/empty list behavior: @@ -399,15 +400,15 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) if len(systemSharedLibs) > 0 { systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs) } - systemSharedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, systemSharedLibs)) + systemSharedDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs)) sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs) - dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) + dynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs)) headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs) - headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs)) + headerDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, headerLibs)) exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers) - exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs)) + exportedDeps.SetSelectValue(axis, config, bazelLabelForHeaderDeps(ctx, exportedLibs)) linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps)) if baseLinkerProps.Version_script != nil { @@ -424,14 +425,14 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) // 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 + depResolutionFunc func(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList } productVarToDepFields := map[string]productVarDep{ // product variables do not support exclude_shared_libs - "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}, + "Shared_libs": productVarDep{attribute: &dynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes}, + "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps, bazelLabelForStaticDepsExcludes}, + "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, bazelLabelForWholeDepsExcludes}, } productVariableProps := android.ProductVariableProperties(ctx) @@ -559,3 +560,59 @@ func bp2BuildParseExportedIncludesHelper(ctx android.TopDownMutatorContext, modu return exported } + +func bazelLabelForStaticModule(ctx android.TopDownMutatorContext, m blueprint.Module) string { + label := android.BazelModuleLabel(ctx, m) + if aModule, ok := m.(android.Module); ok { + if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) { + label += "_bp2build_cc_library_static" + } + } + return label +} + +func bazelLabelForSharedModule(ctx android.TopDownMutatorContext, m blueprint.Module) string { + // cc_library, at it's root name, propagates the shared library, which depends on the static + // library. + return android.BazelModuleLabel(ctx, m) +} + +func bazelLabelForStaticWholeModuleDeps(ctx android.TopDownMutatorContext, m blueprint.Module) string { + label := bazelLabelForStaticModule(ctx, m) + if aModule, ok := m.(android.Module); ok { + if android.IsModulePrebuilt(aModule) { + label += "_alwayslink" + } + } + return label +} + +func bazelLabelForWholeDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList { + return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps) +} + +func bazelLabelForWholeDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList { + return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps) +} + +func bazelLabelForStaticDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList { + return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule) +} + +func bazelLabelForStaticDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList { + return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule) +} + +func bazelLabelForSharedDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList { + return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule) +} + +func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList { + // This is not elegant, but bp2build's shared library targets only propagate + // their header information as part of the normal C++ provider. + return bazelLabelForSharedDeps(ctx, modules) +} + +func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList { + return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule) +} diff --git a/cc/builder.go b/cc/builder.go index 4b0a4b65f..b494f7bab 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -454,15 +454,19 @@ func escapeSingleQuotes(s string) string { } // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files -func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths, +func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths, flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects { // Source files are one-to-one with tidy, coverage, or kythe files, if enabled. objFiles := make(android.Paths, len(srcFiles)) var tidyFiles android.Paths + noTidySrcsMap := make(map[android.Path]bool) var tidyVars string if flags.tidy { tidyFiles = make(android.Paths, 0, len(srcFiles)) + for _, path := range noTidySrcs { + noTidySrcsMap[path] = true + } tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT") if len(tidyTimeout) > 0 { tidyVars += "TIDY_TIMEOUT=" + tidyTimeout @@ -673,7 +677,8 @@ func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles and kytheFiles = append(kytheFiles, kytheFile) } - if tidy { + // Even with tidy, some src file could be skipped by noTidySrcsMap. + if tidy && !noTidySrcsMap[srcFile] { tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy") tidyFiles = append(tidyFiles, tidyFile) diff --git a/cc/compiler.go b/cc/compiler.go index 34d68a1e0..b535e7ff6 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -39,6 +39,9 @@ type BaseCompilerProperties struct { // or filegroup using the syntax ":module". Srcs []string `android:"path,arch_variant"` + // list of source files that should not be compiled with clang-tidy. + Tidy_disabled_srcs []string `android:"path,arch_variant"` + // list of source files that should not be used to build the C/C++ module. // This is most useful in the arch/multilib variants to remove non-common files Exclude_srcs []string `android:"path,arch_variant"` @@ -663,7 +666,9 @@ func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathD compiler.srcs = srcs // Compile files listed in c.Properties.Srcs into objects - objs := compileObjs(ctx, buildFlags, "", srcs, pathDeps, compiler.cFlagsDeps) + objs := compileObjs(ctx, buildFlags, "", srcs, + android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs), + pathDeps, compiler.cFlagsDeps) if ctx.Failed() { return Objects{} @@ -673,10 +678,10 @@ func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathD } // Compile a list of source files into objects a specified subdirectory -func compileObjs(ctx android.ModuleContext, flags builderFlags, - subdir string, srcFiles, pathDeps android.Paths, cFlagsDeps android.Paths) Objects { +func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string, + srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects { - return transformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps) + return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps) } // Properties for rust_bindgen related to generating rust bindings. diff --git a/cc/config/vndk.go b/cc/config/vndk.go index 8c678a129..d4fcf7cac 100644 --- a/cc/config/vndk.go +++ b/cc/config/vndk.go @@ -55,6 +55,8 @@ var VndkMustUseVendorVariantList = []string{ "android.hardware.power.stats-unstable-ndk_platform", "android.hardware.radio-V1-ndk", "android.hardware.radio-V1-ndk_platform", + "android.hardware.radio.config-V1-ndk", + "android.hardware.radio.config-V1-ndk_platform", "android.hardware.rebootescrow-ndk_platform", "android.hardware.security.keymint-V1-ndk", "android.hardware.security.keymint-V1-ndk_platform", diff --git a/cc/library.go b/cc/library.go index 703d57fef..8a572f994 100644 --- a/cc/library.go +++ b/cc/library.go @@ -143,6 +143,8 @@ type SharedProperties struct { type StaticOrSharedProperties struct { Srcs []string `android:"path,arch_variant"` + Tidy_disabled_srcs []string `android:"path,arch_variant"` + Sanitized Sanitized `android:"arch_variant"` Cflags []string `android:"arch_variant"` @@ -275,7 +277,7 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) { // For some cc_library modules, their static variants are ready to be // converted, but not their shared variants. For these modules, delegate to // the cc_library_static bp2build converter temporarily instead. - if android.GenerateCcLibraryStaticOnly(ctx) { + if android.GenerateCcLibraryStaticOnly(ctx.Module().Name()) { ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static") return } @@ -989,12 +991,14 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa if library.static() { srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs) - objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, - srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps)) + objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs, + android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs), + library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps)) } else if library.shared() { srcs := android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Srcs) - objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, - srcs, library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps)) + objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary, srcs, + android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs), + library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps)) } return objs diff --git a/cc/ndk_library.go b/cc/ndk_library.go index 704b03afb..7879a7d98 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -272,7 +272,7 @@ func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string, func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects { return compileObjs(ctx, flagsToBuilderFlags(flags), "", - android.Paths{src}, nil, nil) + android.Paths{src}, nil, nil, nil) } func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path { diff --git a/cc/test.go b/cc/test.go index 39347844a..047a69e3e 100644 --- a/cc/test.go +++ b/cc/test.go @@ -357,7 +357,8 @@ func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags { } func (test *testBinary) install(ctx ModuleContext, file android.Path) { - testInstallBase := "/data/local/tests/unrestricted" + // TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base. + testInstallBase := "/data/local/tmp" if ctx.inVendor() || ctx.useVndk() { testInstallBase = "/data/local/tests/vendor" } diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go index fa63b465d..3c9cac190 100644 --- a/cmd/multiproduct_kati/main.go +++ b/cmd/multiproduct_kati/main.go @@ -218,10 +218,16 @@ func distDir(outDir string) string { } } +func forceAnsiOutput() bool { + value := os.Getenv("SOONG_UI_ANSI_OUTPUT") + return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true" +} + func main() { stdio := terminal.StdioImpl{} - output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false) + output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false, + forceAnsiOutput()) log := logger.New(output) defer log.Cleanup() diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go index fe567a976..be8148749 100644 --- a/cmd/pom2bp/pom2bp.go +++ b/cmd/pom2bp/pom2bp.go @@ -24,6 +24,7 @@ import ( "io/ioutil" "os" "os/exec" + "path" "path/filepath" "regexp" "sort" @@ -164,7 +165,8 @@ func InList(s string, list []string) bool { type Dependency struct { XMLName xml.Name `xml:"dependency"` - BpTarget string `xml:"-"` + BpTarget string `xml:"-"` + BazelTarget string `xml:"-"` GroupId string `xml:"groupId"` ArtifactId string `xml:"artifactId"` @@ -230,6 +232,14 @@ func (p Pom) ModuleType() string { } } +func (p Pom) BazelTargetType() string { + if p.IsAar() { + return "android_library" + } else { + return "java_library" + } +} + func (p Pom) ImportModuleType() string { if p.IsAar() { return "android_library_import" @@ -240,6 +250,14 @@ func (p Pom) ImportModuleType() string { } } +func (p Pom) BazelImportTargetType() string { + if p.IsAar() { + return "aar_import" + } else { + return "java_import" + } +} + func (p Pom) ImportProperty() string { if p.IsAar() { return "aars" @@ -248,6 +266,14 @@ func (p Pom) ImportProperty() string { } } +func (p Pom) BazelImportProperty() string { + if p.IsAar() { + return "aar" + } else { + return "jars" + } +} + func (p Pom) BpName() string { if p.BpTarget == "" { p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId) @@ -263,6 +289,14 @@ func (p Pom) BpAarDeps() []string { return p.BpDeps("aar", []string{"compile", "runtime"}) } +func (p Pom) BazelJarDeps() []string { + return p.BazelDeps("jar", []string{"compile", "runtime"}) +} + +func (p Pom) BazelAarDeps() []string { + return p.BazelDeps("aar", []string{"compile", "runtime"}) +} + func (p Pom) BpExtraStaticLibs() []string { return extraStaticLibs[p.BpName()] } @@ -289,6 +323,91 @@ func (p Pom) BpDeps(typeExt string, scopes []string) []string { return ret } +// BazelDeps obtains dependencies filtered by type and scope. The results of this +// method are formatted as Bazel BUILD targets. +func (p Pom) BazelDeps(typeExt string, scopes []string) []string { + var ret []string + for _, d := range p.Dependencies { + if d.Type != typeExt || !InList(d.Scope, scopes) { + continue + } + ret = append(ret, d.BazelTarget) + } + return ret +} + +func PathModVars() (string, string, string) { + cmd := "/bin/bash" + androidTop := os.Getenv("ANDROID_BUILD_TOP") + envSetupSh := path.Join(androidTop, "build/envsetup.sh") + return cmd, androidTop, envSetupSh +} + +func InitRefreshMod(poms []*Pom) error { + cmd, _, envSetupSh := PathModVars() + // refreshmod is expensive, so if pathmod is already working we can skip it. + _, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+poms[0].BpName()).Output() + if exitErr, _ := err.(*exec.ExitError); exitErr != nil || err != nil { + _, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && refreshmod").Output() + if exitErr, _ := err.(*exec.ExitError); exitErr != nil { + return fmt.Errorf("failed to run %s\n%s\ntry running lunch.", cmd, string(exitErr.Stderr)) + } else if err != nil { + return err + } + } + return nil +} + +func BazelifyExtraDeps(extraDeps ExtraDeps, modules map[string]*Pom) error { + for _, deps := range extraDeps { + for _, dep := range deps { + bazelName, err := BpNameToBazelTarget(dep, modules) + if err != nil { + return err + } + dep = bazelName + } + + } + return nil +} + +func (p *Pom) GetBazelDepNames(modules map[string]*Pom) error { + for _, d := range p.Dependencies { + bazelName, err := BpNameToBazelTarget(d.BpName(), modules) + if err != nil { + return err + } + d.BazelTarget = bazelName + } + return nil +} + +func BpNameToBazelTarget(bpName string, modules map[string]*Pom) (string, error) { + cmd, androidTop, envSetupSh := PathModVars() + + if _, ok := modules[bpName]; ok { + // We've seen the POM for this dependency, it will be local to the output BUILD file + return ":" + bpName, nil + } else { + // we don't have the POM for this artifact, find and use the fully qualified target name. + output, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+bpName).Output() + if exitErr, _ := err.(*exec.ExitError); exitErr != nil { + return "", fmt.Errorf("failed to run %s %s\n%s", cmd, bpName, string(exitErr.Stderr)) + } else if err != nil { + return "", err + } + relPath := "" + for _, line := range strings.Fields(string(output)) { + if strings.Contains(line, androidTop) { + relPath = strings.TrimPrefix(line, androidTop) + relPath = strings.TrimLeft(relPath, "/") + } + } + return "//" + relPath + ":" + bpName, nil + } +} + func (p Pom) SdkVersion() string { return sdkVersion } @@ -512,6 +631,75 @@ var bpDepsTemplate = template.Must(template.New("bp").Parse(` } `)) +var bazelTemplate = template.Must(template.New("bp").Parse(` +{{.BazelImportTargetType}} ( + name = "{{.BpName}}", + {{.BazelImportProperty}}: {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, + visibility = ["//visibility:public"], + {{- if .IsAar}} + deps = [ + {{- range .BazelJarDeps}} + "{{.}}", + {{- end}} + {{- range .BazelAarDeps}} + "{{.}}", + {{- end}} + {{- range .BpExtraStaticLibs}} + "{{.}}", + {{- end}} + {{- range .BpExtraLibs}} + "{{.}}", + {{- end}} + {{- range .BpOptionalUsesLibs}} + "{{.}}", + {{- end}} + ], + {{- end}} +) +`)) + +var bazelDepsTemplate = template.Must(template.New("bp").Parse(` +{{.BazelImportTargetType}} ( + name = "{{.BpName}}", + {{.BazelImportProperty}} = {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, + visibility = ["//visibility:public"], + deps = [ + {{- range .BazelJarDeps}} + "{{.}}", + {{- end}} + {{- range .BazelAarDeps}} + "{{.}}", + {{- end}} + {{- range .BpExtraStaticLibs}} + "{{.}}", + {{- end}} + {{- range .BpExtraLibs}} + "{{.}}", + {{- end}} + {{- range .BpOptionalUsesLibs}} + "{{.}}", + {{- end}} + ], + exports = [ + {{- range .BazelJarDeps}} + "{{.}}", + {{- end}} + {{- range .BazelAarDeps}} + "{{.}}", + {{- end}} + {{- range .BpExtraStaticLibs}} + "{{.}}", + {{- end}} + {{- range .BpExtraLibs}} + "{{.}}", + {{- end}} + {{- range .BpOptionalUsesLibs}} + "{{.}}", + {{- end}} + ], +) +`)) + func parse(filename string) (*Pom, error) { data, err := ioutil.ReadFile(filename) if err != nil { @@ -559,12 +747,14 @@ func rerunForRegen(filename string) error { // Extract the old args from the file line := scanner.Text() - if strings.HasPrefix(line, "// pom2bp ") { + if strings.HasPrefix(line, "// pom2bp ") { // .bp file line = strings.TrimPrefix(line, "// pom2bp ") - } else if strings.HasPrefix(line, "// pom2mk ") { + } else if strings.HasPrefix(line, "// pom2mk ") { // .bp file converted from .mk file line = strings.TrimPrefix(line, "// pom2mk ") - } else if strings.HasPrefix(line, "# pom2mk ") { + } else if strings.HasPrefix(line, "# pom2mk ") { // .mk file line = strings.TrimPrefix(line, "# pom2mk ") + } else if strings.HasPrefix(line, "# pom2bp ") { // Bazel BUILD file + line = strings.TrimPrefix(line, "# pom2bp ") } else { return fmt.Errorf("unexpected second line: %q", line) } @@ -650,6 +840,7 @@ Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-lib } var regen string + var pom2build bool flag.Var(&excludes, "exclude", "Exclude module") flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module") @@ -664,6 +855,7 @@ Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-lib flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies") flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules") flag.StringVar(®en, "regen", "", "Rewrite specified file") + flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file") flag.Parse() if regen != "" { @@ -758,6 +950,16 @@ Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-lib os.Exit(1) } + if pom2build { + if err := InitRefreshMod(poms); err != nil { + fmt.Fprintf(os.Stderr, "Error in refreshmod: %s", err) + os.Exit(1) + } + BazelifyExtraDeps(extraStaticLibs, modules) + BazelifyExtraDeps(extraLibs, modules) + BazelifyExtraDeps(optionalUsesLibs, modules) + } + for _, pom := range poms { if pom.IsAar() { err := pom.ExtractMinSdkVersion() @@ -767,19 +969,32 @@ Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-lib } } pom.FixDeps(modules) + if pom2build { + pom.GetBazelDepNames(modules) + } } buf := &bytes.Buffer{} + commentString := "//" + if pom2build { + commentString = "#" + } + fmt.Fprintln(buf, commentString, "Automatically generated with:") + fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " ")) - fmt.Fprintln(buf, "// Automatically generated with:") - fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " ")) + depsTemplate := bpDepsTemplate + template := bpTemplate + if pom2build { + depsTemplate = bazelDepsTemplate + template = bazelTemplate + } for _, pom := range poms { var err error if staticDeps { - err = bpDepsTemplate.Execute(buf, pom) + err = depsTemplate.Execute(buf, pom) } else { - err = bpTemplate.Execute(buf, pom) + err = template.Execute(buf, pom) } if err != nil { fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err) @@ -787,11 +1002,15 @@ Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-lib } } - out, err := bpfix.Reformat(buf.String()) - if err != nil { - fmt.Fprintln(os.Stderr, "Error formatting output", err) - os.Exit(1) + if pom2build { + os.Stdout.WriteString(buf.String()) + } else { + out, err := bpfix.Reformat(buf.String()) + if err != nil { + fmt.Fprintln(os.Stderr, "Error formatting output", err) + os.Exit(1) + } + os.Stdout.WriteString(out) } - os.Stdout.WriteString(out) } diff --git a/cmd/run_with_timeout/run_with_timeout_test.go b/cmd/run_with_timeout/run_with_timeout_test.go index ed6ec11e7..6729e6165 100644 --- a/cmd/run_with_timeout/run_with_timeout_test.go +++ b/cmd/run_with_timeout/run_with_timeout_test.go @@ -50,7 +50,7 @@ func Test_runWithTimeout(t *testing.T) { args: args{ command: "echo", args: []string{"foo"}, - timeout: 1 * time.Second, + timeout: 10 * time.Second, }, wantStdout: "foo\n", }, @@ -58,7 +58,7 @@ func Test_runWithTimeout(t *testing.T) { name: "timed out", args: args{ command: "sh", - args: []string{"-c", "sleep 1 && echo foo"}, + args: []string{"-c", "sleep 10 && echo foo"}, timeout: 1 * time.Millisecond, }, wantStderr: ".*: process timed out after .*\n", @@ -68,7 +68,7 @@ func Test_runWithTimeout(t *testing.T) { name: "on_timeout command", args: args{ command: "sh", - args: []string{"-c", "sleep 1 && echo foo"}, + args: []string{"-c", "sleep 10 && echo foo"}, timeout: 1 * time.Millisecond, onTimeoutCmd: "echo bar", }, diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go index 8d8f37f4b..d2fbed411 100644 --- a/cmd/soong_build/writedocs.go +++ b/cmd/soong_build/writedocs.go @@ -372,6 +372,7 @@ li a:hover:not(.active) { {{if .Properties -}} <div class="accordion" id="{{getModule}}.{{.Name}}"> <span class="fixed">⊕</span><b>{{.Name}}</b> + <i>{{.Type}}</i> {{- range .OtherNames -}}, {{.}}{{- end -}} </div> <div class="collapsible"> diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index d70978727..9ee373e79 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -164,7 +164,8 @@ func main() { // Create a terminal output that mimics Ninja's. output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput, - build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")) + build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"), + build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT")) // Attach a new logger instance to the terminal output. log := logger.New(output) diff --git a/cuj/cuj.go b/cuj/cuj.go index 413f4237f..869e0f7b1 100644 --- a/cuj/cuj.go +++ b/cuj/cuj.go @@ -48,7 +48,7 @@ type TestResults struct { // Run runs a single build command. It emulates the "m" command line by calling into Soong UI directly. func (t *Test) Run(logsDir string) { - output := terminal.NewStatusOutput(os.Stdout, "", false, false) + output := terminal.NewStatusOutput(os.Stdout, "", false, false, false) log := logger.New(output) defer log.Cleanup() @@ -138,6 +138,8 @@ func main() { cujDir := filepath.Join(outDir, "cuj_tests") + wd, _ := os.Getwd() + os.Setenv("TOP", wd) // Use a subdirectory for the out directory for the tests to keep them isolated. os.Setenv("OUT_DIR", filepath.Join(cujDir, "out")) diff --git a/java/base.go b/java/base.go index 86022c3b2..78aaa19cf 100644 --- a/java/base.go +++ b/java/base.go @@ -382,7 +382,7 @@ func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { return nil } if sdkVersion.Kind == android.SdkCorePlatform { - if useLegacyCorePlatformApiByName(j.BaseModuleName()) { + if useLegacyCorePlatformApi(ctx, j.BaseModuleName()) { return fmt.Errorf("non stable SDK %v - uses legacy core platform", sdkVersion) } else { // Treat stable core platform as stable. @@ -643,6 +643,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } else if j.shouldInstrumentStatic(ctx) { ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent") } + + if j.useCompose() { + ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag, + "androidx.compose.compiler_compiler-hosted") + } } func hasSrcExt(srcs []string, ext string) bool { @@ -911,6 +916,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { if ctx.Device() { kotlincFlags = append(kotlincFlags, "-no-jdk") } + + for _, plugin := range deps.kotlinPlugins { + kotlincFlags = append(kotlincFlags, "-Xplugin="+plugin.String()) + } + flags.kotlincDeps = append(flags.kotlincDeps, deps.kotlinPlugins...) + if len(kotlincFlags) > 0 { // optimization. ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " ")) @@ -1325,6 +1336,10 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.outputFile = outputFile.WithoutRel() } +func (j *Module) useCompose() bool { + return android.InList("androidx.compose.runtime_runtime", j.properties.Static_libs) +} + // 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 { @@ -1755,6 +1770,8 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.kotlinStdlib = append(deps.kotlinStdlib, dep.HeaderJars...) case kotlinAnnotationsTag: deps.kotlinAnnotations = dep.HeaderJars + case kotlinPluginTag: + deps.kotlinPlugins = append(deps.kotlinPlugins, dep.ImplementationAndResourcesJars...) case syspropPublicStubDepTag: // This is a sysprop implementation library, forward the JavaInfoProvider from // the corresponding sysprop public stub library as SyspropPublicStubInfoProvider. diff --git a/java/builder.go b/java/builder.go index ea011b8e1..ae124a3e7 100644 --- a/java/builder.go +++ b/java/builder.go @@ -263,6 +263,7 @@ type javaBuilderFlags struct { kotlincFlags string kotlincClasspath classpath + kotlincDeps android.Paths proto android.ProtoFlags } diff --git a/java/dex.go b/java/dex.go index 667800f8a..8045b5cc6 100644 --- a/java/dex.go +++ b/java/dex.go @@ -32,6 +32,9 @@ type DexProperties struct { // list of module-specific flags that will be used for dex compiles Dxflags []string `android:"arch_variant"` + // A list of files containing rules that specify the classes to keep in the main dex file. + Main_dex_rules []string `android:"path"` + Optimize struct { // If false, disable all optimization. Defaults to true for android_app and android_test // modules, false for java_library and java_test modules. @@ -164,13 +167,20 @@ var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8", }, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir", "r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"}) -func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string { - flags := d.dexProperties.Dxflags +func (d *dexer) dexCommonFlags(ctx android.ModuleContext, + minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) { + + flags = d.dexProperties.Dxflags // Translate all the DX flags to D8 ones until all the build files have been migrated // to D8 flags. See: b/69377755 flags = android.RemoveListFromList(flags, []string{"--core-library", "--dex", "--multi-dex"}) + for _, f := range android.PathsForModuleSrc(ctx, d.dexProperties.Main_dex_rules) { + flags = append(flags, "--main-dex-rules", f.String()) + deps = append(deps, f) + } + if ctx.Config().Getenv("NO_OPTIMIZE_DX") != "" { flags = append(flags, "--debug") } @@ -187,7 +197,7 @@ func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android. } flags = append(flags, "--min-api "+strconv.Itoa(effectiveVersion.FinalOrFutureInt())) - return flags + return flags, deps } func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) { @@ -286,7 +296,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi zipFlags += " -L 0" } - commonFlags := d.dexCommonFlags(ctx, minSdkVersion) + commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion) useR8 := d.effectiveOptimizeEnabled() if useR8 { @@ -298,6 +308,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi proguardUsageZip := android.PathForModuleOut(ctx, "proguard_usage.zip") d.proguardUsageZip = android.OptionalPathForPath(proguardUsageZip) r8Flags, r8Deps := d.r8Flags(ctx, flags) + r8Deps = append(r8Deps, commonDeps...) rule := r8 args := map[string]string{ "r8Flags": strings.Join(append(commonFlags, r8Flags...), " "), @@ -324,6 +335,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi }) } else { d8Flags, d8Deps := d8Flags(flags) + d8Deps = append(d8Deps, commonDeps...) rule := d8 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_D8") { rule = d8RE diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 51cd50169..1c6fbac72 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -1194,13 +1194,6 @@ func retrieveClassesJarsFromModule(module android.Module) android.Paths { // 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 - } - // Any missing dependency should be allowed. if ctx.Config().AllowMissingDependencies() { return true diff --git a/java/java.go b/java/java.go index c649a1508..4a4486658 100644 --- a/java/java.go +++ b/java/java.go @@ -286,6 +286,7 @@ var ( frameworkResTag = dependencyTag{name: "framework-res"} kotlinStdlibTag = dependencyTag{name: "kotlin-stdlib"} kotlinAnnotationsTag = dependencyTag{name: "kotlin-annotations"} + kotlinPluginTag = dependencyTag{name: "kotlin-plugin"} proguardRaiseTag = dependencyTag{name: "proguard-raise"} certificateTag = dependencyTag{name: "certificate"} instrumentationForTag = dependencyTag{name: "instrumentation_for"} @@ -380,6 +381,7 @@ type deps struct { aidlPreprocess android.OptionalPath kotlinStdlib android.Paths kotlinAnnotations android.Paths + kotlinPlugins android.Paths disableTurbine bool } diff --git a/java/kotlin.go b/java/kotlin.go index 3a6fc0f48..e4f1bc198 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -81,6 +81,7 @@ func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, var deps android.Paths deps = append(deps, flags.kotlincClasspath...) + deps = append(deps, flags.kotlincDeps...) deps = append(deps, srcJars...) deps = append(deps, commonSrcFiles...) diff --git a/java/kotlin_test.go b/java/kotlin_test.go index db3069693..cac0af3b9 100644 --- a/java/kotlin_test.go +++ b/java/kotlin_test.go @@ -281,3 +281,46 @@ func TestKaptEncodeFlags(t *testing.T) { }) } } + +func TestKotlinCompose(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + ).RunTestWithBp(t, ` + java_library { + name: "androidx.compose.runtime_runtime", + } + + java_library_host { + name: "androidx.compose.compiler_compiler-hosted", + } + + java_library { + name: "withcompose", + srcs: ["a.kt"], + static_libs: ["androidx.compose.runtime_runtime"], + } + + java_library { + name: "nocompose", + srcs: ["a.kt"], + } + `) + + buildOS := result.Config.BuildOS.String() + + composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output + withCompose := result.ModuleForTests("withcompose", "android_common") + noCompose := result.ModuleForTests("nocompose", "android_common") + + android.AssertStringListContains(t, "missing compose compiler dependency", + withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) + + android.AssertStringDoesContain(t, "missing compose compiler plugin", + withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) + + android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency", + noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String()) + + android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin", + noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String()) +} diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go index 8c401a7d7..77493105e 100644 --- a/java/legacy_core_platform_api_usage.go +++ b/java/legacy_core_platform_api_usage.go @@ -163,17 +163,27 @@ func init() { } } -func useLegacyCorePlatformApi(ctx android.EarlyModuleContext) bool { - return useLegacyCorePlatformApiByName(ctx.ModuleName()) +var legacyCorePlatformApiLookupKey = android.NewOnceKey("legacyCorePlatformApiLookup") + +func getLegacyCorePlatformApiLookup(config android.Config) map[string]struct{} { + return config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return legacyCorePlatformApiLookup + }).(map[string]struct{}) } -func useLegacyCorePlatformApiByName(name string) bool { - _, found := legacyCorePlatformApiLookup[name] +// useLegacyCorePlatformApi checks to see whether the supplied module name is in the list of modules +// that are able to use the legacy core platform API and returns true if it does, false otherwise. +// +// This method takes the module name separately from the context as this may be being called for a +// module that is not the target of the supplied context. +func useLegacyCorePlatformApi(ctx android.EarlyModuleContext, moduleName string) bool { + lookup := getLegacyCorePlatformApiLookup(ctx.Config()) + _, found := lookup[moduleName] return found } func corePlatformSystemModules(ctx android.EarlyModuleContext) string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformSystemModules } else { return config.StableCorePlatformSystemModules @@ -181,7 +191,7 @@ func corePlatformSystemModules(ctx android.EarlyModuleContext) string { } func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string { - if useLegacyCorePlatformApi(ctx) { + if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) { return config.LegacyCorePlatformBootclasspathLibraries } else { return config.StableCorePlatformBootclasspathLibraries diff --git a/java/sdk_library.go b/java/sdk_library.go index 1d3907178..2d8aef74c 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -376,6 +376,9 @@ type ApiScopeProperties struct { } type sdkLibraryProperties struct { + // List of source files that are needed to compile the API, but are not part of runtime library. + Api_srcs []string `android:"arch_variant"` + // Visibility for impl library module. If not specified then defaults to the // visibility property. Impl_library_visibility []string @@ -1438,6 +1441,7 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC props.Name = proptools.StringPtr(name) props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_source_visibility) props.Srcs = append(props.Srcs, module.properties.Srcs...) + props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...) props.Sdk_version = module.deviceProperties.Sdk_version props.System_modules = module.deviceProperties.System_modules props.Installable = proptools.BoolPtr(false) diff --git a/java/testing.go b/java/testing.go index d8a77cf37..a642753c4 100644 --- a/java/testing.go +++ b/java/testing.go @@ -229,6 +229,26 @@ func FixtureConfigureApexBootJars(bootJars ...string) android.FixturePreparer { ) } +// FixtureUseLegacyCorePlatformApi prepares the fixture by setting the exception list of those +// modules that are allowed to use the legacy core platform API to be the ones supplied. +func FixtureUseLegacyCorePlatformApi(moduleNames ...string) android.FixturePreparer { + lookup := make(map[string]struct{}) + for _, moduleName := range moduleNames { + lookup[moduleName] = struct{}{} + } + return android.FixtureModifyConfig(func(config android.Config) { + // Try and set the legacyCorePlatformApiLookup in the config, the returned value will be the + // actual value that is set. + cached := config.Once(legacyCorePlatformApiLookupKey, func() interface{} { + return lookup + }) + // Make sure that the cached value is the one we need. + if !reflect.DeepEqual(cached, lookup) { + panic(fmt.Errorf("attempting to set legacyCorePlatformApiLookupKey to %q but it has already been set to %q", lookup, cached)) + } + }) +} + // registerRequiredBuildComponentsForTest registers the build components used by // PrepareForTestWithJavaDefaultModules. // diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp index 4fa3eb678..b18bfc72c 100644 --- a/mk2rbc/Android.bp +++ b/mk2rbc/Android.bp @@ -38,6 +38,7 @@ bootstrap_go_package { "soong_variables.go", "types.go", "variable.go", + "version_defaults.go", ], deps: ["androidmk-parser"], } diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go index 209e82bbd..7b5f298e2 100644 --- a/mk2rbc/cmd/mk2rbc.go +++ b/mk2rbc/cmd/mk2rbc.go @@ -81,6 +81,7 @@ var backupSuffix string var tracedVariables []string var errorLogger = errorsByType{data: make(map[string]datum)} var makefileFinder = &LinuxMakefileFinder{} +var versionDefaultsMk = filepath.Join("build", "make", "core", "version_defaults.mk") func main() { flag.Usage = func() { @@ -165,13 +166,24 @@ func main() { quit(fmt.Errorf("cannot generate configuration launcher for %s, it is not a known product", product)) } + versionDefaults, err := generateVersionDefaults() + if err != nil { + quit(err) + } ok = convertOne(path) && ok - err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(path), mk2rbc.MakePath2ModuleName(path))) + versionDefaultsPath := outputFilePath(versionDefaultsMk) + err = writeGenerated(versionDefaultsPath, versionDefaults) if err != nil { fmt.Fprintf(os.Stderr, "%s:%s", path, err) ok = false } + err = writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(path), versionDefaultsPath, + mk2rbc.MakePath2ModuleName(path))) + if err != nil { + fmt.Fprintf(os.Stderr, "%s:%s", path, err) + ok = false + } } else { files := flag.Args() if *allInSource { @@ -194,6 +206,15 @@ func main() { } } +func generateVersionDefaults() (string, error) { + versionSettings, err := mk2rbc.ParseVersionDefaults(filepath.Join(*rootDir, versionDefaultsMk)) + if err != nil { + return "", err + } + return mk2rbc.VersionDefaults(versionSettings), nil + +} + func quit(s interface{}) { fmt.Fprintln(os.Stderr, s) os.Exit(2) diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go index b05d3409b..b9b7e2ccd 100644 --- a/mk2rbc/mk2rbc.go +++ b/mk2rbc/mk2rbc.go @@ -1618,12 +1618,12 @@ func Convert(req Request) (*StarlarkScript, error) { return starScript, nil } -func Launcher(path, name string) string { +func Launcher(mainModuleUri, versionDefaultsUri, mainModuleName string) string { var buf bytes.Buffer fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName) - fmt.Fprintf(&buf, "load(%q, \"init\")\n", path) - fmt.Fprintf(&buf, "g, config = %s(%q, init)\n", cfnMain, name) - fmt.Fprintf(&buf, "%s(g, config)\n", cfnPrintVars) + fmt.Fprintf(&buf, "load(%q, \"version_defaults\")\n", versionDefaultsUri) + fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri) + fmt.Fprintf(&buf, "%s(%s(%q, init, version_defaults))\n", cfnPrintVars, cfnMain, mainModuleName) return buf.String() } diff --git a/mk2rbc/test/version_defaults.mk.test b/mk2rbc/test/version_defaults.mk.test new file mode 100644 index 000000000..166639278 --- /dev/null +++ b/mk2rbc/test/version_defaults.mk.test @@ -0,0 +1,22 @@ +INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk) +ifdef INTERNAL_BUILD_ID_MAKEFILE + include $(INTERNAL_BUILD_ID_MAKEFILE) +endif + +DEFAULT_PLATFORM_VERSION := TP1A +.KATI_READONLY := DEFAULT_PLATFORM_VERSION +MIN_PLATFORM_VERSION := TP1A +MAX_PLATFORM_VERSION := TP1A +PLATFORM_VERSION_LAST_STABLE := 12 +PLATFORM_VERSION_CODENAME.SP2A := Sv2 +PLATFORM_VERSION_CODENAME.TP1A := Tiramisu +ifndef PLATFORM_SDK_VERSION + PLATFORM_SDK_VERSION := 31 +endif +.KATI_READONLY := PLATFORM_SDK_VERSION +PLATFORM_SDK_EXTENSION_VERSION := 1 +PLATFORM_BASE_SDK_EXTENSION_VERSION := 0 +ifndef PLATFORM_SECURITY_PATCH + PLATFORM_SECURITY_PATCH := 2021-10-05 +endif +include $(BUILD_SYSTEM)/version_util.mk diff --git a/mk2rbc/version_defaults.go b/mk2rbc/version_defaults.go new file mode 100644 index 000000000..27e8198a8 --- /dev/null +++ b/mk2rbc/version_defaults.go @@ -0,0 +1,109 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mk2rbc + +import ( + mkparser "android/soong/androidmk/parser" + "bytes" + "fmt" + "io/ioutil" + "os" + "sort" + "strconv" + "strings" +) + +const codenamePrefix = "PLATFORM_VERSION_CODENAME." + +// ParseVersionDefaults extracts version settings from the given file +// and returns the map. +func ParseVersionDefaults(path string) (map[string]string, error) { + contents, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + parser := mkparser.NewParser(path, bytes.NewBuffer(contents)) + nodes, errs := parser.Parse() + if len(errs) > 0 { + for _, e := range errs { + fmt.Fprintln(os.Stderr, "ERROR:", e) + } + return nil, fmt.Errorf("cannot parse %s", path) + } + + result := map[string]string{ + "DEFAULT_PLATFORM_VERSION": "", + "MAX_PLATFORM_VERSION": "", + "MIN_PLATFORM_VERSION": "A", + "PLATFORM_BASE_SDK_EXTENSION_VERSION": "", + "PLATFORM_SDK_EXTENSION_VERSION": "", + "PLATFORM_SDK_VERSION": "", + "PLATFORM_SECURITY_PATCH": "", + "PLATFORM_VERSION_LAST_STABLE": "", + } + for _, node := range nodes { + asgn, ok := node.(*mkparser.Assignment) + if !(ok && asgn.Name.Const()) { + continue + } + s := asgn.Name.Strings[0] + _, ok = result[s] + if !ok { + ok = strings.HasPrefix(s, codenamePrefix) + } + if !ok { + continue + } + v := asgn.Value + if !v.Const() { + return nil, fmt.Errorf("the value of %s should be constant", s) + } + result[s] = strings.TrimSpace(v.Strings[0]) + } + return result, nil +} + +func genericValue(s string) interface{} { + if ival, err := strconv.ParseInt(s, 0, 0); err == nil { + return ival + } + return s +} + +// VersionDefaults generates the contents of the version_defaults.rbc file +func VersionDefaults(values map[string]string) string { + var sink bytes.Buffer + var lines []string + var codenames []string + for name, value := range values { + if strings.HasPrefix(name, codenamePrefix) { + codenames = append(codenames, + fmt.Sprintf("%q: %q", strings.TrimPrefix(name, codenamePrefix), value)) + } else { + // Print numbers as such + lines = append(lines, fmt.Sprintf(" %s = %#v,\n", + strings.ToLower(name), genericValue(value))) + } + } + sort.Strings(lines) + sink.WriteString("version_defaults = struct(\n") + for _, l := range lines { + sink.WriteString(l) + } + sink.WriteString(" codenames = { ") + sink.WriteString(strings.Join(codenames, ", ")) + sink.WriteString(" }\n)\n") + return sink.String() +} diff --git a/mk2rbc/version_defaults_test.go b/mk2rbc/version_defaults_test.go new file mode 100644 index 000000000..c78fa3238 --- /dev/null +++ b/mk2rbc/version_defaults_test.go @@ -0,0 +1,60 @@ +package mk2rbc + +import ( + "path/filepath" + "reflect" + "strings" + "testing" +) + +func TestParseVersionDefaults(t *testing.T) { + testDir := getTestDirectory() + abspath := func(relPath string) string { return filepath.Join(testDir, relPath) } + actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test")) + if err != nil { + t.Fatal(err) + } + expectedProducts := map[string]string{ + "DEFAULT_PLATFORM_VERSION": "TP1A", + "MAX_PLATFORM_VERSION": "TP1A", + "MIN_PLATFORM_VERSION": "TP1A", + "PLATFORM_BASE_SDK_EXTENSION_VERSION": "0", + "PLATFORM_SDK_EXTENSION_VERSION": "1", + "PLATFORM_SDK_VERSION": "31", + "PLATFORM_SECURITY_PATCH": "2021-10-05", + "PLATFORM_VERSION_LAST_STABLE": "12", + "PLATFORM_VERSION_CODENAME.SP2A": "Sv2", + "PLATFORM_VERSION_CODENAME.TP1A": "Tiramisu", + } + if !reflect.DeepEqual(actualProducts, expectedProducts) { + t.Errorf("\nExpected: %v\n Actual: %v", expectedProducts, actualProducts) + } +} + +func TestVersionDefaults(t *testing.T) { + testDir := getTestDirectory() + abspath := func(relPath string) string { return filepath.Join(testDir, relPath) } + actualProducts, err := ParseVersionDefaults(abspath("version_defaults.mk.test")) + if err != nil { + t.Fatal(err) + } + expectedString := `version_defaults = struct( + default_platform_version = "TP1A", + max_platform_version = "TP1A", + min_platform_version = "TP1A", + platform_base_sdk_extension_version = 0, + platform_sdk_extension_version = 1, + platform_sdk_version = 31, + platform_security_patch = "2021-10-05", + platform_version_last_stable = 12, + codenames = { "SP2A": "Sv2", "TP1A": "Tiramisu" } +) +` + actualString := VersionDefaults(actualProducts) + if !reflect.DeepEqual(actualString, expectedString) { + t.Errorf("\nExpected: %v\nActual:\n%v", + strings.ReplaceAll(expectedString, "\n", "\n"), + strings.ReplaceAll(actualString, "\n", "\n")) + } + +} diff --git a/python/python.go b/python/python.go index a35a1ac9c..f90017268 100644 --- a/python/python.go +++ b/python/python.go @@ -45,7 +45,7 @@ func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) { type VersionProperties struct { // whether the module is required to be built with this version. // Defaults to true for Python 3, and false otherwise. - Enabled *bool `android:"arch_variant"` + Enabled *bool // list of source files specific to this Python version. // Using the syntax ":module", srcs may reference the outputs of other modules that produce source files, @@ -60,7 +60,7 @@ type VersionProperties struct { Libs []string `android:"arch_variant"` // whether the binary is required to be built with embedded launcher for this version, defaults to false. - Embedded_launcher *bool `android:"arch_variant"` // TODO(b/174041232): Remove this property + Embedded_launcher *bool // TODO(b/174041232): Remove this property } // properties that apply to all python modules @@ -70,10 +70,10 @@ type BaseProperties struct { // eg. Pkg_path = "a/b/c"; Other packages can reference this module by using // (from a.b.c import ...) statement. // if left unspecified, all the source/data files path is unchanged within zip file. - Pkg_path *string `android:"arch_variant"` + Pkg_path *string // true, if the Python module is used internally, eg, Python std libs. - Is_internal *bool `android:"arch_variant"` + Is_internal *bool // list of source (.py) files compatible both with Python2 and Python3 used to compile the // Python module. diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py index 71fe358ff..b4936b865 100755 --- a/scripts/manifest_check.py +++ b/scripts/manifest_check.py @@ -358,7 +358,7 @@ def main(): # the check has failed. if args.enforce_uses_libraries_status: with open(args.enforce_uses_libraries_status, 'w') as f: - if not errmsg is not None: + if errmsg is not None: f.write('%s\n' % errmsg) if args.extract_target_sdk_version: diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go index 4e8c56804..936b275a1 100644 --- a/ui/terminal/simple_status.go +++ b/ui/terminal/simple_status.go @@ -24,15 +24,17 @@ import ( type simpleStatusOutput struct { writer io.Writer formatter formatter + keepANSI bool } // NewSimpleStatusOutput returns a StatusOutput that represents the // current build status similarly to Ninja's built-in terminal // output. -func NewSimpleStatusOutput(w io.Writer, formatter formatter) status.StatusOutput { +func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput { return &simpleStatusOutput{ writer: w, formatter: formatter, + keepANSI: keepANSI, } } @@ -54,7 +56,9 @@ func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts sta progress := s.formatter.progress(counts) + str output := s.formatter.result(result) - output = string(stripAnsiEscapes([]byte(output))) + if !s.keepANSI { + output = string(stripAnsiEscapes([]byte(output))) + } if output != "" { fmt.Fprint(s.writer, progress, "\n", output) diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go index 6bdf14074..06a4064ff 100644 --- a/ui/terminal/smart_status.go +++ b/ui/terminal/smart_status.go @@ -77,7 +77,12 @@ func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput s.requestedTableHeight = h } - s.updateTermSize() + if w, h, ok := termSize(s.writer); ok { + s.termWidth, s.termHeight = w, h + s.computeTableHeight() + } else { + s.tableMode = false + } if s.tableMode { // Add empty lines at the bottom of the screen to scroll back the existing history @@ -296,40 +301,44 @@ func (s *smartStatusOutput) stopSigwinch() { close(s.sigwinch) } +// computeTableHeight recomputes s.tableHeight based on s.termHeight and s.requestedTableHeight. +func (s *smartStatusOutput) computeTableHeight() { + tableHeight := s.requestedTableHeight + if tableHeight == 0 { + tableHeight = s.termHeight / 4 + if tableHeight < 1 { + tableHeight = 1 + } else if tableHeight > 10 { + tableHeight = 10 + } + } + if tableHeight > s.termHeight-1 { + tableHeight = s.termHeight - 1 + } + s.tableHeight = tableHeight +} + +// updateTermSize recomputes the table height after a SIGWINCH and pans any existing text if +// necessary. func (s *smartStatusOutput) updateTermSize() { if w, h, ok := termSize(s.writer); ok { - firstUpdate := s.termHeight == 0 && s.termWidth == 0 oldScrollingHeight := s.termHeight - s.tableHeight s.termWidth, s.termHeight = w, h if s.tableMode { - tableHeight := s.requestedTableHeight - if tableHeight == 0 { - tableHeight = s.termHeight / 4 - if tableHeight < 1 { - tableHeight = 1 - } else if tableHeight > 10 { - tableHeight = 10 - } - } - if tableHeight > s.termHeight-1 { - tableHeight = s.termHeight - 1 - } - s.tableHeight = tableHeight + s.computeTableHeight() scrollingHeight := s.termHeight - s.tableHeight - if !firstUpdate { - // If the scrolling region has changed, attempt to pan the existing text so that it is - // not overwritten by the table. - if scrollingHeight < oldScrollingHeight { - pan := oldScrollingHeight - scrollingHeight - if pan > s.tableHeight { - pan = s.tableHeight - } - fmt.Fprint(s.writer, ansi.panDown(pan)) + // If the scrolling region has changed, attempt to pan the existing text so that it is + // not overwritten by the table. + if scrollingHeight < oldScrollingHeight { + pan := oldScrollingHeight - scrollingHeight + if pan > s.tableHeight { + pan = s.tableHeight } + fmt.Fprint(s.writer, ansi.panDown(pan)) } } } diff --git a/ui/terminal/status.go b/ui/terminal/status.go index d8e739211..2ad174fee 100644 --- a/ui/terminal/status.go +++ b/ui/terminal/status.go @@ -26,12 +26,12 @@ import ( // // statusFormat takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. -func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild bool) status.StatusOutput { +func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput { formatter := newFormatter(statusFormat, quietBuild) if !forceSimpleOutput && isSmartTerminal(w) { return NewSmartStatusOutput(w, formatter) } else { - return NewSimpleStatusOutput(w, formatter) + return NewSimpleStatusOutput(w, formatter, forceKeepANSI) } } diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go index aa69dff53..810e31d1b 100644 --- a/ui/terminal/status_test.go +++ b/ui/terminal/status_test.go @@ -94,7 +94,7 @@ func TestStatusOutput(t *testing.T) { t.Run("smart", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) tt.calls(stat) stat.Flush() @@ -105,7 +105,7 @@ func TestStatusOutput(t *testing.T) { t.Run("simple", func(t *testing.T) { simple := &bytes.Buffer{} - stat := NewStatusOutput(simple, "", false, false) + stat := NewStatusOutput(simple, "", false, false, false) tt.calls(stat) stat.Flush() @@ -116,7 +116,7 @@ func TestStatusOutput(t *testing.T) { t.Run("force simple", func(t *testing.T) { smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", true, false) + stat := NewStatusOutput(smart, "", true, false, false) tt.calls(stat) stat.Flush() @@ -269,7 +269,7 @@ func TestSmartStatusOutputWidthChange(t *testing.T) { os.Setenv(tableHeightEnVar, "") smart := &fakeSmartTerminal{termWidth: 40} - stat := NewStatusOutput(smart, "", false, false) + stat := NewStatusOutput(smart, "", false, false, false) smartStat := stat.(*smartStatusOutput) smartStat.sigwinchHandled = make(chan bool) |