Merge "Add java_binary_host support for Windows"
diff --git a/android/bazel.go b/android/bazel.go
index 8341e57..bfd0f90 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -150,17 +150,23 @@
"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 @@
// 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 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 @@
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 ccbc156..b5746f7 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// (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 @@
// 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 @@
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 @@
// 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 @@
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 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 763cd7c..2e378ba 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -263,38 +263,56 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
return OptionalPath{}
}
if !exists {
- return OptionalPath{}
+ return InvalidOptionalPath(path.String() + " does not exist")
}
return OptionalPathForPath(path)
}
@@ -1127,6 +1146,7 @@
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 @@
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 f4e4ce1..3f4625d 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -137,26 +137,35 @@
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 @@
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 bd2faa2..b9d8fa8 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -491,6 +491,66 @@
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) 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 @@
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 bcbf1ad..2a2a1f4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2187,6 +2187,7 @@
name string
expectedError string
bp string
+ preparer android.FixturePreparer
}{
{
name: "Non-updatable apex with non-stable dep",
@@ -2258,6 +2259,30 @@
`,
},
{
+ 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 @@
}
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 1d52a70..40526a6 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -11,7 +11,6 @@
"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 48b2945..45a3cb6 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -43,7 +43,7 @@
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 2cbb557..b1a6e2c 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -249,7 +249,6 @@
type conversionResults struct {
buildFileToTargets map[string]BazelTargets
metrics CodegenMetrics
- compatLayer CodegenCompatLayer
}
func (r conversionResults) BuildDirToTargets() map[string]BazelTargets {
@@ -262,11 +261,7 @@
// 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 @@
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 @@
// 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 @@
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 371593b..b3a1053 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -1022,18 +1022,18 @@
}),
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 01dbdb9..0000000
--- 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 75bc2b4..354abf6 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -16,29 +16,19 @@
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 56ea589..dfa1a9e 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -80,7 +80,7 @@
}
func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
- files := CreateSoongInjectionFiles(CodegenCompatLayer{})
+ files := CreateSoongInjectionFiles(CodegenMetrics{})
expectedFilePaths := []bazelFilepath{
{
@@ -92,8 +92,8 @@
basename: "constants.bzl",
},
{
- dir: "module_name_to_label",
- basename: GeneratedBuildFileName,
+ dir: "metrics",
+ basename: "converted_modules.txt",
},
}
@@ -107,9 +107,5 @@
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 645ef2d..1cc4143 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -9,31 +9,69 @@
// 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
+
+ // Total number of Soong modules converted to handcrafted targets
+ handCraftedModuleCount int
+
+ // Total number of unconverted Soong modules
+ unconvertedModuleCount int
// Counts of generated Bazel targets per Bazel rule class
- RuleClassCount map[string]int
-
- // Total number of handcrafted targets
- handCraftedTargetCount int
+ 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 67ea70e..e48f757 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -20,6 +20,7 @@
"android/soong/android"
"android/soong/bazel"
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -136,10 +137,10 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 4b0a4b6..b494f7b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -454,15 +454,19 @@
}
// 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 @@
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 34d68a1..b535e7f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -39,6 +39,9 @@
// 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 @@
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 @@
}
// 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 8c678a1..d4fcf7c 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -55,6 +55,8 @@
"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 703d57f..8a572f9 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -143,6 +143,8 @@
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 @@
// 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 @@
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 704b03a..7879a7d 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -272,7 +272,7 @@
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 3934784..047a69e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -357,7 +357,8 @@
}
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 fa63b46..3c9cac1 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -218,10 +218,16 @@
}
}
+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 fe567a9..be81487 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -24,6 +24,7 @@
"io/ioutil"
"os"
"os/exec"
+ "path"
"path/filepath"
"regexp"
"sort"
@@ -164,7 +165,8 @@
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) 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) 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) 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 @@
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 @@
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 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 @@
// 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 @@
}
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 @@
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 @@
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 @@
}
}
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 @@
}
}
- 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 ed6ec11..6729e61 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 @@
args: args{
command: "echo",
args: []string{"foo"},
- timeout: 1 * time.Second,
+ timeout: 10 * time.Second,
},
wantStdout: "foo\n",
},
@@ -58,7 +58,7 @@
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 @@
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 8d8f37f..d2fbed4 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -372,6 +372,7 @@
{{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 d709787..9ee373e 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -164,7 +164,8 @@
// 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 413f423..869e0f7 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -48,7 +48,7 @@
// 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 @@
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 86022c3..78aaa19 100644
--- a/java/base.go
+++ b/java/base.go
@@ -382,7 +382,7 @@
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 @@
} 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 @@
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 @@
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 @@
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 ea011b8..ae124a3 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -263,6 +263,7 @@
kotlincFlags string
kotlincClasspath classpath
+ kotlincDeps android.Paths
proto android.ProtoFlags
}
diff --git a/java/dex.go b/java/dex.go
index 667800f..8045b5c 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -32,6 +32,9 @@
// 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 @@
}, []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 @@
}
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 @@
zipFlags += " -L 0"
}
- commonFlags := d.dexCommonFlags(ctx, minSdkVersion)
+ commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
useR8 := d.effectiveOptimizeEnabled()
if useR8 {
@@ -298,6 +308,7 @@
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 @@
})
} 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 51cd501..1c6fbac 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -1194,13 +1194,6 @@
// 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 c649a15..4a44866 100644
--- a/java/java.go
+++ b/java/java.go
@@ -286,6 +286,7 @@
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 @@
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 3a6fc0f..e4f1bc1 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -81,6 +81,7 @@
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 db30696..cac0af3 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -281,3 +281,46 @@
})
}
}
+
+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 8c401a7..7749310 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -163,17 +163,27 @@
}
}
-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 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 1d39071..2d8aef7 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -376,6 +376,9 @@
}
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 @@
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 d8a77cf..a642753 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -229,6 +229,26 @@
)
}
+// 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 4fa3eb6..b18bfc7 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -38,6 +38,7 @@
"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 209e82b..7b5f298 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -81,6 +81,7 @@
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 @@
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 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 b05d340..b9b7e2c 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -1618,12 +1618,12 @@
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 0000000..1666392
--- /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 0000000..27e8198
--- /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 0000000..c78fa32
--- /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 a35a1ac..f900172 100644
--- a/python/python.go
+++ b/python/python.go
@@ -45,7 +45,7 @@
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 @@
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 @@
// 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 71fe358..b4936b8 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -358,7 +358,7 @@
# 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 4e8c568..936b275 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -24,15 +24,17 @@
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 @@
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 6bdf140..06a4064 100644
--- a/ui/terminal/smart_status.go
+++ b/ui/terminal/smart_status.go
@@ -77,7 +77,12 @@
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 @@
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 d8e7392..2ad174f 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -26,12 +26,12 @@
//
// 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 aa69dff..810e31d 100644
--- a/ui/terminal/status_test.go
+++ b/ui/terminal/status_test.go
@@ -94,7 +94,7 @@
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 @@
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 @@
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 @@
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)