Merge "rust: Add features list to rust_project.json"
diff --git a/android/Android.bp b/android/Android.bp
index 2406321..773aa6a 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -14,6 +14,7 @@
"soong-bazel",
"soong-cquery",
"soong-remoteexec",
+ "soong-response",
"soong-shared",
"soong-ui-metrics_proto",
],
@@ -85,6 +86,7 @@
"androidmk_test.go",
"apex_test.go",
"arch_test.go",
+ "bazel_test.go",
"config_test.go",
"csuite_config_test.go",
"depset_test.go",
diff --git a/android/bazel.go b/android/bazel.go
index 683495b..5bb3879 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,7 +32,13 @@
// If true, bp2build will generate the converted Bazel target for this module. Note: this may
// cause a conflict due to the duplicate targets if label is also set.
- Bp2build_available bool
+ //
+ // This is a bool pointer to support tristates: true, false, not set.
+ //
+ // To opt-in a module, set bazel_module: { bp2build_available: true }
+ // To opt-out a module, set bazel_module: { bp2build_available: false }
+ // To defer the default setting for the directory, do not set the value.
+ Bp2build_available *bool
}
// Properties contains common module properties for Bazel migration purposes.
@@ -54,9 +60,9 @@
HasHandcraftedLabel() bool
HandcraftedLabel() string
GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
- ConvertWithBp2build() bool
+ ConvertWithBp2build(ctx BazelConversionPathContext) bool
GetBazelBuildFileContents(c Config, path, name string) (string, error)
- ConvertedToBazel() bool
+ ConvertedToBazel(ctx BazelConversionPathContext) bool
}
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -91,15 +97,138 @@
if b.HasHandcraftedLabel() {
return b.HandcraftedLabel()
}
- if b.ConvertWithBp2build() {
+ if b.ConvertWithBp2build(ctx) {
return bp2buildModuleLabel(ctx, module)
}
return "" // no label for unconverted module
}
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+ // iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+ // which can also mean that the key doesn't exist in a lookup.
+
+ // all modules in this package and subpackages default to bp2build_available: true.
+ // allows modules to opt-out.
+ Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+ // all modules in this package (not recursively) default to bp2build_available: false.
+ // allows modules to opt-in.
+ Bp2BuildDefaultFalse
+)
+
+var (
+ // Configure modules in these directories to enable bp2build_available: true or false by default.
+ bp2buildDefaultConfig = Bp2BuildConfig{
+ "bionic": Bp2BuildDefaultTrueRecursively,
+ "system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+ "system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+ }
+
+ // Per-module denylist to always opt modules out.
+ bp2buildModuleDoNotConvert = map[string]bool{
+ "libBionicBenchmarksUtils": true,
+ "libbionic_spawn_benchmark": true,
+ "libc_jemalloc_wrapper": true,
+ "libc_bootstrap": true,
+ "libc_init_static": true,
+ "libc_init_dynamic": true,
+ "libc_tzcode": true,
+ "libc_freebsd": true,
+ "libc_freebsd_large_stack": true,
+ "libc_netbsd": true,
+ "libc_openbsd_ndk": true,
+ "libc_openbsd_large_stack": true,
+ "libc_openbsd": true,
+ "libc_gdtoa": true,
+ "libc_fortify": true,
+ "libc_bionic": true,
+ "libc_bionic_ndk": true,
+ "libc_bionic_systrace": true,
+ "libc_pthread": true,
+ "libc_syscalls": true,
+ "libc_aeabi": true,
+ "libc_ndk": true,
+ "libc_nopthread": true,
+ "libc_common": true,
+ "libc_static_dispatch": true,
+ "libc_dynamic_dispatch": true,
+ "libc_common_static": true,
+ "libc_common_shared": true,
+ "libc_unwind_static": true,
+ "libc_nomalloc": true,
+ "libasync_safe": true,
+ "libc_malloc_debug_backtrace": true,
+ "libsystemproperties": true,
+ "libdl_static": true,
+ "liblinker_main": true,
+ "liblinker_malloc": true,
+ "liblinker_debuggerd_stub": true,
+ "libbionic_tests_headers_posix": true,
+ "libc_dns": true,
+ }
+)
+
// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build() bool {
- return b.bazelProperties.Bazel_module.Bp2build_available
+func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
+ if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
+ return false
+ }
+
+ // Ensure that the module type of this module has a bp2build converter. This
+ // prevents mixed builds from using auto-converted modules just by matching
+ // the package dir; it also has to have a bp2build mutator as well.
+ if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+ return false
+ }
+
+ packagePath := ctx.ModuleDir()
+ config := ctx.Config().bp2buildPackageConfig
+
+ // This is a tristate value: true, false, or unset.
+ propValue := b.bazelProperties.Bazel_module.Bp2build_available
+ if bp2buildDefaultTrueRecursively(packagePath, config) {
+ // Allow modules to explicitly opt-out.
+ return proptools.BoolDefault(propValue, true)
+ }
+
+ // Allow modules to explicitly opt-in.
+ return proptools.BoolDefault(propValue, false)
+}
+
+// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
+// set of package prefixes where all modules must be converted. That is, if the
+// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will
+// return true.
+//
+// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry
+// exactly, this module will return false early.
+//
+// This function will also return false if the package doesn't match anything in
+// the config.
+func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
+ ret := false
+
+ if config[packagePath] == Bp2BuildDefaultFalse {
+ return false
+ }
+
+ packagePrefix := ""
+ // e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
+ for _, part := range strings.Split(packagePath, "/") {
+ packagePrefix += part
+ if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+ // package contains this prefix and this prefix should convert all modules
+ return true
+ }
+ // Continue to the next part of the package dir.
+ packagePrefix += "/"
+ }
+
+ return ret
}
// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
@@ -126,6 +255,6 @@
// ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
// or manually
-func (b *BazelModuleBase) ConvertedToBazel() bool {
- return b.ConvertWithBp2build() || b.HasHandcraftedLabel()
+func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
+ return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
}
diff --git a/android/bazel_test.go b/android/bazel_test.go
new file mode 100644
index 0000000..e5d8fbb
--- /dev/null
+++ b/android/bazel_test.go
@@ -0,0 +1,134 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package android
+
+import "testing"
+
+func TestConvertAllModulesInPackage(t *testing.T) {
+ testCases := []struct {
+ prefixes Bp2BuildConfig
+ packageDir string
+ }{
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "d/e/f": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "a/b": Bp2BuildDefaultFalse,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ }
+
+ for _, test := range testCases {
+ if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
+ }
+ }
+}
+
+func TestModuleOptIn(t *testing.T) {
+ testCases := []struct {
+ prefixes Bp2BuildConfig
+ packageDir string
+ }{
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a", // opt-in by default
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "d/e/f": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "foo/bar",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultTrueRecursively,
+ "a/b": Bp2BuildDefaultFalse,
+ "a/b/c": Bp2BuildDefaultTrueRecursively,
+ },
+ packageDir: "a/b",
+ },
+ {
+ prefixes: Bp2BuildConfig{
+ "a": Bp2BuildDefaultFalse,
+ "a/b": Bp2BuildDefaultTrueRecursively,
+ "a/b/c": Bp2BuildDefaultFalse,
+ },
+ packageDir: "a",
+ },
+ }
+
+ for _, test := range testCases {
+ if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+ t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
+ }
+ }
+}
diff --git a/android/config.go b/android/config.go
index e335de0..cfbc37f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -140,6 +140,9 @@
fs pathtools.FileSystem
mockBpList string
+ bp2buildPackageConfig Bp2BuildConfig
+ bp2buildModuleTypeConfig map[string]bool
+
// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
// in tests when a path doesn't exist.
TestAllowNonExistentPaths bool
@@ -281,6 +284,8 @@
config.mockFileSystem(bp, fs)
+ config.bp2buildModuleTypeConfig = map[string]bool{}
+
return Config{config}
}
@@ -452,6 +457,8 @@
Bool(config.productVariables.ClangCoverage))
config.BazelContext, err = NewBazelContext(config)
+ config.bp2buildPackageConfig = bp2buildDefaultConfig
+ config.bp2buildModuleTypeConfig = make(map[string]bool)
return Config{config}, err
}
diff --git a/android/filegroup.go b/android/filegroup.go
index 2eb4741..abbb4d4 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -53,7 +53,7 @@
func FilegroupBp2Build(ctx TopDownMutatorContext) {
fg, ok := ctx.Module().(*fileGroup)
- if !ok || !fg.ConvertWithBp2build() {
+ if !ok || !fg.ConvertWithBp2build(ctx) {
return
}
diff --git a/android/fixture.go b/android/fixture.go
index 6c9ea6b..8d62958 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -138,7 +138,7 @@
// }
//
// func TestJavaStuff(t *testing.T) {
-// result := android.GroupFixturePreparers(t,
+// result := android.GroupFixturePreparers(
// prepareForJavaTest,
// android.FixtureWithRootAndroidBp(`java_library {....}`),
// android.MockFS{...}.AddToFixture(),
diff --git a/android/mutator.go b/android/mutator.go
index 9552aa1..9e99bee 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -226,7 +226,7 @@
var bp2buildPreArchMutators = []RegisterMutatorFunc{}
var bp2buildDepsMutators = []RegisterMutatorFunc{}
-var bp2buildMutators = []RegisterMutatorFunc{}
+var bp2buildMutators = map[string]RegisterMutatorFunc{}
// RegisterBp2BuildMutator registers specially crafted mutators for
// converting Blueprint/Android modules into special modules that can
@@ -237,7 +237,7 @@
f := func(ctx RegisterMutatorsContext) {
ctx.TopDown(moduleType, m)
}
- bp2buildMutators = append(bp2buildMutators, f)
+ bp2buildMutators[moduleType] = f
}
// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index b8ef0f5..de0197a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -308,7 +308,8 @@
}
}),
test.fs.AddToFixture(),
- ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
RunTest(t)
})
}
diff --git a/android/paths.go b/android/paths.go
index babf48c..1278961 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -174,14 +174,41 @@
// example, Rel on a PathsForModuleSrc would return the path relative to the module source
// directory, and OutputPath.Join("foo").Rel() would return "foo".
Rel() string
+
+ // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
+ //
+ // It is guaranteed to always return the same type as it is called on, e.g. if called on an
+ // InstallPath then the returned value can be converted to an InstallPath.
+ //
+ // A standard build has the following structure:
+ // ../top/
+ // out/ - make install files go here.
+ // out/soong - this is the buildDir passed to NewTestConfig()
+ // ... - the source files
+ //
+ // This function converts a path so that it appears relative to the ../top/ directory, i.e.
+ // * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top
+ // relative path "out/<path>"
+ // * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are
+ // converted into the top relative path "out/soong/<path>".
+ // * Source paths are already relative to the top.
+ // * Phony paths are not relative to anything.
+ // * toolDepPath have an absolute but known value in so don't need making relative to anything in
+ // order to test.
+ RelativeToTop() Path
}
+const (
+ OutDir = "out"
+ OutSoongDir = OutDir + "/soong"
+)
+
// WritablePath is a type of path that can be used as an output for build rules.
type WritablePath interface {
Path
// return the path to the build directory.
- buildDir() string
+ getBuildDir() string
// the writablePath method doesn't directly do anything,
// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
@@ -271,6 +298,20 @@
// Paths is a slice of Path objects, with helpers to operate on the collection.
type Paths []Path
+// RelativeToTop creates a new Paths containing the result of calling Path.RelativeToTop on each
+// item in this slice.
+func (p Paths) RelativeToTop() Paths {
+ ensureTestOnly()
+ if p == nil {
+ return p
+ }
+ ret := make(Paths, len(p))
+ for i, path := range p {
+ ret[i] = path.RelativeToTop()
+ }
+ return ret
+}
+
func (paths Paths) containsPath(path Path) bool {
for _, p := range paths {
if p == path {
@@ -340,6 +381,7 @@
GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
Module() Module
+ ModuleType() string
OtherModuleName(m blueprint.Module) string
OtherModuleDir(m blueprint.Module) string
}
@@ -450,7 +492,7 @@
// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
b, ok := module.(Bazelable)
// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
- if !ok || !b.ConvertedToBazel() {
+ if !ok || !b.ConvertedToBazel(ctx) {
return bp2buildModuleLabel(ctx, module)
}
return b.GetBazelLabel(ctx, module)
@@ -909,6 +951,20 @@
// WritablePaths is a slice of WritablePath, used for multiple outputs.
type WritablePaths []WritablePath
+// RelativeToTop creates a new WritablePaths containing the result of calling Path.RelativeToTop on
+// each item in this slice.
+func (p WritablePaths) RelativeToTop() WritablePaths {
+ ensureTestOnly()
+ if p == nil {
+ return p
+ }
+ ret := make(WritablePaths, len(p))
+ for i, path := range p {
+ ret[i] = path.RelativeToTop().(WritablePath)
+ }
+ return ret
+}
+
// Strings returns the string forms of the writable paths.
func (p WritablePaths) Strings() []string {
if p == nil {
@@ -934,9 +990,8 @@
}
type basePath struct {
- path string
- config Config
- rel string
+ path string
+ rel string
}
func (p basePath) Ext() string {
@@ -967,6 +1022,14 @@
// SourcePath is a Path representing a file path rooted from SrcDir
type SourcePath struct {
basePath
+
+ // The sources root, i.e. Config.SrcDir()
+ srcDir string
+}
+
+func (p SourcePath) RelativeToTop() Path {
+ ensureTestOnly()
+ return p
}
var _ Path = SourcePath{}
@@ -980,7 +1043,7 @@
// code that is embedding ninja variables in paths
func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validateSafePath(pathComponents...)
- ret := SourcePath{basePath{p, ctx.Config(), ""}}
+ ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
if err != nil {
return ret, err
}
@@ -996,7 +1059,7 @@
// pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
p, err := validatePath(pathComponents...)
- ret := SourcePath{basePath{p, ctx.Config(), ""}}
+ ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
if err != nil {
return ret, err
}
@@ -1090,7 +1153,7 @@
}
func (p SourcePath) String() string {
- return filepath.Join(p.config.srcDir, p.path)
+ return filepath.Join(p.srcDir, p.path)
}
// Join creates a new SourcePath with paths... joined with the current path. The
@@ -1122,7 +1185,7 @@
ReportPathErrorf(ctx, "Cannot find relative path for %s(%s)", reflect.TypeOf(path).Name(), path)
return OptionalPath{}
}
- dir := filepath.Join(p.config.srcDir, p.path, relDir)
+ dir := filepath.Join(p.srcDir, p.path, relDir)
// Use Glob so that we are run again if the directory is added.
if pathtools.IsGlob(dir) {
ReportPathErrorf(ctx, "Path may not contain a glob: %s", dir)
@@ -1135,13 +1198,17 @@
if len(paths) == 0 {
return OptionalPath{}
}
- relPath := Rel(ctx, p.config.srcDir, paths[0])
+ relPath := Rel(ctx, p.srcDir, paths[0])
return OptionalPathForPath(PathForSource(ctx, relPath))
}
// OutputPath is a Path representing an intermediates file path rooted from the build directory
type OutputPath struct {
basePath
+
+ // The soong build directory, i.e. Config.BuildDir()
+ buildDir string
+
fullPath string
}
@@ -1156,8 +1223,18 @@
return p
}
-func (p OutputPath) buildDir() string {
- return p.config.buildDir
+func (p OutputPath) getBuildDir() string {
+ return p.buildDir
+}
+
+func (p OutputPath) RelativeToTop() Path {
+ return p.outputPathRelativeToTop()
+}
+
+func (p OutputPath) outputPathRelativeToTop() OutputPath {
+ p.fullPath = StringPathRelativeToTop(p.buildDir, p.fullPath)
+ p.buildDir = OutSoongDir
+ return p
}
func (p OutputPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
@@ -1173,6 +1250,11 @@
basePath
}
+func (t toolDepPath) RelativeToTop() Path {
+ ensureTestOnly()
+ return t
+}
+
var _ Path = toolDepPath{}
// pathForBuildToolDep returns a toolDepPath representing the given path string.
@@ -1181,7 +1263,7 @@
// Only use this function to construct paths for dependencies of the build
// tool invocation.
func pathForBuildToolDep(ctx PathContext, path string) toolDepPath {
- return toolDepPath{basePath{path, ctx.Config(), ""}}
+ return toolDepPath{basePath{path, ""}}
}
// PathForOutput joins the provided paths and returns an OutputPath that is
@@ -1194,7 +1276,7 @@
}
fullPath := filepath.Join(ctx.Config().buildDir, path)
path = fullPath[len(fullPath)-len(path):]
- return OutputPath{basePath{path, ctx.Config(), ""}, fullPath}
+ return OutputPath{basePath{path, ""}, ctx.Config().buildDir, fullPath}
}
// PathsForOutput returns Paths rooted from buildDir
@@ -1350,7 +1432,13 @@
OutputPath
}
+func (p ModuleOutPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleOutPath{}
+var _ WritablePath = ModuleOutPath{}
func (p ModuleOutPath) objPathWithExt(ctx ModuleOutPathContext, subdir, ext string) ModuleObjPath {
return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
@@ -1428,7 +1516,8 @@
reportPathError(ctx, err)
}
- outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ outputPath := OutputPath{basePath{"", ""},
+ ctx.Config().buildDir,
ctx.Config().BazelContext.OutputBase()}
return BazelOutPath{
@@ -1454,7 +1543,13 @@
ModuleOutPath
}
+func (p ModuleGenPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleGenPath{}
+var _ WritablePath = ModuleGenPath{}
var _ genPathProvider = ModuleGenPath{}
var _ objPathProvider = ModuleGenPath{}
@@ -1487,7 +1582,13 @@
ModuleOutPath
}
+func (p ModuleObjPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleObjPath{}
+var _ WritablePath = ModuleObjPath{}
// PathForModuleObj returns a Path representing the paths... under the module's
// 'obj' directory.
@@ -1505,7 +1606,13 @@
ModuleOutPath
}
+func (p ModuleResPath) RelativeToTop() Path {
+ p.OutputPath = p.outputPathRelativeToTop()
+ return p
+}
+
var _ Path = ModuleResPath{}
+var _ WritablePath = ModuleResPath{}
// PathForModuleRes returns a Path representing the paths... under the module's
// 'res' directory.
@@ -1522,6 +1629,9 @@
type InstallPath struct {
basePath
+ // The soong build directory, i.e. Config.BuildDir()
+ buildDir string
+
// partitionDir is the part of the InstallPath that is automatically determined according to the context.
// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
partitionDir string
@@ -1530,8 +1640,28 @@
makePath bool
}
-func (p InstallPath) buildDir() string {
- return p.config.buildDir
+// Will panic if called from outside a test environment.
+func ensureTestOnly() {
+ // Normal soong test environment
+ if InList("-test.short", os.Args) {
+ return
+ }
+ // IntelliJ test environment
+ if InList("-test.v", os.Args) {
+ return
+ }
+
+ panic(fmt.Errorf("Not in test\n%s", strings.Join(os.Args, "\n")))
+}
+
+func (p InstallPath) RelativeToTop() Path {
+ ensureTestOnly()
+ p.buildDir = OutSoongDir
+ return p
+}
+
+func (p InstallPath) getBuildDir() string {
+ return p.buildDir
}
func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1546,9 +1676,9 @@
func (p InstallPath) String() string {
if p.makePath {
// Make path starts with out/ instead of out/soong.
- return filepath.Join(p.config.buildDir, "../", p.path)
+ return filepath.Join(p.buildDir, "../", p.path)
} else {
- return filepath.Join(p.config.buildDir, p.path)
+ return filepath.Join(p.buildDir, p.path)
}
}
@@ -1557,9 +1687,9 @@
// The ./soong is dropped if the install path is for Make.
func (p InstallPath) PartitionDir() string {
if p.makePath {
- return filepath.Join(p.config.buildDir, "../", p.partitionDir)
+ return filepath.Join(p.buildDir, "../", p.partitionDir)
} else {
- return filepath.Join(p.config.buildDir, p.partitionDir)
+ return filepath.Join(p.buildDir, p.partitionDir)
}
}
@@ -1642,7 +1772,8 @@
}
base := InstallPath{
- basePath: basePath{partionPath, ctx.Config(), ""},
+ basePath: basePath{partionPath, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: partionPath,
makePath: false,
}
@@ -1652,7 +1783,8 @@
func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
base := InstallPath{
- basePath: basePath{prefix, ctx.Config(), ""},
+ basePath: basePath{prefix, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: prefix,
makePath: false,
}
@@ -1787,7 +1919,7 @@
if strings.ContainsAny(phony, "$/") {
ReportPathErrorf(ctx, "Phony target contains invalid character ($ or /): %s", phony)
}
- return PhonyPath{basePath{phony, ctx.Config(), ""}}
+ return PhonyPath{basePath{phony, ""}}
}
type PhonyPath struct {
@@ -1796,8 +1928,16 @@
func (p PhonyPath) writablePath() {}
-func (p PhonyPath) buildDir() string {
- return p.config.buildDir
+func (p PhonyPath) getBuildDir() string {
+ // A phone path cannot contain any / so cannot be relative to the build directory.
+ return ""
+}
+
+func (p PhonyPath) RelativeToTop() Path {
+ ensureTestOnly()
+ // A phony path cannot contain any / so does not have a build directory so switching to a new
+ // build directory has no effect so just return this path.
+ return p
}
func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1811,10 +1951,17 @@
basePath
}
+func (p testPath) RelativeToTop() Path {
+ ensureTestOnly()
+ return p
+}
+
func (p testPath) String() string {
return p.path
}
+var _ Path = testPath{}
+
// PathForTesting returns a Path constructed from joining the elements of paths with '/'. It should only be used from
// within tests.
func PathForTesting(paths ...string) Path {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 04864a1..ebccaa7 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -272,11 +272,9 @@
}
}
-// PrebuiltPostDepsMutator does two operations. It replace dependencies on the
-// source module with dependencies on the prebuilt when both modules exist and
-// the prebuilt should be used. When the prebuilt should not be used, disable
-// installing it. Secondly, it also adds a sourcegroup to any filegroups found
-// in the prebuilt's 'Srcs' property.
+// PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
+// prebuilt when both modules exist and the prebuilt should be used. When the prebuilt should not
+// be used, disable installing it.
func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
diff --git a/android/register.go b/android/register.go
index aeaa6ff..35469d4 100644
--- a/android/register.go
+++ b/android/register.go
@@ -174,7 +174,13 @@
t.register(ctx)
}
- RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators)
+ bp2buildMutatorList := []RegisterMutatorFunc{}
+ for t, f := range bp2buildMutators {
+ ctx.config.bp2buildModuleTypeConfig[t] = true
+ bp2buildMutatorList = append(bp2buildMutatorList, f)
+ }
+
+ RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutatorList)
}
// Register the pipeline of singletons, module types, and mutators for
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 0d8e2b7..06e82c8 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -28,6 +28,7 @@
"android/soong/cmd/sbox/sbox_proto"
"android/soong/remoteexec"
+ "android/soong/response"
"android/soong/shared"
)
@@ -408,30 +409,21 @@
func (r *RuleBuilder) RspFileInputs() Paths {
var rspFileInputs Paths
for _, c := range r.commands {
- if c.rspFileInputs != nil {
- if rspFileInputs != nil {
- panic("Multiple commands in a rule may not have rsp file inputs")
- }
- rspFileInputs = c.rspFileInputs
+ for _, rspFile := range c.rspFiles {
+ rspFileInputs = append(rspFileInputs, rspFile.paths...)
}
}
return rspFileInputs
}
-// RspFile returns the path to the rspfile that was passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
-func (r *RuleBuilder) RspFile() WritablePath {
- var rspFile WritablePath
+func (r *RuleBuilder) rspFiles() []rspFileAndPaths {
+ var rspFiles []rspFileAndPaths
for _, c := range r.commands {
- if c.rspFile != nil {
- if rspFile != nil {
- panic("Multiple commands in a rule may not have rsp file inputs")
- }
- rspFile = c.rspFile
- }
+ rspFiles = append(rspFiles, c.rspFiles...)
}
- return rspFile
+ return rspFiles
}
// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
@@ -459,26 +451,6 @@
Inputs(depFiles.Paths())
}
-// composeRspFileContent returns a string that will serve as the contents of the rsp file to pass
-// the listed input files to the command running in the sandbox.
-func (r *RuleBuilder) composeRspFileContent(rspFileInputs Paths) string {
- if r.sboxInputs {
- if len(rspFileInputs) > 0 {
- // When SandboxInputs is used the paths need to be rewritten to be relative to the sandbox
- // directory so that they are valid after sbox chdirs into the sandbox directory.
- return proptools.NinjaEscape(strings.Join(r.sboxPathsForInputsRel(rspFileInputs), " "))
- } else {
- // If the list of inputs is empty fall back to "$in" so that the rspfilecontent Ninja
- // variable is set to something non-empty, otherwise ninja will complain. The inputs
- // will be empty (all the non-rspfile inputs are implicits), so $in will evaluate to
- // an empty string.
- return "$in"
- }
- } else {
- return "$in"
- }
-}
-
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs.
func (r *RuleBuilder) Build(name string, desc string) {
@@ -520,8 +492,7 @@
commands := r.Commands()
outputs := r.Outputs()
inputs := r.Inputs()
- rspFileInputs := r.RspFileInputs()
- rspFilePath := r.RspFile()
+ rspFiles := r.rspFiles()
if len(commands) == 0 {
return
@@ -575,11 +546,22 @@
})
}
- // If using an rsp file copy it into the sbox directory.
- if rspFilePath != nil {
- command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
- From: proto.String(rspFilePath.String()),
- To: proto.String(r.sboxPathForInputRel(rspFilePath)),
+ // If using rsp files copy them and their contents into the sbox directory with
+ // the appropriate path mappings.
+ for _, rspFile := range rspFiles {
+ command.RspFiles = append(command.RspFiles, &sbox_proto.RspFile{
+ File: proto.String(rspFile.file.String()),
+ // These have to match the logic in sboxPathForInputRel
+ PathMappings: []*sbox_proto.PathMapping{
+ {
+ From: proto.String(r.outDir.String()),
+ To: proto.String(sboxOutSubDir),
+ },
+ {
+ From: proto.String(PathForOutput(r.ctx).String()),
+ To: proto.String(sboxOutSubDir),
+ },
+ },
})
}
@@ -641,20 +623,30 @@
inputs = append(inputs, sboxCmd.inputs...)
if r.rbeParams != nil {
- var remoteInputs []string
- remoteInputs = append(remoteInputs, inputs.Strings()...)
- remoteInputs = append(remoteInputs, tools.Strings()...)
- remoteInputs = append(remoteInputs, rspFileInputs.Strings()...)
- if rspFilePath != nil {
- remoteInputs = append(remoteInputs, rspFilePath.String())
+ // RBE needs a list of input files to copy to the remote builder. For inputs already
+ // listed in an rsp file, pass the rsp file directly to rewrapper. For the rest,
+ // create a new rsp file to pass to rewrapper.
+ var remoteRspFiles Paths
+ var remoteInputs Paths
+
+ remoteInputs = append(remoteInputs, inputs...)
+ remoteInputs = append(remoteInputs, tools...)
+
+ for _, rspFile := range rspFiles {
+ remoteInputs = append(remoteInputs, rspFile.file)
+ remoteRspFiles = append(remoteRspFiles, rspFile.file)
}
- inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
- inputsListContents := rspFileForInputs(remoteInputs)
- WriteFileRule(r.ctx, inputsListFile, inputsListContents)
- inputs = append(inputs, inputsListFile)
+
+ if len(remoteInputs) > 0 {
+ inputsListFile := r.sboxManifestPath.ReplaceExtension(r.ctx, "rbe_inputs.list")
+ writeRspFileRule(r.ctx, inputsListFile, remoteInputs)
+ remoteRspFiles = append(remoteRspFiles, inputsListFile)
+ // Add the new rsp file as an extra input to the rule.
+ inputs = append(inputs, inputsListFile)
+ }
r.rbeParams.OutputFiles = outputs.Strings()
- r.rbeParams.RSPFile = inputsListFile.String()
+ r.rbeParams.RSPFiles = remoteRspFiles.Strings()
rewrapperCommand := r.rbeParams.NoVarTemplate(r.ctx.Config().RBEWrapper())
commandString = rewrapperCommand + " bash -c '" + strings.ReplaceAll(commandString, `'`, `'\''`) + "'"
}
@@ -672,9 +664,24 @@
implicitOutputs := outputs[1:]
var rspFile, rspFileContent string
- if rspFilePath != nil {
- rspFile = rspFilePath.String()
- rspFileContent = r.composeRspFileContent(rspFileInputs)
+ var rspFileInputs Paths
+ if len(rspFiles) > 0 {
+ // The first rsp files uses Ninja's rsp file support for the rule
+ rspFile = rspFiles[0].file.String()
+ // Use "$in" for rspFileContent to avoid duplicating the list of files in the dependency
+ // list and in the contents of the rsp file. Inputs to the rule that are not in the
+ // rsp file will be listed in Implicits instead of Inputs so they don't show up in "$in".
+ rspFileContent = "$in"
+ rspFileInputs = append(rspFileInputs, rspFiles[0].paths...)
+
+ for _, rspFile := range rspFiles[1:] {
+ // Any additional rsp files need an extra rule to write the file.
+ writeRspFileRule(r.ctx, rspFile.file, rspFile.paths)
+ // The main rule needs to depend on the inputs listed in the extra rsp file.
+ inputs = append(inputs, rspFile.paths...)
+ // The main rule needs to depend on the extra rsp file.
+ inputs = append(inputs, rspFile.file)
+ }
}
var pool blueprint.Pool
@@ -725,8 +732,12 @@
depFiles WritablePaths
tools Paths
packagedTools []PackagingSpec
- rspFileInputs Paths
- rspFile WritablePath
+ rspFiles []rspFileAndPaths
+}
+
+type rspFileAndPaths struct {
+ file WritablePath
+ paths Paths
}
func (c *RuleBuilderCommand) addInput(path Path) string {
@@ -782,13 +793,6 @@
return path.String()
}
-// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
-// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
-// sandboxing are enabled.
-func SboxPathForTool(ctx BuilderContext, path Path) string {
- return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
-}
-
func sboxPathForToolRel(ctx BuilderContext, path Path) string {
// Errors will be handled in RuleBuilder.Build where we have a context to report them
relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
@@ -831,17 +835,21 @@
return ret
}
-// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
-// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
-// reference the tool.
-func SboxPathForPackagedTool(spec PackagingSpec) string {
- return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
-}
-
func sboxPathForPackagedToolRel(spec PackagingSpec) string {
return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
}
+// PathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
+// reference the tool.
+func (c *RuleBuilderCommand) PathForPackagedTool(spec PackagingSpec) string {
+ if !c.rule.sboxTools {
+ panic("PathForPackagedTool() requires SandboxTools()")
+ }
+
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
// if it is not. This can be used on the RuleBuilder command line to reference the tool.
@@ -852,6 +860,20 @@
return path.String()
}
+// PathsForTools takes a list of paths to tools, which may be output files or source files, and
+// returns the corresponding paths for the tools in the sbox sandbox if sbox is enabled, or the
+// original paths if it is not. This can be used on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathsForTools(paths Paths) []string {
+ if c.rule.sbox && c.rule.sboxTools {
+ var ret []string
+ for _, path := range paths {
+ ret = append(ret, filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path)))
+ }
+ return ret
+ }
+ return paths.Strings()
+}
+
// PackagedTool adds the specified tool path to the command line. It can only be used with tool
// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
@@ -1170,22 +1192,19 @@
return c.Text(flag + c.PathForOutput(path))
}
-// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
-// between them. The paths will be written to the rspfile. If sbox is enabled, the rspfile must
-// be outside the sbox directory.
+// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with
+// no separator between them. The paths will be written to the rspfile. If sbox is enabled, the
+// rspfile must be outside the sbox directory. The first use of FlagWithRspFileInputList in any
+// RuleBuilderCommand of a RuleBuilder will use Ninja's rsp file support for the rule, additional
+// uses will result in an auxiliary rules to write the rspFile contents.
func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, rspFile WritablePath, paths Paths) *RuleBuilderCommand {
- if c.rspFileInputs != nil {
- panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
- }
-
// Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
// generated.
if paths == nil {
paths = Paths{}
}
- c.rspFileInputs = paths
- c.rspFile = rspFile
+ c.rspFiles = append(c.rspFiles, rspFileAndPaths{rspFile, paths})
if c.rule.sbox {
if _, isRel, _ := maybeRelErr(c.rule.outDir.String(), rspFile.String()); isRel {
@@ -1264,13 +1283,12 @@
}
func (builderContextForTests) Build(PackageContext, BuildParams) {}
-func rspFileForInputs(paths []string) string {
- s := strings.Builder{}
- for i, path := range paths {
- if i != 0 {
- s.WriteByte(' ')
- }
- s.WriteString(proptools.ShellEscape(path))
+func writeRspFileRule(ctx BuilderContext, rspFile WritablePath, paths Paths) {
+ buf := &strings.Builder{}
+ err := response.WriteRspFile(buf, paths.Strings())
+ if err != nil {
+ // There should never be I/O errors writing to a bytes.Buffer.
+ panic(err)
}
- return s.String()
+ WriteFileRule(ctx, rspFile, buf.String())
}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 9cd60a2..9c5ca41 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -376,8 +376,6 @@
wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer " +
"out_local/module/DepFile out_local/module/depfile out_local/module/ImplicitDepFile out_local/module/depfile2"
- wantRspFileContent := "$in"
-
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
@@ -389,8 +387,6 @@
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
-
- AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
})
t.Run("sbox", func(t *testing.T) {
@@ -409,8 +405,6 @@
wantDepMergerCommand := "out_local/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
- wantRspFileContent := "$in"
-
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
@@ -422,8 +416,6 @@
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
-
- AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
})
t.Run("sbox tools", func(t *testing.T) {
@@ -442,7 +434,34 @@
wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
- wantRspFileContent := "$in"
+ AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
+
+ AssertDeepEquals(t, "rule.Inputs()", wantInputs, rule.Inputs())
+ AssertDeepEquals(t, "rule.RspfileInputs()", wantRspFileInputs, rule.RspFileInputs())
+ AssertDeepEquals(t, "rule.Outputs()", wantOutputs, rule.Outputs())
+ AssertDeepEquals(t, "rule.SymlinkOutputs()", wantSymlinkOutputs, rule.SymlinkOutputs())
+ AssertDeepEquals(t, "rule.DepFiles()", wantDepFiles, rule.DepFiles())
+ AssertDeepEquals(t, "rule.Tools()", wantTools, rule.Tools())
+ AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
+
+ AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
+ })
+
+ t.Run("sbox inputs", func(t *testing.T) {
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, "module"),
+ PathForOutput(ctx, "sbox.textproto")).SandboxInputs()
+ addCommands(rule)
+
+ wantCommands := []string{
+ "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile " +
+ "FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output " +
+ "FlagWithRspFileInputList=__SBOX_SANDBOX_DIR__/out/rsp Input __SBOX_SANDBOX_DIR__/out/Output " +
+ "__SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+ "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3 input3 __SBOX_SANDBOX_DIR__/out/output2",
+ }
+
+ wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
AssertDeepEquals(t, "rule.Commands()", wantCommands, rule.Commands())
@@ -455,8 +474,6 @@
AssertDeepEquals(t, "rule.OrderOnlys()", wantOrderOnlys, rule.OrderOnlys())
AssertSame(t, "rule.depFileMergerCmd()", wantDepMergerCommand, rule.depFileMergerCmd(rule.DepFiles()).String())
-
- AssertSame(t, "rule.composeRspFileContent()", wantRspFileContent, rule.composeRspFileContent(rule.RspFileInputs()))
})
}
@@ -472,8 +489,9 @@
properties struct {
Srcs []string
- Restat bool
- Sbox bool
+ Restat bool
+ Sbox bool
+ Sbox_inputs bool
}
}
@@ -482,9 +500,15 @@
out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
outDir := PathForModuleOut(ctx, "gen")
+ rspFile := PathForModuleOut(ctx, "rsp")
+ rspFile2 := PathForModuleOut(ctx, "rsp2")
+ rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
+ rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForModuleOut(ctx, "sbox.textproto")
- testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
+ testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat,
+ t.properties.Sbox, t.properties.Sbox_inputs, rspFile, rspFileContents,
+ rspFile2, rspFileContents2)
}
type testRuleBuilderSingleton struct{}
@@ -498,18 +522,35 @@
out := PathForOutput(ctx, "singleton/gen/baz")
outDep := PathForOutput(ctx, "singleton/gen/baz.d")
outDir := PathForOutput(ctx, "singleton/gen")
+ rspFile := PathForOutput(ctx, "singleton/rsp")
+ rspFile2 := PathForOutput(ctx, "singleton/rsp2")
+ rspFileContents := PathsForSource(ctx, []string{"rsp_in"})
+ rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
- testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
+ testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false, false,
+ rspFile, rspFileContents, rspFile2, rspFileContents2)
}
-func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
+func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath,
+ restat, sbox, sboxInputs bool,
+ rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
+
rule := NewRuleBuilder(pctx, ctx)
if sbox {
rule.Sbox(outDir, manifestPath)
+ if sboxInputs {
+ rule.SandboxInputs()
+ }
}
- rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
+ rule.Command().
+ Tool(PathForSource(ctx, "cp")).
+ Inputs(in).
+ Output(out).
+ ImplicitDepFile(outDep).
+ FlagWithRspFileInputList("@", rspFile, rspFileContents).
+ FlagWithRspFileInputList("@", rspFile2, rspFileContents2)
if restat {
rule.Restat()
@@ -540,6 +581,12 @@
srcs: ["bar"],
sbox: true,
}
+ rule_builder_test {
+ name: "foo_sbox_inputs",
+ srcs: ["bar"],
+ sbox: true,
+ sbox_inputs: true,
+ }
`
result := GroupFixturePreparers(
@@ -548,7 +595,10 @@
fs.AddToFixture(),
).RunTest(t)
- check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
+ check := func(t *testing.T, params TestingBuildParams, rspFile2Params TestingBuildParams,
+ wantCommand, wantOutput, wantDepfile, wantRspFile, wantRspFile2 string,
+ wantRestat bool, extraImplicits, extraCmdDeps []string) {
+
t.Helper()
command := params.RuleParams.Command
re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
@@ -561,9 +611,19 @@
AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
+ wantInputs := []string{"rsp_in"}
+ AssertArrayString(t, "Inputs", wantInputs, params.Inputs.Strings())
+
wantImplicits := append([]string{"bar"}, extraImplicits...)
+ // The second rsp file and the files listed in it should be in implicits
+ wantImplicits = append(wantImplicits, "rsp_in2", wantRspFile2)
AssertPathsRelativeToTopEquals(t, "Implicits", wantImplicits, params.Implicits)
+ wantRspFileContent := "$in"
+ AssertStringEquals(t, "RspfileContent", wantRspFileContent, params.RuleParams.RspfileContent)
+
+ AssertStringEquals(t, "Rspfile", wantRspFile, params.RuleParams.Rspfile)
+
AssertPathRelativeToTopEquals(t, "Output", wantOutput, params.Output)
if len(params.ImplicitOutputs) != 0 {
@@ -575,18 +635,42 @@
if params.Deps != blueprint.DepsGCC {
t.Errorf("want Deps = %q, got %q", blueprint.DepsGCC, params.Deps)
}
+
+ rspFile2Content := ContentFromFileRuleForTests(t, rspFile2Params)
+ AssertStringEquals(t, "rspFile2 content", "rsp_in2\n", rspFile2Content)
}
t.Run("module", func(t *testing.T) {
outFile := "out/soong/.intermediates/foo/gen/foo"
- check(t, result.ModuleForTests("foo", "").Rule("rule").RelativeToTop(),
- "cp bar "+outFile,
- outFile, outFile+".d", true, nil, nil)
+ rspFile := "out/soong/.intermediates/foo/rsp"
+ rspFile2 := "out/soong/.intermediates/foo/rsp2"
+ module := result.ModuleForTests("foo", "")
+ check(t, module.Rule("rule").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
})
t.Run("sbox", func(t *testing.T) {
outDir := "out/soong/.intermediates/foo_sbox"
outFile := filepath.Join(outDir, "gen/foo_sbox")
depFile := filepath.Join(outDir, "gen/foo_sbox.d")
+ rspFile := filepath.Join(outDir, "rsp")
+ rspFile2 := filepath.Join(outDir, "rsp2")
+ manifest := filepath.Join(outDir, "sbox.textproto")
+ sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
+ sandboxPath := shared.TempDirForOutDir("out/soong")
+
+ cmd := `rm -rf ` + outDir + `/gen && ` +
+ sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
+ module := result.ModuleForTests("foo_sbox", "")
+ check(t, module.Output("gen/foo_sbox").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+ cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
+ })
+ t.Run("sbox_inputs", func(t *testing.T) {
+ outDir := "out/soong/.intermediates/foo_sbox_inputs"
+ outFile := filepath.Join(outDir, "gen/foo_sbox_inputs")
+ depFile := filepath.Join(outDir, "gen/foo_sbox_inputs.d")
+ rspFile := filepath.Join(outDir, "rsp")
+ rspFile2 := filepath.Join(outDir, "rsp2")
manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join("out", "soong", "host", result.Config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir("out/soong")
@@ -594,13 +678,18 @@
cmd := `rm -rf ` + outDir + `/gen && ` +
sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
- check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox").RelativeToTop(),
- cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
+ module := result.ModuleForTests("foo_sbox_inputs", "")
+ check(t, module.Output("gen/foo_sbox_inputs").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+ cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
})
t.Run("singleton", func(t *testing.T) {
outFile := filepath.Join("out/soong/singleton/gen/baz")
- check(t, result.SingletonForTests("rule_builder_test").Rule("rule").RelativeToTop(),
- "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
+ rspFile := filepath.Join("out/soong/singleton/rsp")
+ rspFile2 := filepath.Join("out/soong/singleton/rsp2")
+ singleton := result.SingletonForTests("rule_builder_test")
+ check(t, singleton.Rule("rule").RelativeToTop(), singleton.Output(rspFile2).RelativeToTop(),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
})
}
diff --git a/android/testing.go b/android/testing.go
index 0dfe38c..27573d5 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -117,6 +117,11 @@
}),
)
+// Prepares a test that disallows non-existent paths.
+var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Config) {
+ config.TestAllowNonExistentPaths = false
+})
+
func NewTestArchContext(config Config) *TestContext {
ctx := NewTestContext(config)
ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@@ -158,12 +163,17 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConfig) {
+ ctx.config.bp2buildPackageConfig = config
+}
+
// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
// type to the equivalent Bazel target.
func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
f := func(ctx RegisterMutatorsContext) {
ctx.TopDown(moduleType, m)
}
+ ctx.config.bp2buildModuleTypeConfig[moduleType] = true
ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
}
@@ -529,8 +539,7 @@
// The parts of this structure which are changed are:
// * BuildParams
// * Args
-// * Path instances are intentionally not modified, use AssertPathRelativeToTopEquals or
-// AssertPathsRelativeToTopEquals instead which do something similar.
+// * All Path, Paths, WritablePath and WritablePaths fields.
//
// * RuleParams
// * Command
@@ -554,6 +563,20 @@
// Take a copy of the build params and replace any args that contains test specific temporary
// paths with paths relative to the top.
bparams := p.BuildParams
+ bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile)
+ bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output)
+ bparams.Outputs = bparams.Outputs.RelativeToTop()
+ bparams.SymlinkOutput = normalizeWritablePathRelativeToTop(bparams.SymlinkOutput)
+ bparams.SymlinkOutputs = bparams.SymlinkOutputs.RelativeToTop()
+ bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput)
+ bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop()
+ bparams.Input = normalizePathRelativeToTop(bparams.Input)
+ bparams.Inputs = bparams.Inputs.RelativeToTop()
+ bparams.Implicit = normalizePathRelativeToTop(bparams.Implicit)
+ bparams.Implicits = bparams.Implicits.RelativeToTop()
+ bparams.OrderOnly = bparams.OrderOnly.RelativeToTop()
+ bparams.Validation = normalizePathRelativeToTop(bparams.Validation)
+ bparams.Validations = bparams.Validations.RelativeToTop()
bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args)
// Ditto for any fields in the RuleParams.
@@ -572,6 +595,20 @@
}
}
+func normalizeWritablePathRelativeToTop(path WritablePath) WritablePath {
+ if path == nil {
+ return nil
+ }
+ return path.RelativeToTop().(WritablePath)
+}
+
+func normalizePathRelativeToTop(path Path) Path {
+ if path == nil {
+ return nil
+ }
+ return path.RelativeToTop()
+}
+
// baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
type baseTestingComponent struct {
config Config
@@ -899,7 +936,7 @@
}
p := path.String()
if w, ok := path.(WritablePath); ok {
- rel, err := filepath.Rel(w.buildDir(), p)
+ rel, err := filepath.Rel(w.getBuildDir(), p)
if err != nil {
panic(err)
}
@@ -925,19 +962,14 @@
// PathRelativeToTop returns a string representation of the path relative to a notional top
// directory.
//
-// For a WritablePath it applies StringPathRelativeToTop to it, using the buildDir returned from the
-// WritablePath's buildDir() method. For all other paths, i.e. source paths, that are already
-// relative to the top it just returns their string representation.
+// It return "<nil path>" if the supplied path is nil, otherwise it returns the result of calling
+// Path.RelativeToTop to obtain a relative Path and then calling Path.String on that to get the
+// string representation.
func PathRelativeToTop(path Path) string {
if path == nil {
return "<nil path>"
}
- p := path.String()
- if w, ok := path.(WritablePath); ok {
- buildDir := w.buildDir()
- return StringPathRelativeToTop(buildDir, p)
- }
- return p
+ return path.RelativeToTop().String()
}
// PathsRelativeToTop creates a slice of strings where each string is the result of applying
@@ -954,23 +986,13 @@
// StringPathRelativeToTop returns a string representation of the path relative to a notional top
// directory.
//
-// A standard build has the following structure:
-// ../top/
-// out/ - make install files go here.
-// out/soong - this is the buildDir passed to NewTestConfig()
-// ... - the source files
-//
-// This function converts a path so that it appears relative to the ../top/ directory, i.e.
-// * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top
-// relative path "out/<path>"
-// * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are
-// converted into the top relative path "out/soong/<path>".
-// * Source paths are already relative to the top.
+// See Path.RelativeToTop for more details as to what `relative to top` means.
//
// This is provided for processing paths that have already been converted into a string, e.g. paths
// in AndroidMkEntries structures. As a result it needs to be supplied the soong output dir against
// which it can try and relativize paths. PathRelativeToTop must be used for process Path objects.
func StringPathRelativeToTop(soongOutDir string, path string) string {
+ ensureTestOnly()
// A relative path must be a source path so leave it as it is.
if !filepath.IsAbs(path) {
diff --git a/apex/apex.go b/apex/apex.go
index a67fe1f..dca5595 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,7 +29,6 @@
"android/soong/android"
"android/soong/bpf"
"android/soong/cc"
- "android/soong/dexpreopt"
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
@@ -1573,9 +1572,6 @@
if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
return false
}
- if depTag == dexpreopt.Dex2oatDepTag {
- return false
- }
ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b159660..e0cefa1 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -141,13 +141,12 @@
}
`),
android.FixtureMergeMockFs(android.MockFS{
- "a.java": nil,
- "PrebuiltAppFoo.apk": nil,
- "PrebuiltAppFooPriv.apk": nil,
- "build/make/target/product/security": nil,
- "apex_manifest.json": nil,
- "AndroidManifest.xml": nil,
- "system/sepolicy/apex/myapex-file_contexts": nil,
+ "a.java": nil,
+ "PrebuiltAppFoo.apk": nil,
+ "PrebuiltAppFooPriv.apk": nil,
+ "apex_manifest.json": nil,
+ "AndroidManifest.xml": nil,
+ "system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/myapex.updatable-file_contexts": nil,
"system/sepolicy/apex/myapex2-file_contexts": nil,
"system/sepolicy/apex/otherapex-file_contexts": nil,
diff --git a/apex/testing.go b/apex/testing.go
index e662cad..926125f 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -19,4 +19,11 @@
var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
android.FixtureRegisterWithContext(registerApexBuildComponents),
android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of an apex module type.
+ android.MockFS{
+ // Needed by apex.
+ "system/core/rootdir/etc/public.libraries.android.txt": nil,
+ "build/soong/scripts/gen_ndk_backedby_apex.sh": nil,
+ }.AddToFixture(),
)
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index c74f902..cc616f2 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -27,6 +27,7 @@
"build_conversion_test.go",
"bzl_conversion_test.go",
"cc_library_headers_conversion_test.go",
+ "cc_library_static_conversion_test.go",
"cc_object_conversion_test.go",
"conversion_test.go",
"python_binary_conversion_test.go",
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 97a5137..007d6d8 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -18,44 +18,48 @@
"android/soong/android"
"fmt"
"os"
+ "strings"
)
-// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
-// Android.bp files that are capable of being built with Bazel.
+// Codegen is the backend of bp2build. The code generator is responsible for
+// writing .bzl files that are equivalent to Android.bp files that are capable
+// of being built with Bazel.
func Codegen(ctx *CodegenContext) CodegenMetrics {
outputDir := android.PathForOutput(ctx, "bp2build")
android.RemoveAllOutputDir(outputDir)
- ruleShims := CreateRuleShims(android.ModuleTypeFactories())
-
buildToTargets, metrics := GenerateBazelTargets(ctx)
- filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode)
+ filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+
+ generatedBuildFiles := []string{}
for _, f := range filesToWrite {
- if err := writeFile(outputDir, ctx, f); err != nil {
+ p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
+ if err := writeFile(ctx, p, f.Contents); err != nil {
fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
}
+ // if these generated files are modified, regenerate on next run.
+ generatedBuildFiles = append(generatedBuildFiles, p.String())
}
+ // The MANIFEST file contains the full list of files generated by bp2build, excluding itself.
+ // Its purpose is for downstream tools to understand the set of files converted by bp2build.
+ manifestFile := outputDir.Join(ctx, "MANIFEST")
+ writeFile(ctx, manifestFile, strings.Join(generatedBuildFiles, "\n"))
+ generatedBuildFiles = append(generatedBuildFiles, manifestFile.String())
+
return metrics
}
-func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
- return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
+// Get the output directory and create it if it doesn't exist.
+func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+ dirPath := outputDir.Join(ctx, dir)
+ android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm)
+ return dirPath
}
-func getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
- return outputDir.Join(ctx, dir)
-}
-
-// The auto-conversion directory should be read-only, sufficient for bazel query. The files
-// are not intended to be edited by end users.
-func writeReadOnlyFile(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
- android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
- pathToFile := dir.Join(ctx, baseName)
-
- // 0444 is read-only
- err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
-
- return err
+func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
+ // These files are made editable to allow users to modify and iterate on them
+ // in the source tree.
+ return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 89acbe9..b9b250a 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -412,7 +412,7 @@
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.RegisterBp2BuildMutator("custom_starlark", customBp2BuildMutatorFromStarlark)
+ ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -1144,7 +1144,7 @@
}
}
-func TestAllowlistingBp2buildTargets(t *testing.T) {
+func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
testCases := []struct {
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
@@ -1222,6 +1222,124 @@
}
}
+func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
+ testCases := []struct {
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator bp2buildMutator
+ expectedCount map[string]int
+ description string
+ bp2buildConfig android.Bp2BuildConfig
+ checkDir string
+ fs map[string]string
+ }{
+ {
+ description: "test bp2build config package and subpackages config",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ expectedCount: map[string]int{
+ "migrated": 1,
+ "migrated/but_not_really": 0,
+ "migrated/but_not_really/but_really": 1,
+ "not_migrated": 0,
+ "also_not_migrated": 0,
+ },
+ bp2buildConfig: android.Bp2BuildConfig{
+ "migrated": android.Bp2BuildDefaultTrueRecursively,
+ "migrated/but_not_really": android.Bp2BuildDefaultFalse,
+ "not_migrated": android.Bp2BuildDefaultFalse,
+ },
+ fs: map[string]string{
+ "migrated/Android.bp": `filegroup { name: "a" }`,
+ "migrated/but_not_really/Android.bp": `filegroup { name: "b" }`,
+ "migrated/but_not_really/but_really/Android.bp": `filegroup { name: "c" }`,
+ "not_migrated/Android.bp": `filegroup { name: "d" }`,
+ "also_not_migrated/Android.bp": `filegroup { name: "e" }`,
+ },
+ },
+ {
+ description: "test bp2build config opt-in and opt-out",
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+ expectedCount: map[string]int{
+ "package-opt-in": 2,
+ "package-opt-in/subpackage": 0,
+ "package-opt-out": 1,
+ "package-opt-out/subpackage": 0,
+ },
+ bp2buildConfig: android.Bp2BuildConfig{
+ "package-opt-in": android.Bp2BuildDefaultFalse,
+ "package-opt-out": android.Bp2BuildDefaultTrueRecursively,
+ },
+ fs: map[string]string{
+ "package-opt-in/Android.bp": `
+filegroup { name: "opt-in-a" }
+filegroup { name: "opt-in-b", bazel_module: { bp2build_available: true } }
+filegroup { name: "opt-in-c", bazel_module: { bp2build_available: true } }
+`,
+
+ "package-opt-in/subpackage/Android.bp": `
+filegroup { name: "opt-in-d" } // parent package not configured to DefaultTrueRecursively
+`,
+
+ "package-opt-out/Android.bp": `
+filegroup { name: "opt-out-a" }
+filegroup { name: "opt-out-b", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-c", bazel_module: { bp2build_available: false } }
+`,
+
+ "package-opt-out/subpackage/Android.bp": `
+filegroup { name: "opt-out-g", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-h", bazel_module: { bp2build_available: false } }
+`,
+ },
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ fs := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.fs {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ fs[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, "", fs)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+ // For each directory, test that the expected number of generated targets is correct.
+ for dir, expectedCount := range testCase.expectedCount {
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount := len(bazelTargets); actualCount != expectedCount {
+ t.Fatalf(
+ "%s: Expected %d bazel target for %s package, got %d",
+ testCase.description,
+ expectedCount,
+ dir,
+ actualCount)
+ }
+
+ }
+ }
+}
+
func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
testCases := []struct {
description string
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 5bf5c80..049f84a 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -123,7 +123,8 @@
name: "foo_headers",
export_include_dirs: ["dir-1", "dir-2"],
header_libs: ["lib-1", "lib-2"],
- export_header_lib_headers: ["lib-1", "lib-2"],
+
+ // TODO: Also support export_header_lib_headers
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`cc_library_headers(
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
new file mode 100644
index 0000000..7bf5fd3
--- /dev/null
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -0,0 +1,307 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/cc"
+ "strings"
+ "testing"
+)
+
+const (
+ // See cc/testing.go for more context
+ soongCcLibraryStaticPreamble = `
+cc_defaults {
+ name: "linux_bionic_supported",
+}
+
+toolchain_library {
+ name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+}
+
+toolchain_library {
+ name: "libatomic",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+}`
+)
+
+func TestCcLibraryStaticLoadStatement(t *testing.T) {
+ testCases := []struct {
+ bazelTargets BazelTargets
+ expectedLoadStatements string
+ }{
+ {
+ bazelTargets: BazelTargets{
+ BazelTarget{
+ name: "cc_library_static_target",
+ ruleClass: "cc_library_static",
+ // NOTE: No bzlLoadLocation for native rules
+ },
+ },
+ expectedLoadStatements: ``,
+ },
+ }
+
+ for _, testCase := range testCases {
+ actual := testCase.bazelTargets.LoadStatements()
+ expected := testCase.expectedLoadStatements
+ if actual != expected {
+ t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+ }
+ }
+
+}
+
+func TestCcLibraryStaticBp2Build(t *testing.T) {
+ testCases := []struct {
+ description string
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+ preArchMutators []android.RegisterMutatorFunc
+ depsMutators []android.RegisterMutatorFunc
+ bp string
+ expectedBazelTargets []string
+ filesystem map[string]string
+ dir string
+ }{
+ {
+ description: "cc_library_static test",
+ moduleTypeUnderTest: "cc_library_static",
+ moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+ filesystem: map[string]string{
+ // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+ "include_dir_1/include_dir_1_a.h": "",
+ "include_dir_1/include_dir_1_b.h": "",
+ "include_dir_2/include_dir_2_a.h": "",
+ "include_dir_2/include_dir_2_b.h": "",
+ // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+ "local_include_dir_1/local_include_dir_1_a.h": "",
+ "local_include_dir_1/local_include_dir_1_b.h": "",
+ "local_include_dir_2/local_include_dir_2_a.h": "",
+ "local_include_dir_2/local_include_dir_2_b.h": "",
+ // NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+ "export_include_dir_1/export_include_dir_1_a.h": "",
+ "export_include_dir_1/export_include_dir_1_b.h": "",
+ "export_include_dir_2/export_include_dir_2_a.h": "",
+ "export_include_dir_2/export_include_dir_2_b.h": "",
+ },
+ bp: soongCcLibraryStaticPreamble + `
+cc_library_headers {
+ name: "header_lib_1",
+ export_include_dirs: ["header_lib_1"],
+}
+
+cc_library_headers {
+ name: "header_lib_2",
+ export_include_dirs: ["header_lib_2"],
+}
+
+cc_library_static {
+ name: "static_lib_1",
+ srcs: ["static_lib_1.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "static_lib_2",
+ srcs: ["static_lib_2.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "whole_static_lib_1",
+ srcs: ["whole_static_lib_1.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "whole_static_lib_2",
+ srcs: ["whole_static_lib_2.cc"],
+ bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+ name: "foo_static",
+ srcs: [
+ "foo_static1.cc",
+ "foo_static2.cc",
+ ],
+ cflags: [
+ "-Dflag1",
+ "-Dflag2"
+ ],
+ static_libs: [
+ "static_lib_1",
+ "static_lib_2"
+ ],
+ whole_static_libs: [
+ "whole_static_lib_1",
+ "whole_static_lib_2"
+ ],
+ include_dirs: [
+ "include_dir_1",
+ "include_dir_2",
+ ],
+ local_include_dirs: [
+ "local_include_dir_1",
+ "local_include_dir_2",
+ ],
+ export_include_dirs: [
+ "export_include_dir_1",
+ "export_include_dir_2"
+ ],
+ header_libs: [
+ "header_lib_1",
+ "header_lib_2"
+ ],
+
+ // TODO: Also support export_header_lib_headers
+
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTargets: []string{`cc_library_static(
+ name = "foo_static",
+ copts = [
+ "-Dflag1",
+ "-Dflag2",
+ ],
+ deps = [
+ ":header_lib_1",
+ ":header_lib_2",
+ ":static_lib_1",
+ ":static_lib_2",
+ ":whole_static_lib_1",
+ ":whole_static_lib_2",
+ ],
+ hdrs = [
+ "export_include_dir_1/export_include_dir_1_a.h",
+ "export_include_dir_1/export_include_dir_1_b.h",
+ "export_include_dir_2/export_include_dir_2_a.h",
+ "export_include_dir_2/export_include_dir_2_b.h",
+ ],
+ includes = [
+ "export_include_dir_1",
+ "export_include_dir_2",
+ "include_dir_1",
+ "include_dir_2",
+ "local_include_dir_1",
+ "local_include_dir_2",
+ ],
+ linkstatic = True,
+ srcs = [
+ "foo_static1.cc",
+ "foo_static2.cc",
+ ],
+)`, `cc_library_static(
+ name = "static_lib_1",
+ linkstatic = True,
+ srcs = [
+ "static_lib_1.cc",
+ ],
+)`, `cc_library_static(
+ name = "static_lib_2",
+ linkstatic = True,
+ srcs = [
+ "static_lib_2.cc",
+ ],
+)`, `cc_library_static(
+ name = "whole_static_lib_1",
+ linkstatic = True,
+ srcs = [
+ "whole_static_lib_1.cc",
+ ],
+)`, `cc_library_static(
+ name = "whole_static_lib_2",
+ linkstatic = True,
+ srcs = [
+ "whole_static_lib_2.cc",
+ ],
+)`},
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ filesystem := make(map[string][]byte)
+ toParse := []string{
+ "Android.bp",
+ }
+ for f, content := range testCase.filesystem {
+ if strings.HasSuffix(f, "Android.bp") {
+ toParse = append(toParse, f)
+ }
+ filesystem[f] = []byte(content)
+ }
+ config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+ ctx := android.NewTestContext(config)
+
+ cc.RegisterCCBuildComponents(ctx)
+ ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+ ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ for _, m := range testCase.depsMutators {
+ ctx.DepsBp2BuildMutators(m)
+ }
+ ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, toParse)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+ _, errs = ctx.ResolveDependencies(config)
+ if Errored(t, testCase.description, errs) {
+ continue
+ }
+
+ checkDir := dir
+ if testCase.dir != "" {
+ checkDir = testCase.dir
+ }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+ if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+ t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+ } else {
+ for i, target := range bazelTargets {
+ if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+ t.Errorf(
+ "%s: Expected generated Bazel target to be '%s', got '%s'",
+ testCase.description,
+ w,
+ g,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 7877bb8..787222d 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -21,14 +21,15 @@
mode CodegenMode) []BazelFile {
files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
- // Write top level files: WORKSPACE and BUILD. These files are empty.
+ // Write top level files: WORKSPACE. These files are empty.
files = append(files, newFile("", "WORKSPACE", ""))
- // Used to denote that the top level directory is a package.
- files = append(files, newFile("", GeneratedBuildFileName, ""))
-
- files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
if mode == QueryView {
+ // Used to denote that the top level directory is a package.
+ files = append(files, newFile("", GeneratedBuildFileName, ""))
+
+ files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
+
// These files are only used for queryview.
files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index ec5f27e..a115ddc 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -89,16 +89,8 @@
expectedFilePaths := []filepath{
{
dir: "",
- basename: "BUILD",
- },
- {
- dir: "",
basename: "WORKSPACE",
},
- {
- dir: bazelRulesSubDir,
- basename: "BUILD",
- },
}
assertFilecountsAreEqual(t, files, expectedFilePaths)
diff --git a/bp2build/testing.go b/bp2build/testing.go
index a15a4a5..ede8044 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -126,7 +126,7 @@
func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
if m, ok := ctx.Module().(*customModule); ok {
- if !m.ConvertWithBp2build() {
+ if !m.ConvertWithBp2build(ctx) {
return
}
@@ -147,7 +147,7 @@
// module to target.
func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
if m, ok := ctx.Module().(*customModule); ok {
- if !m.ConvertWithBp2build() {
+ if !m.ConvertWithBp2build(ctx) {
return
}
diff --git a/cc/Android.bp b/cc/Android.bp
index bdbb3c0..79e92cb 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -10,6 +10,7 @@
"blueprint-pathtools",
"soong",
"soong-android",
+ "soong-bazel",
"soong-cc-config",
"soong-etc",
"soong-genrule",
diff --git a/cc/builder.go b/cc/builder.go
index 273cdd3..4771b89 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -73,7 +73,7 @@
Labels: map[string]string{"type": "link", "tool": "clang"},
ExecStrategy: "${config.RECXXLinksExecStrategy}",
Inputs: []string{"${out}.rsp", "$implicitInputs"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"${out}", "$implicitOutputs"},
ToolchainInputs: []string{"$ldCmd"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
@@ -256,7 +256,7 @@
Labels: map[string]string{"type": "tool", "name": "abi-linker"},
ExecStrategy: "${config.REAbiLinkerExecStrategy}",
Inputs: []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicitInputs"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
ToolchainInputs: []string{"$sAbiLinker"},
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},
diff --git a/cc/cc.go b/cc/cc.go
index cab7459..f843b41 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1363,6 +1363,19 @@
if ver == "apex_inherit" || ver == "" {
ver = ctx.sdkVersion()
}
+ // For crt objects, the meaning of min_sdk_version is very different from other types of
+ // module. For them, min_sdk_version defines the oldest version that the build system will
+ // create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
+ // the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
+ // and min_sdk_version properties of the variants are set to the corresponding version
+ // numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
+ // min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
+ // an old version. Since the variant is for the platform, it's preferred to target the
+ // latest version.
+ if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
+ ver = strconv.Itoa(android.FutureApiLevelInt)
+ }
+
// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
minSdkVersionInt, err2 := strconv.Atoi(ver)
@@ -1833,12 +1846,6 @@
if c.compiler != nil {
deps = c.compiler.compilerDeps(ctx, deps)
}
- // Add the PGO dependency (the clang_rt.profile runtime library), which
- // sometimes depends on symbols from libgcc, before libgcc gets added
- // in linkerDeps().
- if c.pgo != nil {
- deps = c.pgo.deps(ctx, deps)
- }
if c.linker != nil {
deps = c.linker.linkerDeps(ctx, deps)
}
@@ -1933,9 +1940,14 @@
return nil
}
if m.UseSdk() {
+ // Choose the CRT that best satisfies the min_sdk_version requirement of this module
+ minSdkVersion := m.MinSdkVersion()
+ if minSdkVersion == "" || minSdkVersion == "apex_inherit" {
+ minSdkVersion = m.SdkVersion()
+ }
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
- {Mutator: "version", Variation: m.SdkVersion()},
+ {Mutator: "version", Variation: minSdkVersion},
}
}
return []blueprint.Variation{
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7196615..f1efbff 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -166,6 +166,15 @@
recoveryVariant = "android_recovery_arm64_armv8-a_shared"
)
+// Test that the PrepareForTestWithCcDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithCcDefaultModules(t *testing.T) {
+ android.GroupFixturePreparers(
+ PrepareForTestWithCcDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).RunTest(t)
+}
+
func TestFuchsiaDeps(t *testing.T) {
t.Helper()
@@ -3628,6 +3637,71 @@
}
}
+func TestMinSdkVersionInClangTriple(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ min_sdk_version: "29",
+ }`)
+
+ cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
+}
+
+func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
+ ctx := testCc(t, `
+ cc_object {
+ name: "crt_foo",
+ srcs: ["foo.c"],
+ crt: true,
+ stl: "none",
+ min_sdk_version: "28",
+
+ }`)
+
+ arch := "android_arm64_armv8-a"
+ for _, v := range []string{"", "28", "29", "30", "current"} {
+ var variant string
+ if v == "" {
+ variant = arch
+ } else {
+ variant = arch + "_sdk_" + v
+ }
+ cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
+ vNum := v
+ if v == "current" || v == "" {
+ vNum = "10000"
+ }
+ expected := "-target aarch64-linux-android" + vNum + " "
+ android.AssertStringDoesContain(t, "cflag", cflags, expected)
+ }
+}
+
+func TestUseCrtObjectOfCorrectVersion(t *testing.T) {
+ ctx := testCc(t, `
+ cc_binary {
+ name: "bin",
+ srcs: ["foo.c"],
+ stl: "none",
+ min_sdk_version: "29",
+ sdk_version: "current",
+ }
+ `)
+
+ // Sdk variant uses the crt object of the matching min_sdk_version
+ variant := "android_arm64_armv8-a_sdk"
+ crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+ android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
+ variant+"_29/crtbegin_dynamic.o")
+
+ // platform variant uses the crt object built for platform
+ variant = "android_arm64_armv8-a"
+ crt = ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+ android.AssertStringDoesContain(t, "crt dep of platform variant", crt,
+ variant+"/crtbegin_dynamic.o")
+}
+
type MemtagNoteType int
const (
diff --git a/cc/compiler.go b/cc/compiler.go
index b09b58e..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -414,12 +414,7 @@
target := "-target " + tc.ClangTriple()
if ctx.Os().Class == android.Device {
- // When built for the non-updateble part of platform, minSdkVersion doesn't matter.
- // It matters only when building we are building for modules that can be unbundled.
- version := "current"
- if !ctx.isForPlatform() || ctx.isSdkVariant() {
- version = ctx.minSdkVersion()
- }
+ version := ctx.minSdkVersion()
if version == "" || version == "current" {
target += strconv.Itoa(android.FutureApiLevelInt)
} else {
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index d083d2a..864fba1 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -46,7 +46,6 @@
arm64Ldflags = []string{
"-Wl,--hash-style=gnu",
"-Wl,-z,separate-code",
- "-Wl,--icf=safe",
}
arm64Lldflags = append(ClangFilterUnknownLldflags(arm64Ldflags),
@@ -128,10 +127,10 @@
var (
arm64ClangArchVariantCflagsVar = map[string]string{
- "armv8-a": "${config.Arm64ClangArmv8ACflags}",
+ "armv8-a": "${config.Arm64ClangArmv8ACflags}",
"armv8-a-branchprot": "${config.Arm64ClangArmv8ABranchProtCflags}",
- "armv8-2a": "${config.Arm64ClangArmv82ACflags}",
- "armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
+ "armv8-2a": "${config.Arm64ClangArmv82ACflags}",
+ "armv8-2a-dotprod": "${config.Arm64ClangArmv82ADotprodCflags}",
}
arm64ClangCpuVariantCflagsVar = map[string]string{
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index f01c638..a402f8f 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -34,7 +34,6 @@
armCppflags = []string{}
armLdflags = []string{
- "-Wl,--icf=safe",
"-Wl,--hash-style=gnu",
"-Wl,-m,armelf",
}
diff --git a/cc/config/global.go b/cc/config/global.go
index 7e80900..ed18300 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -97,6 +97,7 @@
"-Wl,--exclude-libs,libgcc_stripped.a",
"-Wl,--exclude-libs,libunwind_llvm.a",
"-Wl,--exclude-libs,libunwind.a",
+ "-Wl,--icf=safe",
}
deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 59c0422..fce28c1 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -245,10 +245,6 @@
return LibclangRuntimeLibrary(t, "tsan")
}
-func ProfileRuntimeLibrary(t Toolchain) string {
- return LibclangRuntimeLibrary(t, "profile")
-}
-
func ScudoRuntimeLibrary(t Toolchain) string {
return LibclangRuntimeLibrary(t, "scudo")
}
diff --git a/cc/library.go b/cc/library.go
index 22a36c6..9bec974 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -27,6 +27,7 @@
"github.com/google/blueprint/pathtools"
"android/soong/android"
+ "android/soong/bazel"
"android/soong/cc/config"
)
@@ -200,6 +201,8 @@
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -1914,6 +1917,7 @@
for i, module := range modules {
module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
+ module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
}
}
@@ -2024,3 +2028,137 @@
return outputFile
}
+
+func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelList {
+ var headerLibs []string
+ for _, linkerProps := range module.linker.linkerProps() {
+ if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
+ headerLibs = baseLinkerProps.Header_libs
+ // FIXME: re-export include dirs from baseLinkerProps.Export_header_lib_headers?
+ break
+ }
+ }
+
+ headerLibsLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+ return headerLibsLabels
+}
+
+func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelList, bazel.LabelList) {
+ libraryDecorator := module.linker.(*libraryDecorator)
+
+ includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
+ includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+
+ includeDirsLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
+
+ var includeDirGlobs []string
+ for _, includeDir := range includeDirs {
+ includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
+ includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.inc")
+ includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.hpp")
+ }
+
+ headersLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+
+ return includeDirsLabels, headersLabels
+}
+
+type bazelCcLibraryStaticAttributes struct {
+ Copts []string
+ Srcs bazel.LabelList
+ Deps bazel.LabelList
+ Linkstatic bool
+ Includes bazel.LabelList
+ Hdrs bazel.LabelList
+}
+
+type bazelCcLibraryStatic struct {
+ android.BazelTargetModuleBase
+ bazelCcLibraryStaticAttributes
+}
+
+func BazelCcLibraryStaticFactory() android.Module {
+ module := &bazelCcLibraryStatic{}
+ module.AddProperties(&module.bazelCcLibraryStaticAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ // Not a cc module
+ return
+ }
+ if !module.ConvertWithBp2build(ctx) {
+ return
+ }
+ if ctx.ModuleType() != "cc_library_static" {
+ return
+ }
+
+ var copts []string
+ var srcs []string
+ var includeDirs []string
+ var localIncludeDirs []string
+ for _, props := range module.compiler.compilerProps() {
+ if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+ copts = baseCompilerProps.Cflags
+ srcs = baseCompilerProps.Srcs
+ includeDirs = baseCompilerProps.Include_dirs
+ localIncludeDirs = baseCompilerProps.Local_include_dirs
+ break
+ }
+ }
+ srcsLabels := android.BazelLabelForModuleSrc(ctx, srcs)
+
+ var staticLibs []string
+ var wholeStaticLibs []string
+ for _, props := range module.linker.linkerProps() {
+ if baseLinkerProperties, ok := props.(*BaseLinkerProperties); ok {
+ staticLibs = baseLinkerProperties.Static_libs
+ wholeStaticLibs = baseLinkerProperties.Whole_static_libs
+ break
+ }
+ }
+
+ // FIXME: Treat Static_libs and Whole_static_libs differently?
+ allDeps := staticLibs
+ allDeps = append(allDeps, wholeStaticLibs...)
+
+ depsLabels := android.BazelLabelForModuleDeps(ctx, allDeps)
+
+ // FIXME: Unify absolute vs relative paths
+ // FIXME: Use -I copts instead of setting includes= ?
+ allIncludes := includeDirs
+ allIncludes = append(allIncludes, localIncludeDirs...)
+ includesLabels := android.BazelLabelForModuleSrc(ctx, allIncludes)
+
+ exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
+ includesLabels.Append(exportedIncludesLabels)
+
+ headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
+ depsLabels.Append(headerLibsLabels)
+
+ attrs := &bazelCcLibraryStaticAttributes{
+ Copts: copts,
+ Srcs: bazel.UniqueBazelLabelList(srcsLabels),
+ Deps: bazel.UniqueBazelLabelList(depsLabels),
+ Linkstatic: true,
+ Includes: bazel.UniqueBazelLabelList(includesLabels),
+ Hdrs: bazel.UniqueBazelLabelList(exportedIncludesHeadersLabels),
+ }
+
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "cc_library_static",
+ Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+ }
+
+ ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryStatic) Name() string {
+ return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryStatic) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 115b775..719d538 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -86,7 +86,7 @@
return
}
- if !module.ConvertWithBp2build() {
+ if !module.ConvertWithBp2build(ctx) {
return
}
@@ -94,35 +94,14 @@
return
}
- lib, _ := module.linker.(*libraryDecorator)
+ exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
- // list of directories that will be added to the include path (using -I) for this
- // module and any module that links against this module.
- includeDirs := lib.flagExporter.Properties.Export_system_include_dirs
- includeDirs = append(includeDirs, lib.flagExporter.Properties.Export_include_dirs...)
- includeDirLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
-
- var includeDirGlobs []string
- for _, includeDir := range includeDirs {
- includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
- }
-
- headerLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
-
- // list of modules that should only provide headers for this module.
- var headerLibs []string
- for _, linkerProps := range lib.linkerProps() {
- if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
- headerLibs = baseLinkerProps.Export_header_lib_headers
- break
- }
- }
- headerLibLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+ headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
attrs := &bazelCcLibraryHeadersAttributes{
- Includes: includeDirLabels,
- Hdrs: headerLabels,
- Deps: headerLibLabels,
+ Includes: exportedIncludesLabels,
+ Hdrs: exportedIncludesHeadersLabels,
+ Deps: headerLibsLabels,
}
props := bazel.BazelTargetModuleProperties{
diff --git a/cc/linkable.go b/cc/linkable.go
index 58919a0..6aa238b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -111,6 +111,7 @@
InProduct() bool
SdkVersion() string
+ MinSdkVersion() string
AlwaysSdk() bool
IsSdkVariant() bool
@@ -157,8 +158,16 @@
}
// StaticDepTag returns the dependency tag for any C++ static libraries.
-func StaticDepTag() blueprint.DependencyTag {
- return libraryDependencyTag{Kind: staticLibraryDependency}
+func StaticDepTag(wholeStatic bool) blueprint.DependencyTag {
+ return libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: wholeStatic}
+}
+
+// IsWholeStaticLib whether a dependency tag is a whole static library dependency.
+func IsWholeStaticLib(depTag blueprint.DependencyTag) bool {
+ if tag, ok := depTag.(libraryDependencyTag); ok {
+ return tag.wholeStatic
+ }
+ return false
}
// HeaderDepTag returns the dependency tag for any C++ "header-only" libraries.
diff --git a/cc/object.go b/cc/object.go
index 6bea28b..664be8d 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -131,7 +131,7 @@
// Bazel equivalent target, plus any necessary include deps for the cc_object.
func ObjectBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
diff --git a/cc/pgo.go b/cc/pgo.go
index ada694b..95c9c2e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -22,7 +22,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/cc/config"
)
var (
@@ -271,14 +270,6 @@
}
}
-func (pgo *pgo) deps(ctx BaseModuleContext, deps Deps) Deps {
- if pgo.Properties.ShouldProfileModule {
- runtimeLibrary := config.ProfileRuntimeLibrary(ctx.toolchain())
- deps.LateStaticLibs = append(deps.LateStaticLibs, runtimeLibrary)
- }
- return deps
-}
-
func (pgo *pgo) flags(ctx ModuleContext, flags Flags) Flags {
if ctx.Host() {
return flags
diff --git a/cc/testing.go b/cc/testing.go
index d8adc61..6e35655 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -619,11 +619,26 @@
RegisterVndkLibraryTxtTypes(ctx)
}),
+
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of a cc module type.
+ android.MockFS{
+ // Needed for ndk_prebuilt_(shared|static)_stl.
+ "prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs": nil,
+ }.AddToFixture(),
)
// Preparer that will define default cc modules, e.g. standard prebuilt modules.
var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers(
PrepareForTestWithCcBuildComponents,
+
+ // Additional files needed in tests that disallow non-existent source.
+ android.MockFS{
+ "defaults/cc/common/libc.map.txt": nil,
+ "defaults/cc/common/libdl.map.txt": nil,
+ "defaults/cc/common/libm.map.txt": nil,
+ }.AddToFixture(),
+
// Place the default cc test modules that are common to all platforms in a location that will not
// conflict with default test modules defined by other packages.
android.FixtureAddTextFile(DefaultCcCommonTestModulesDir+"Android.bp", commonDefaultModules()),
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index 930d040..c516f99 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -22,7 +22,7 @@
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
- "soong-zip",
+ "soong-response",
],
srcs: [
"merge_zips.go",
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 274c8ee..712c7fc 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -25,12 +25,14 @@
"os"
"path/filepath"
"sort"
+ "strings"
+
+ "android/soong/response"
"github.com/google/blueprint/pathtools"
"android/soong/jar"
"android/soong/third_party/zip"
- soongZip "android/soong/zip"
)
// Input zip: we can open it, close it, and obtain an array of entries
@@ -690,15 +692,20 @@
inputs := make([]string, 0)
for _, input := range args[1:] {
if input[0] == '@' {
- bytes, err := ioutil.ReadFile(input[1:])
+ f, err := os.Open(strings.TrimPrefix(input[1:], "@"))
if err != nil {
log.Fatal(err)
}
- inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
- continue
+
+ rspInputs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ inputs = append(inputs, rspInputs...)
+ } else {
+ inputs = append(inputs, input)
}
- inputs = append(inputs, input)
- continue
}
log.SetFlags(log.Lshortfile)
diff --git a/cmd/sbox/Android.bp b/cmd/sbox/Android.bp
index d88505f..b8d75ed 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -21,6 +21,7 @@
deps: [
"sbox_proto",
"soong-makedeps",
+ "soong-response",
],
srcs: [
"sbox.go",
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index f47c601..fcc80a9 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -32,6 +32,7 @@
"android/soong/cmd/sbox/sbox_proto"
"android/soong/makedeps"
+ "android/soong/response"
"github.com/golang/protobuf/proto"
)
@@ -218,6 +219,11 @@
return "", fmt.Errorf("command is required")
}
+ pathToTempDirInSbox := tempDir
+ if command.GetChdir() {
+ pathToTempDirInSbox = "."
+ }
+
err = os.MkdirAll(tempDir, 0777)
if err != nil {
return "", fmt.Errorf("failed to create %q: %w", tempDir, err)
@@ -228,10 +234,9 @@
if err != nil {
return "", err
}
-
- pathToTempDirInSbox := tempDir
- if command.GetChdir() {
- pathToTempDirInSbox = "."
+ err = copyRspFiles(command.RspFiles, tempDir, pathToTempDirInSbox)
+ if err != nil {
+ return "", err
}
if strings.Contains(rawCommand, depFilePlaceholder) {
@@ -409,6 +414,83 @@
return nil
}
+// copyRspFiles copies rsp files into the sandbox with path mappings, and also copies the files
+// listed into the sandbox.
+func copyRspFiles(rspFiles []*sbox_proto.RspFile, toDir, toDirInSandbox string) error {
+ for _, rspFile := range rspFiles {
+ err := copyOneRspFile(rspFile, toDir, toDirInSandbox)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// copyOneRspFiles copies an rsp file into the sandbox with path mappings, and also copies the files
+// listed into the sandbox.
+func copyOneRspFile(rspFile *sbox_proto.RspFile, toDir, toDirInSandbox string) error {
+ in, err := os.Open(rspFile.GetFile())
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ files, err := response.ReadRspFile(in)
+ if err != nil {
+ return err
+ }
+
+ for i, from := range files {
+ // Convert the real path of the input file into the path inside the sandbox using the
+ // path mappings.
+ to := applyPathMappings(rspFile.PathMappings, from)
+
+ // Copy the file into the sandbox.
+ err := copyOneFile(from, joinPath(toDir, to), false)
+ if err != nil {
+ return err
+ }
+
+ // Rewrite the name in the list of files to be relative to the sandbox directory.
+ files[i] = joinPath(toDirInSandbox, to)
+ }
+
+ // Convert the real path of the rsp file into the path inside the sandbox using the path
+ // mappings.
+ outRspFile := joinPath(toDir, applyPathMappings(rspFile.PathMappings, rspFile.GetFile()))
+
+ err = os.MkdirAll(filepath.Dir(outRspFile), 0777)
+ if err != nil {
+ return err
+ }
+
+ out, err := os.Create(outRspFile)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ // Write the rsp file with converted paths into the sandbox.
+ err = response.WriteRspFile(out, files)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// applyPathMappings takes a list of path mappings and a path, and returns the path with the first
+// matching path mapping applied. If the path does not match any of the path mappings then it is
+// returned unmodified.
+func applyPathMappings(pathMappings []*sbox_proto.PathMapping, path string) string {
+ for _, mapping := range pathMappings {
+ if strings.HasPrefix(path, mapping.GetFrom()+"/") {
+ return joinPath(mapping.GetTo()+"/", strings.TrimPrefix(path, mapping.GetFrom()+"/"))
+ }
+ }
+ return path
+}
+
// moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted
// to moving files where the source and destination are in the same filesystem. This is OK for
// sbox because the temporary directory is inside the out directory. It updates the timestamp
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
index 79bb90c..b996481 100644
--- a/cmd/sbox/sbox_proto/sbox.pb.go
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -86,10 +86,13 @@
CopyAfter []*Copy `protobuf:"bytes,4,rep,name=copy_after,json=copyAfter" json:"copy_after,omitempty"`
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line.
- InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
+ // A list of files that will be copied before the sandboxed command, and whose contents should be
+ // copied as if they were listed in copy_before.
+ RspFiles []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *Command) Reset() { *m = Command{} }
@@ -152,6 +155,13 @@
return ""
}
+func (m *Command) GetRspFiles() []*RspFile {
+ if m != nil {
+ return m.RspFiles
+ }
+ return nil
+}
+
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
// are relative to is specific to the context the Copy is used in and will be different for
// from and to.
@@ -211,10 +221,110 @@
return false
}
+// RspFile describes an rspfile that should be copied into the sandbox directory.
+type RspFile struct {
+ // The path to the rsp file.
+ File *string `protobuf:"bytes,1,req,name=file" json:"file,omitempty"`
+ // A list of path mappings that should be applied to each file listed in the rsp file.
+ PathMappings []*PathMapping `protobuf:"bytes,2,rep,name=path_mappings,json=pathMappings" json:"path_mappings,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *RspFile) Reset() { *m = RspFile{} }
+func (m *RspFile) String() string { return proto.CompactTextString(m) }
+func (*RspFile) ProtoMessage() {}
+func (*RspFile) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{3}
+}
+
+func (m *RspFile) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_RspFile.Unmarshal(m, b)
+}
+func (m *RspFile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_RspFile.Marshal(b, m, deterministic)
+}
+func (m *RspFile) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_RspFile.Merge(m, src)
+}
+func (m *RspFile) XXX_Size() int {
+ return xxx_messageInfo_RspFile.Size(m)
+}
+func (m *RspFile) XXX_DiscardUnknown() {
+ xxx_messageInfo_RspFile.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RspFile proto.InternalMessageInfo
+
+func (m *RspFile) GetFile() string {
+ if m != nil && m.File != nil {
+ return *m.File
+ }
+ return ""
+}
+
+func (m *RspFile) GetPathMappings() []*PathMapping {
+ if m != nil {
+ return m.PathMappings
+ }
+ return nil
+}
+
+// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
+type PathMapping struct {
+ From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
+ To *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *PathMapping) Reset() { *m = PathMapping{} }
+func (m *PathMapping) String() string { return proto.CompactTextString(m) }
+func (*PathMapping) ProtoMessage() {}
+func (*PathMapping) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{4}
+}
+
+func (m *PathMapping) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_PathMapping.Unmarshal(m, b)
+}
+func (m *PathMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_PathMapping.Marshal(b, m, deterministic)
+}
+func (m *PathMapping) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_PathMapping.Merge(m, src)
+}
+func (m *PathMapping) XXX_Size() int {
+ return xxx_messageInfo_PathMapping.Size(m)
+}
+func (m *PathMapping) XXX_DiscardUnknown() {
+ xxx_messageInfo_PathMapping.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PathMapping proto.InternalMessageInfo
+
+func (m *PathMapping) GetFrom() string {
+ if m != nil && m.From != nil {
+ return *m.From
+ }
+ return ""
+}
+
+func (m *PathMapping) GetTo() string {
+ if m != nil && m.To != nil {
+ return *m.To
+ }
+ return ""
+}
+
func init() {
proto.RegisterType((*Manifest)(nil), "sbox.Manifest")
proto.RegisterType((*Command)(nil), "sbox.Command")
proto.RegisterType((*Copy)(nil), "sbox.Copy")
+ proto.RegisterType((*RspFile)(nil), "sbox.RspFile")
+ proto.RegisterType((*PathMapping)(nil), "sbox.PathMapping")
}
func init() {
@@ -222,22 +332,27 @@
}
var fileDescriptor_9d0425bf0de86ed1 = []byte{
- // 268 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x4f, 0x4b, 0xc3, 0x40,
- 0x10, 0xc5, 0xc9, 0x9f, 0xd2, 0x64, 0x6a, 0x7b, 0x18, 0x3c, 0xec, 0x45, 0x09, 0x01, 0x21, 0x45,
- 0xe8, 0xc1, 0x6f, 0x60, 0xf5, 0x20, 0x82, 0x97, 0x1c, 0x45, 0x08, 0x9b, 0x64, 0x43, 0x02, 0x4d,
- 0x26, 0xec, 0x6e, 0xa0, 0xfd, 0x56, 0x7e, 0x44, 0xd9, 0x49, 0x2a, 0x82, 0xb7, 0x99, 0xdf, 0xe3,
- 0xcd, 0x7b, 0x0c, 0x80, 0x29, 0xe9, 0x7c, 0x18, 0x35, 0x59, 0xc2, 0xd0, 0xcd, 0xe9, 0x17, 0x44,
- 0x1f, 0x72, 0xe8, 0x1a, 0x65, 0x2c, 0xee, 0x21, 0xaa, 0xa8, 0xef, 0xe5, 0x50, 0x1b, 0xe1, 0x25,
- 0x41, 0xb6, 0x79, 0xda, 0x1e, 0xd8, 0xf0, 0x32, 0xd3, 0xfc, 0x57, 0xc6, 0x07, 0xd8, 0xd1, 0x64,
- 0xc7, 0xc9, 0x16, 0xb5, 0x1a, 0x9b, 0xee, 0xa4, 0x84, 0x9f, 0x78, 0x59, 0x9c, 0x6f, 0x67, 0xfa,
- 0x3a, 0xc3, 0xf4, 0xdb, 0x83, 0xf5, 0x62, 0xc6, 0x47, 0xd8, 0x54, 0x34, 0x5e, 0x8a, 0x52, 0x35,
- 0xa4, 0xd5, 0x12, 0x00, 0xd7, 0x80, 0xf1, 0x92, 0x83, 0x93, 0x8f, 0xac, 0xe2, 0x2d, 0xac, 0xaa,
- 0xb6, 0xee, 0x34, 0x9f, 0x8d, 0xf2, 0x79, 0x41, 0x01, 0xeb, 0xa5, 0x81, 0x08, 0x12, 0x3f, 0x8b,
- 0xf3, 0xeb, 0x8a, 0x7b, 0x60, 0x77, 0x21, 0x1b, 0xab, 0xb4, 0x08, 0xff, 0xdd, 0x8e, 0x9d, 0xfa,
- 0xec, 0x44, 0xbc, 0x03, 0xe8, 0x06, 0xd7, 0xbc, 0x95, 0xa6, 0x15, 0x2b, 0xae, 0x1d, 0x33, 0x79,
- 0x93, 0xa6, 0x4d, 0xdf, 0x21, 0x74, 0x0e, 0x44, 0x08, 0x1b, 0x4d, 0xbd, 0xf0, 0x38, 0x88, 0x67,
- 0xdc, 0x81, 0x6f, 0x49, 0xf8, 0x4c, 0x7c, 0x4b, 0x78, 0x0f, 0xa0, 0xce, 0xaa, 0x9a, 0xac, 0x2c,
- 0x4f, 0x4a, 0x04, 0x5c, 0xf5, 0x0f, 0x39, 0xde, 0x7c, 0xf2, 0xc3, 0x0b, 0x7e, 0xf8, 0x4f, 0x00,
- 0x00, 0x00, 0xff, 0xff, 0x78, 0x37, 0x3e, 0x6a, 0x7d, 0x01, 0x00, 0x00,
+ // 342 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x5f, 0x4b, 0xc3, 0x30,
+ 0x14, 0xc5, 0x69, 0xd7, 0xb9, 0xf6, 0xee, 0x0f, 0x18, 0x7c, 0xc8, 0x8b, 0x32, 0x0a, 0xc2, 0xa6,
+ 0x30, 0xd0, 0x07, 0xdf, 0x9d, 0x22, 0x22, 0x0c, 0x24, 0xe0, 0x8b, 0x08, 0x25, 0xeb, 0x52, 0x5b,
+ 0x58, 0x9b, 0x90, 0x64, 0xb0, 0x7d, 0x57, 0x3f, 0x8c, 0xe4, 0xa6, 0xd3, 0x82, 0x2f, 0xbe, 0xdd,
+ 0x7b, 0x0e, 0xf7, 0xdc, 0x5f, 0xc2, 0x05, 0x30, 0x6b, 0xb9, 0x5f, 0x28, 0x2d, 0xad, 0x24, 0x91,
+ 0xab, 0xd3, 0x0f, 0x88, 0x57, 0xbc, 0xa9, 0x0a, 0x61, 0x2c, 0x99, 0x43, 0x9c, 0xcb, 0xba, 0xe6,
+ 0xcd, 0xc6, 0xd0, 0x60, 0xda, 0x9b, 0x0d, 0x6f, 0xc7, 0x0b, 0x1c, 0x78, 0xf0, 0x2a, 0xfb, 0xb1,
+ 0xc9, 0x25, 0x4c, 0xe4, 0xce, 0xaa, 0x9d, 0xcd, 0x36, 0x42, 0x15, 0xd5, 0x56, 0xd0, 0x70, 0x1a,
+ 0xcc, 0x12, 0x36, 0xf6, 0xea, 0xa3, 0x17, 0xd3, 0xaf, 0x00, 0x06, 0xed, 0x30, 0xb9, 0x86, 0x61,
+ 0x2e, 0xd5, 0x21, 0x5b, 0x8b, 0x42, 0x6a, 0xd1, 0x2e, 0x80, 0xe3, 0x02, 0x75, 0x60, 0xe0, 0xec,
+ 0x25, 0xba, 0xe4, 0x0c, 0xfa, 0x79, 0xb9, 0xa9, 0x34, 0xc6, 0xc6, 0xcc, 0x37, 0x84, 0xc2, 0xa0,
+ 0x25, 0xa0, 0xbd, 0x69, 0x38, 0x4b, 0xd8, 0xb1, 0x25, 0x73, 0xc0, 0xe9, 0x8c, 0x17, 0x56, 0x68,
+ 0x1a, 0xfd, 0xc9, 0x4e, 0x9c, 0x7b, 0xef, 0x4c, 0x72, 0x0e, 0x50, 0x35, 0x8e, 0xbc, 0xe4, 0xa6,
+ 0xa4, 0x7d, 0xc4, 0x4e, 0x50, 0x79, 0xe6, 0xa6, 0x24, 0x57, 0x90, 0x68, 0xa3, 0x32, 0x87, 0x6f,
+ 0xe8, 0x49, 0xf7, 0x17, 0x98, 0x51, 0x4f, 0xd5, 0x56, 0xb0, 0x58, 0xfb, 0xc2, 0xa4, 0x2f, 0x10,
+ 0xb9, 0x74, 0x42, 0x20, 0x2a, 0xb4, 0xac, 0x69, 0x80, 0x50, 0x58, 0x93, 0x09, 0x84, 0x56, 0xd2,
+ 0x10, 0x95, 0xd0, 0x4a, 0x72, 0x01, 0x20, 0xf6, 0x22, 0xdf, 0x59, 0xbe, 0xde, 0x0a, 0xda, 0xc3,
+ 0x67, 0x75, 0x94, 0xf4, 0x0d, 0x06, 0xed, 0x02, 0x8c, 0x73, 0x5f, 0x7a, 0x8c, 0x73, 0xda, 0x1d,
+ 0x8c, 0x15, 0xb7, 0x65, 0x56, 0x73, 0xa5, 0xaa, 0xe6, 0xd3, 0xd0, 0x10, 0xd1, 0x4e, 0x3d, 0xda,
+ 0x2b, 0xb7, 0xe5, 0xca, 0x3b, 0x6c, 0xa4, 0x7e, 0x1b, 0x93, 0xde, 0xc0, 0xb0, 0x63, 0xfe, 0x87,
+ 0x74, 0x39, 0x7a, 0xc7, 0x33, 0xc9, 0xf0, 0x4c, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x82,
+ 0xb0, 0xc3, 0x33, 0x02, 0x00, 0x00,
}
diff --git a/cmd/sbox/sbox_proto/sbox.proto b/cmd/sbox/sbox_proto/sbox.proto
index 695b0e8..bdf92c6 100644
--- a/cmd/sbox/sbox_proto/sbox.proto
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -47,6 +47,10 @@
// An optional hash of the input files to ensure the textproto files and the sbox rule reruns
// when the lists of inputs changes, even if the inputs are not on the command line.
optional string input_hash = 5;
+
+ // A list of files that will be copied before the sandboxed command, and whose contents should be
+ // copied as if they were listed in copy_before.
+ repeated RspFile rsp_files = 6;
}
// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
@@ -58,4 +62,19 @@
// If true, make the file executable after copying it.
optional bool executable = 3;
-}
\ No newline at end of file
+}
+
+// RspFile describes an rspfile that should be copied into the sandbox directory.
+message RspFile {
+ // The path to the rsp file.
+ required string file = 1;
+
+ // A list of path mappings that should be applied to each file listed in the rsp file.
+ repeated PathMapping path_mappings = 2;
+}
+
+// PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
+message PathMapping {
+ required string from = 1;
+ required string to = 2;
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 94efa4d..e2fc78c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -21,8 +21,10 @@
"os"
"path/filepath"
"strings"
+ "time"
"android/soong/shared"
+
"github.com/google/blueprint/bootstrap"
"android/soong/android"
@@ -191,13 +193,24 @@
func writeUsedVariablesFile(path string, configuration android.Config) {
data, err := shared.EnvFileContents(configuration.EnvDeps())
if err != nil {
- fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+ fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
os.Exit(1)
}
err = ioutil.WriteFile(path, data, 0666)
if err != nil {
- fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+ fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
+ os.Exit(1)
+ }
+
+ // Touch the output Ninja file so that it's not older than the file we just
+ // wrote. We can't write the environment file earlier because one an access
+ // new environment variables while writing it.
+ outputNinjaFile := shared.JoinPath(topDir, bootstrap.CmdlineOutFile())
+ currentTime := time.Now().Local()
+ err = os.Chtimes(outputNinjaFile, currentTime, currentTime)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error touching output file %s: %s\n", outputNinjaFile, err)
os.Exit(1)
}
}
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 888466a..02f1120 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -33,6 +33,8 @@
OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
+ PreoptWithUpdatableBcp bool // If updatable boot jars are included in dexpreopt or not.
+
UseArtImage bool // use the art image (use other boot class path dex files without image)
HasSystemOther bool // store odex files that match PatternsOnSystemOther on the system_other partition
@@ -352,9 +354,23 @@
}
}
-var Dex2oatDepTag = struct {
+type dex2oatDependencyTag struct {
blueprint.BaseDependencyTag
-}{}
+}
+
+func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+func (d dex2oatDependencyTag) ExcludeFromApexContents() {
+}
+
+// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
+// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
+// the apex.
+var Dex2oatDepTag = dex2oatDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag
+var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag
// RegisterToolDeps adds the necessary dependencies to binary modules for tools
// that are required later when Get(Cached)GlobalSoongConfig is called. It
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 4801482..c0ba5ca 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -117,3 +117,17 @@
dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars)
})
}
+
+// FixtureSetUpdatableBootJars sets the UpdatableBootJars property in the global config.
+func FixtureSetUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+ })
+}
+
+// FixtureSetPreoptWithUpdatableBcp sets the PreoptWithUpdatableBcp property in the global config.
+func FixtureSetPreoptWithUpdatableBcp(value bool) android.FixturePreparer {
+ return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+ dexpreoptConfig.PreoptWithUpdatableBcp = value
+ })
+}
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 214940d..8fb5c40 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -16,6 +16,7 @@
],
srcs: [
"genrule.go",
+ "locations.go",
],
testSrcs: [
"genrule_test.go",
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b43f28e..9019a83 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -91,7 +91,6 @@
func init() {
pctx.Import("android/soong/android")
- pctx.HostBinToolVariable("sboxCmd", "sbox")
pctx.HostBinToolVariable("soongZip", "soong_zip")
pctx.HostBinToolVariable("zipSync", "zipsync")
@@ -254,18 +253,18 @@
g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
}
- locationLabels := map[string][]string{}
+ locationLabels := map[string]location{}
firstLabel := ""
- addLocationLabel := func(label string, paths []string) {
+ addLocationLabel := func(label string, loc location) {
if firstLabel == "" {
firstLabel = label
}
if _, exists := locationLabels[label]; !exists {
- locationLabels[label] = paths
+ locationLabels[label] = loc
} else {
ctx.ModuleErrorf("multiple labels for %q, %q and %q",
- label, strings.Join(locationLabels[label], " "), strings.Join(paths, " "))
+ label, locationLabels[label], loc)
}
}
@@ -303,17 +302,17 @@
// sandbox.
packagedTools = append(packagedTools, specs...)
// Assume that the first PackagingSpec of the module is the tool.
- addLocationLabel(tag.label, []string{android.SboxPathForPackagedTool(specs[0])})
+ addLocationLabel(tag.label, packagedToolLocation{specs[0]})
} else {
tools = append(tools, path.Path())
- addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, path.Path())})
+ addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
}
case bootstrap.GoBinaryTool:
// A GoBinaryTool provides the install path to a tool, which will be copied.
if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
toolPath := android.PathForOutput(ctx, s)
tools = append(tools, toolPath)
- addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, toolPath)})
+ addLocationLabel(tag.label, toolLocation{android.Paths{toolPath}})
} else {
ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
return
@@ -335,7 +334,7 @@
if ctx.Config().AllowMissingDependencies() {
for _, tool := range g.properties.Tools {
if !seenTools[tool] {
- addLocationLabel(tool, []string{"***missing tool " + tool + "***"})
+ addLocationLabel(tool, errorLocation{"***missing tool " + tool + "***"})
}
}
}
@@ -348,11 +347,7 @@
for _, toolFile := range g.properties.Tool_files {
paths := android.PathsForModuleSrc(ctx, []string{toolFile})
tools = append(tools, paths...)
- var sandboxPaths []string
- for _, path := range paths {
- sandboxPaths = append(sandboxPaths, android.SboxPathForTool(ctx, path))
- }
- addLocationLabel(toolFile, sandboxPaths)
+ addLocationLabel(toolFile, toolLocation{paths})
}
var srcFiles android.Paths
@@ -370,10 +365,10 @@
// The command that uses this placeholder file will never be executed because the rule will be
// replaced with an android.Error rule reporting the missing dependencies.
ctx.AddMissingDependencies(missingDeps)
- addLocationLabel(in, []string{"***missing srcs " + in + "***"})
+ addLocationLabel(in, errorLocation{"***missing srcs " + in + "***"})
} else {
srcFiles = append(srcFiles, paths...)
- addLocationLabel(in, paths.Strings())
+ addLocationLabel(in, inputLocation{paths})
}
}
@@ -408,7 +403,7 @@
cmd := rule.Command()
for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{cmd.PathForOutput(out)})
+ addLocationLabel(out.Rel(), outputLocation{out})
}
referencedDepfile := false
@@ -426,16 +421,17 @@
if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
}
- paths := locationLabels[firstLabel]
+ loc := locationLabels[firstLabel]
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("default label %q has no files", firstLabel)
} else if len(paths) > 1 {
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], nil
+ return paths[0], nil
case "in":
- return strings.Join(srcFiles.Strings(), " "), nil
+ return strings.Join(cmd.PathsForInputs(srcFiles), " "), nil
case "out":
var sandboxOuts []string
for _, out := range task.out {
@@ -453,7 +449,8 @@
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if paths, ok := locationLabels[label]; ok {
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("label %q has no files", label)
} else if len(paths) > 1 {
@@ -466,7 +463,8 @@
}
} else if strings.HasPrefix(name, "locations ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
- if paths, ok := locationLabels[label]; ok {
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
@@ -719,7 +717,7 @@
outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
- android.SboxPathForTool(ctx, depFixerTool),
+ rule.Command().PathForTool(depFixerTool),
strings.Join(commandDepFiles, " "))
extraTools = append(extraTools, depFixerTool)
}
@@ -820,7 +818,7 @@
func GenruleBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
diff --git a/genrule/locations.go b/genrule/locations.go
new file mode 100644
index 0000000..2978b91
--- /dev/null
+++ b/genrule/locations.go
@@ -0,0 +1,104 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package genrule
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+// location is used to service $(location) and $(locations) entries in genrule commands.
+type location interface {
+ Paths(cmd *android.RuleBuilderCommand) []string
+ String() string
+}
+
+// inputLocation is a $(location) result for an entry in the srcs property.
+type inputLocation struct {
+ paths android.Paths
+}
+
+func (l inputLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l inputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForInputs(l.paths)
+}
+
+var _ location = inputLocation{}
+
+// outputLocation is a $(location) result for an entry in the out property.
+type outputLocation struct {
+ path android.WritablePath
+}
+
+func (l outputLocation) String() string {
+ return l.path.String()
+}
+
+func (l outputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForOutput(l.path)}
+}
+
+var _ location = outputLocation{}
+
+// toolLocation is a $(location) result for an entry in the tools or tool_files property.
+type toolLocation struct {
+ paths android.Paths
+}
+
+func (l toolLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l toolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForTools(l.paths)
+}
+
+var _ location = toolLocation{}
+
+// packagedToolLocation is a $(location) result for an entry in the tools or tool_files property
+// that has PackagingSpecs.
+type packagedToolLocation struct {
+ spec android.PackagingSpec
+}
+
+func (l packagedToolLocation) String() string {
+ return l.spec.FileName()
+}
+
+func (l packagedToolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForPackagedTool(l.spec)}
+}
+
+var _ location = packagedToolLocation{}
+
+// errorLocation is a placeholder for a $(location) result that returns garbage to break the command
+// when error reporting is delayed by ALLOW_MISSING_DEPENDENCIES=true.
+type errorLocation struct {
+ err string
+}
+
+func (l errorLocation) String() string {
+ return l.err
+}
+
+func (l errorLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{l.err}
+}
+
+var _ location = errorLocation{}
diff --git a/java/Android.bp b/java/Android.bp
index c67379c..b6c14ac 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -38,6 +38,7 @@
"dexpreopt_bootjars.go",
"dexpreopt_config.go",
"droiddoc.go",
+ "droidstubs.go",
"gen.go",
"genrule.go",
"hiddenapi.go",
@@ -72,6 +73,8 @@
"device_host_converter_test.go",
"dexpreopt_test.go",
"dexpreopt_bootjars_test.go",
+ "droiddoc_test.go",
+ "droidstubs_test.go",
"hiddenapi_singleton_test.go",
"java_test.go",
"jdeps_test.go",
diff --git a/java/app_test.go b/java/app_test.go
index 2523533..825ad20 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -26,6 +26,7 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/dexpreopt"
"android/soong/genrule"
)
@@ -2422,6 +2423,66 @@
`#PCL[/system/framework/android.test.mock.jar] `)
}
+func TestDexpreoptBcp(t *testing.T) {
+ bp := `
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ api_packages: ["foo"],
+ sdk_version: "current",
+ }
+
+ java_sdk_library {
+ name: "bar",
+ srcs: ["a.java"],
+ api_packages: ["bar"],
+ permitted_packages: ["bar"],
+ sdk_version: "current",
+ }
+
+ android_app {
+ name: "app",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ }
+ `
+
+ testCases := []struct {
+ name string
+ with bool
+ expect string
+ }{
+ {
+ name: "with updatable bcp",
+ with: true,
+ expect: "/system/framework/foo.jar:/system/framework/bar.jar",
+ },
+ {
+ name: "without updatable bcp",
+ with: false,
+ expect: "/system/framework/foo.jar",
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("runtime-library", "foo", "bar"),
+ dexpreopt.FixtureSetBootJars("platform:foo"),
+ dexpreopt.FixtureSetUpdatableBootJars("platform:bar"),
+ dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
+ ).RunTestWithBp(t, bp)
+
+ app := result.ModuleForTests("app", "android_common")
+ cmd := app.Rule("dexpreopt").RuleParams.Command
+ bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters
+ android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp)
+ })
+ }
+}
+
func TestCodelessApp(t *testing.T) {
testCases := []struct {
name string
diff --git a/java/boot_image.go b/java/boot_image.go
index 25a4f17..a14940d 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -96,10 +96,6 @@
func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
tag := ctx.OtherModuleDependencyTag(dep)
- if tag == dexpreopt.Dex2oatDepTag {
- // The dex2oat tool is only needed for building and is not required in the apex.
- return false
- }
if android.IsMetaDependencyTag(tag) {
// Cross-cutting metadata dependencies are metadata.
return false
diff --git a/java/builder.go b/java/builder.go
index fc740a8..cde8731 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -150,7 +150,7 @@
&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
ExecStrategy: "${config.RETurbineExecStrategy}",
Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out.tmp"},
OutputDirectories: []string{"$outDir"},
ToolchainInputs: []string{"${config.JavaCmd}"},
@@ -167,7 +167,7 @@
&remoteexec.REParams{
ExecStrategy: "${config.REJarExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, nil)
@@ -182,7 +182,7 @@
&remoteexec.REParams{
ExecStrategy: "${config.REZipExecStrategy}",
Inputs: []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
- RSPFile: "${out}.rsp",
+ RSPFiles: []string{"${out}.rsp"},
OutputFiles: []string{"$out"},
Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
}, []string{"jarArgs"}, []string{"implicits"})
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index a2961c2..b4cf012 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -160,14 +160,17 @@
globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
global := dexpreopt.GetGlobalConfig(ctx)
+
+ isSystemServerJar := inList(ctx.ModuleName(), global.SystemServerJars)
+
bootImage := defaultBootImageConfig(ctx)
- dexFiles := bootImage.dexPathsDeps.Paths()
- // The dex locations for all Android variants are identical.
- dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
if global.UseArtImage {
bootImage = artBootImageConfig(ctx)
}
+ // System server jars are an exception: they are dexpreopted without updatable bootclasspath.
+ dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp && !isSystemServerJar)
+
targets := ctx.MultiTargets()
if len(targets) == 0 {
// assume this is a java library, dexpreopt for all arches for now
@@ -176,7 +179,7 @@
targets = append(targets, target)
}
}
- if inList(ctx.ModuleName(), global.SystemServerJars) && !d.isSDKLibrary {
+ if isSystemServerJar && !d.isSDKLibrary {
// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.
targets = targets[:1]
}
@@ -237,7 +240,7 @@
DexPreoptImagesDeps: imagesDeps,
DexPreoptImageLocations: imageLocations,
- PreoptBootClassPathDexFiles: dexFiles,
+ PreoptBootClassPathDexFiles: dexFiles.Paths(),
PreoptBootClassPathDexLocations: dexLocations,
PreoptExtractedApk: false,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 17499ee..7137f33 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -214,7 +214,7 @@
var artApexNames = []string{
"com.android.art",
"com.android.art.debug",
- "com.android.art,testing",
+ "com.android.art.testing",
"com.google.android.art",
"com.google.android.art.debug",
"com.google.android.art.testing",
@@ -439,6 +439,8 @@
// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx)))
+ copyUpdatableBootJars(ctx)
+
dumpOatRules(ctx, d.defaultBootImage)
}
@@ -630,6 +632,21 @@
return image
}
+// Generate commands that will copy updatable boot jars to predefined paths in the global config.
+func copyUpdatableBootJars(ctx android.SingletonContext) {
+ config := GetUpdatableBootConfig(ctx)
+ getBootJarFunc := func(module android.Module) (int, android.Path) {
+ index, jar, _ := getBootJar(ctx, config.modules, module, "configured in updatable boot jars ")
+ return index, jar
+ }
+ missingDeps := findAndCopyBootJars(ctx, config.modules, config.dexPaths, getBootJarFunc)
+ // Ignoring missing dependencies here. Ideally they should be added to the dexpreopt rule, but
+ // that is not possible as this rule is created after dexpreopt rules (it's in a singleton
+ // context, and they are in a module context). The true fix is to add dependencies from the
+ // dexpreopted modules on updatable boot jars and avoid this copying altogether.
+ _ = missingDeps
+}
+
// Generate boot image build rules for a specific target.
func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant,
profile android.Path, missingDeps []string) android.WritablePaths {
@@ -997,8 +1014,11 @@
image := d.defaultBootImage
if image != nil {
ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
- ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPathsDeps.Strings(), " "))
- ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.getAnyAndroidVariant().dexLocationsDeps, " "))
+
+ global := dexpreopt.GetGlobalConfig(ctx)
+ dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
+ ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(dexLocations, " "))
var imageNames []string
// TODO: the primary ART boot image should not be exposed to Make, as it is installed in a
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 282e936..64b2656 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -176,6 +176,57 @@
})
}
+// Updatable boot config allows to access build/install paths of updatable boot jars without going
+// through the usual trouble of registering dependencies on those modules and extracting build paths
+// from those dependencies.
+type updatableBootConfig struct {
+ // A list of updatable boot jars.
+ modules android.ConfiguredJarList
+
+ // A list of predefined build paths to updatable boot jars. They are configured very early,
+ // before the modules for these jars are processed and the actual paths are generated, and
+ // later on a singleton adds commands to copy actual jars to the predefined paths.
+ dexPaths android.WritablePaths
+
+ // A list of dex locations (a.k.a. on-device paths) to the boot jars.
+ dexLocations []string
+}
+
+var updatableBootConfigKey = android.NewOnceKey("updatableBootConfig")
+
+// Returns updatable boot config.
+func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig {
+ return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
+ updatableBootJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+
+ dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
+ dexPaths := updatableBootJars.BuildPaths(ctx, dir)
+
+ dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
+
+ return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
+ }).(updatableBootConfig)
+}
+
+// Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be
+// passed in -Xbootclasspath and -Xbootclasspath-locations arguments for dex2oat).
+func bcpForDexpreopt(ctx android.PathContext, withUpdatable bool) (android.WritablePaths, []string) {
+ // Non-updatable boot jars (they are used both in the boot image and in dexpreopt).
+ bootImage := defaultBootImageConfig(ctx)
+ dexPaths := bootImage.dexPathsDeps
+ // The dex locations for all Android variants are identical.
+ dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
+
+ if withUpdatable {
+ // Updatable boot jars (they are used only in dexpreopt, but not in the boot image).
+ updBootConfig := GetUpdatableBootConfig(ctx)
+ dexPaths = append(dexPaths, updBootConfig.dexPaths...)
+ dexLocations = append(dexLocations, updBootConfig.dexLocations...)
+ }
+
+ return dexPaths, dexLocations
+}
+
var defaultBootclasspathKey = android.NewOnceKey("defaultBootclasspath")
var copyOf = android.CopyOf
diff --git a/java/droiddoc.go b/java/droiddoc.go
index a892b36..a8e2b0e 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -23,12 +23,10 @@
"android/soong/android"
"android/soong/java/config"
- "android/soong/remoteexec"
)
func init() {
RegisterDocsBuildComponents(android.InitRegistrationContext)
- RegisterStubsBuildComponents(android.InitRegistrationContext)
}
func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
@@ -41,19 +39,6 @@
ctx.RegisterModuleType("javadoc_host", JavadocHostFactory)
}
-func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
-
- ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
- ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
-
- ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
-}
-
-var (
- srcsLibTag = dependencyTag{name: "sources from javalib"}
-)
-
type JavadocProperties struct {
// list of source files used to compile the Java module. May be .java, .logtags, .proto,
// or .aidl files.
@@ -177,80 +162,6 @@
Compat_config *string `android:"path"`
}
-type DroidstubsProperties struct {
- // The generated public API filename by Metalava, defaults to <module>_api.txt
- Api_filename *string
-
- // the generated removed API filename by Metalava, defaults to <module>_removed.txt
- Removed_api_filename *string
-
- // the generated removed Dex API filename by Metalava.
- Removed_dex_api_filename *string
-
- Check_api struct {
- Last_released ApiToCheck
-
- Current ApiToCheck
-
- Api_lint struct {
- Enabled *bool
-
- // If set, performs api_lint on any new APIs not found in the given signature file
- New_since *string `android:"path"`
-
- // If not blank, path to the baseline txt file for approved API lint violations.
- Baseline_file *string `android:"path"`
- }
- }
-
- // user can specify the version of previous released API file in order to do compatibility check.
- Previous_api *string `android:"path"`
-
- // is set to true, Metalava will allow framework SDK to contain annotations.
- Annotations_enabled *bool
-
- // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
- Merge_annotations_dirs []string
-
- // a list of top-level directories containing Java stub files to merge show/hide annotations from.
- Merge_inclusion_annotations_dirs []string
-
- // a file containing a list of classes to do nullability validation for.
- Validate_nullability_from_list *string
-
- // a file containing expected warnings produced by validation of nullability annotations.
- Check_nullability_warnings *string
-
- // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
- Create_doc_stubs *bool
-
- // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
- // Has no effect if create_doc_stubs: true.
- Output_javadoc_comments *bool
-
- // if set to false then do not write out stubs. Defaults to true.
- //
- // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
- Generate_stubs *bool
-
- // if set to true, provides a hint to the build system that this rule uses a lot of memory,
- // whicih can be used for scheduling purposes
- High_mem *bool
-
- // is set to true, Metalava will allow framework SDK to contain API levels annotations.
- Api_levels_annotations_enabled *bool
-
- // the dirs which Metalava extracts API levels annotations from.
- Api_levels_annotations_dirs []string
-
- // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
- Api_levels_jar_filename *string
-
- // if set to true, collect the values used by the Dev tools and
- // write them in files packaged with the SDK. Defaults to false.
- Write_sdk_values *bool
-}
-
//
// Common flags passed down to build rule
//
@@ -947,669 +858,9 @@
}
//
-// Droidstubs
-//
-type Droidstubs struct {
- Javadoc
- android.SdkBase
-
- properties DroidstubsProperties
- apiFile android.WritablePath
- apiXmlFile android.WritablePath
- lastReleasedApiXmlFile android.WritablePath
- privateApiFile android.WritablePath
- removedApiFile android.WritablePath
- removedDexApiFile android.WritablePath
- nullabilityWarningsFile android.WritablePath
-
- checkCurrentApiTimestamp android.WritablePath
- updateCurrentApiTimestamp android.WritablePath
- checkLastReleasedApiTimestamp android.WritablePath
- apiLintTimestamp android.WritablePath
- apiLintReport android.WritablePath
-
- checkNullabilityWarningsTimestamp android.WritablePath
-
- annotationsZip android.WritablePath
- apiVersionsXml android.WritablePath
-
- apiFilePath android.Path
- removedApiFilePath android.Path
-
- metadataZip android.WritablePath
- metadataDir android.WritablePath
-}
-
-// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
-// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
-// a droiddoc module to generate documentation.
-func DroidstubsFactory() android.Module {
- module := &Droidstubs{}
-
- module.AddProperties(&module.properties,
- &module.Javadoc.properties)
-
- InitDroiddocModule(module, android.HostAndDeviceSupported)
- android.InitSdkAwareModule(module)
- return module
-}
-
-// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
-// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
-// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
-// module when symbols needed by the source files are provided by java_library_host modules.
-func DroidstubsHostFactory() android.Module {
- module := &Droidstubs{}
-
- module.AddProperties(&module.properties,
- &module.Javadoc.properties)
-
- InitDroiddocModule(module, android.HostSupported)
- return module
-}
-
-func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{d.stubsSrcJar}, nil
- case ".docs.zip":
- return android.Paths{d.docZip}, nil
- case ".api.txt", android.DefaultDistTag:
- // This is the default dist path for dist properties that have no tag property.
- return android.Paths{d.apiFilePath}, nil
- case ".removed-api.txt":
- return android.Paths{d.removedApiFilePath}, nil
- case ".annotations.zip":
- return android.Paths{d.annotationsZip}, nil
- case ".api_versions.xml":
- return android.Paths{d.apiVersionsXml}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (d *Droidstubs) ApiFilePath() android.Path {
- return d.apiFilePath
-}
-
-func (d *Droidstubs) RemovedApiFilePath() android.Path {
- return d.removedApiFilePath
-}
-
-func (d *Droidstubs) StubsSrcJar() android.Path {
- return d.stubsSrcJar
-}
-
-func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
- d.Javadoc.addDeps(ctx)
-
- if len(d.properties.Merge_annotations_dirs) != 0 {
- for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
- }
- }
-
- if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
- for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
- }
- }
-
- if len(d.properties.Api_levels_annotations_dirs) != 0 {
- for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
- ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
- }
- }
-}
-
-func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
- apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
- String(d.properties.Api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
- d.apiFile = android.PathForModuleOut(ctx, filename)
- cmd.FlagWithOutput("--api ", d.apiFile)
- d.apiFilePath = d.apiFile
- } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
- // If check api is disabled then make the source file available for export.
- d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
- }
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
- apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
- String(d.properties.Removed_api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
- d.removedApiFile = android.PathForModuleOut(ctx, filename)
- cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
- d.removedApiFilePath = d.removedApiFile
- } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
- // If check api is disabled then make the source removed api file available for export.
- d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
- }
-
- if String(d.properties.Removed_dex_api_filename) != "" {
- d.removedDexApiFile = android.PathForModuleOut(ctx, String(d.properties.Removed_dex_api_filename))
- cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
- }
-
- if Bool(d.properties.Write_sdk_values) {
- d.metadataDir = android.PathForModuleOut(ctx, "metadata")
- cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
- }
-
- if stubsDir.Valid() {
- if Bool(d.properties.Create_doc_stubs) {
- cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
- } else {
- cmd.FlagWithArg("--stubs ", stubsDir.String())
- if !Bool(d.properties.Output_javadoc_comments) {
- cmd.Flag("--exclude-documentation-from-stubs")
- }
- }
- }
-}
-
-func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- if Bool(d.properties.Annotations_enabled) {
- cmd.Flag("--include-annotations")
-
- validatingNullability :=
- android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
- String(d.properties.Validate_nullability_from_list) != ""
-
- migratingNullability := String(d.properties.Previous_api) != ""
- if migratingNullability {
- previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
- cmd.FlagWithInput("--migrate-nullness ", previousApi)
- }
-
- if s := String(d.properties.Validate_nullability_from_list); s != "" {
- cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
- }
-
- if validatingNullability {
- d.nullabilityWarningsFile = android.PathForModuleOut(ctx, ctx.ModuleName()+"_nullability_warnings.txt")
- cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
- }
-
- d.annotationsZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"_annotations.zip")
- cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
-
- if len(d.properties.Merge_annotations_dirs) != 0 {
- d.mergeAnnoDirFlags(ctx, cmd)
- }
-
- // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
- cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
- FlagWithArg("--hide ", "SuperfluousPrefix").
- FlagWithArg("--hide ", "AnnotationExtraction")
- }
-}
-
-func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
- if t, ok := m.(*ExportedDroiddocDir); ok {
- cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
- } else {
- ctx.PropertyErrorf("merge_annotations_dirs",
- "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
- }
- })
-}
-
-func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
- if t, ok := m.(*ExportedDroiddocDir); ok {
- cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
- } else {
- ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
- "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
- }
- })
-}
-
-func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
- if !Bool(d.properties.Api_levels_annotations_enabled) {
- return
- }
-
- d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml")
-
- if len(d.properties.Api_levels_annotations_dirs) == 0 {
- ctx.PropertyErrorf("api_levels_annotations_dirs",
- "has to be non-empty if api levels annotations was enabled!")
- }
-
- cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
- cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
- cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
- cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
-
- filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
-
- ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
- if t, ok := m.(*ExportedDroiddocDir); ok {
- for _, dep := range t.deps {
- if strings.HasSuffix(dep.String(), filename) {
- cmd.Implicit(dep)
- }
- }
- cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
- } else {
- ctx.PropertyErrorf("api_levels_annotations_dirs",
- "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
- }
- })
-}
-
-func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
- srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths,
- implicitsRsp, homeDir android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
- rule.Command().Text("rm -rf").Flag(homeDir.String())
- rule.Command().Text("mkdir -p").Flag(homeDir.String())
-
- cmd := rule.Command()
- cmd.FlagWithArg("ANDROID_SDK_HOME=", homeDir.String())
-
- if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
- rule.Remoteable(android.RemoteRuleSupports{RBE: true})
- pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
- execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
- labels := map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"}
- if !sandbox {
- execStrategy = remoteexec.LocalExecStrategy
- labels["shallow"] = "true"
- }
- inputs := []string{
- ctx.Config().HostJavaToolPath(ctx, "metalava").String(),
- homeDir.String(),
- }
- if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
- inputs = append(inputs, strings.Split(v, ",")...)
- }
- cmd.Text((&remoteexec.REParams{
- Labels: labels,
- ExecStrategy: execStrategy,
- Inputs: inputs,
- RSPFile: implicitsRsp.String(),
- ToolchainInputs: []string{config.JavaCmd(ctx).String()},
- Platform: map[string]string{remoteexec.PoolKey: pool},
- EnvironmentVariables: []string{"ANDROID_SDK_HOME"},
- }).NoVarTemplate(ctx.Config().RBEWrapper()))
- }
-
- cmd.BuiltTool("metalava").
- Flag(config.JavacVmFlags).
- Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
- FlagWithArg("-encoding ", "UTF-8").
- FlagWithArg("-source ", javaVersion.String()).
- FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
- FlagWithInput("@", srcJarList)
-
- if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
- cmd.Implicit(android.PathForSource(ctx, javaHome))
- }
-
- if sandbox {
- cmd.FlagWithOutput("--strict-input-files ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
- } else {
- cmd.FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
- }
-
- if implicitsRsp != nil {
- cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
- }
-
- if len(bootclasspath) > 0 {
- cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
- }
-
- if len(classpath) > 0 {
- cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
- }
-
- if len(sourcepaths) > 0 {
- cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
- } else {
- cmd.FlagWithArg("-sourcepath ", `""`)
- }
-
- cmd.Flag("--no-banner").
- Flag("--color").
- Flag("--quiet").
- Flag("--format=v2").
- FlagWithArg("--repeat-errors-max ", "10").
- FlagWithArg("--hide ", "UnresolvedImport")
-
- return cmd
-}
-
-func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- deps := d.Javadoc.collectDeps(ctx)
-
- javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
-
- // Create rule for metalava
-
- srcJarDir := android.PathForModuleOut(ctx, "srcjars")
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- if BoolDefault(d.properties.High_mem, false) {
- // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
- rule.HighMem()
- }
-
- generateStubs := BoolDefault(d.properties.Generate_stubs, true)
- var stubsDir android.OptionalPath
- if generateStubs {
- d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
- stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "stubsDir"))
- rule.Command().Text("rm -rf").Text(stubsDir.String())
- rule.Command().Text("mkdir -p").Text(stubsDir.String())
- }
-
- srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
-
- implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
- homeDir := android.PathForModuleOut(ctx, "metalava-home")
- cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
- deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp, homeDir,
- Bool(d.Javadoc.properties.Sandbox))
- cmd.Implicits(d.Javadoc.implicits)
-
- d.stubsFlags(ctx, cmd, stubsDir)
-
- d.annotationsFlags(ctx, cmd)
- d.inclusionAnnotationsFlags(ctx, cmd)
- d.apiLevelsAnnotationsFlags(ctx, cmd)
-
- if android.InList("--generate-documentation", d.Javadoc.args) {
- // Currently Metalava have the ability to invoke Javadoc in a seperate process.
- // Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
- // "--generate-documentation" arg. This is not needed when Metalava removes this feature.
- d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
- }
-
- cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
- for _, o := range d.Javadoc.properties.Out {
- cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
- }
-
- // Add options for the other optional tasks: API-lint and check-released.
- // We generate separate timestamp files for them.
-
- doApiLint := false
- doCheckReleased := false
-
- // Add API lint options.
-
- if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
- doApiLint = true
-
- newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
- if newSince.Valid() {
- cmd.FlagWithInput("--api-lint ", newSince.Path())
- } else {
- cmd.Flag("--api-lint")
- }
- d.apiLintReport = android.PathForModuleOut(ctx, "api_lint_report.txt")
- cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
-
- // TODO(b/154317059): Clean up this whitelist by baselining and/or checking in last-released.
- if d.Name() != "android.car-system-stubs-docs" &&
- d.Name() != "android.car-stubs-docs" {
- cmd.Flag("--lints-as-errors")
- cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
- }
-
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
- updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
- d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
-
- // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
- // However, because $' ... ' doesn't expand environmental variables, we can't just embed
- // $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again,
- // which is why we have '"$PWD"$' in it.
- //
- // TODO: metalava also has a slightly different message hardcoded. Should we unify this
- // message and metalava's one?
- msg := `$'` + // Enclose with $' ... '
- `************************************************************\n` +
- `Your API changes are triggering API Lint warnings or errors.\n` +
- `To make these errors go away, fix the code according to the\n` +
- `error and/or warning messages above.\n` +
- `\n` +
- `If it is not possible to do so, there are workarounds:\n` +
- `\n` +
- `1. You can suppress the errors with @SuppressLint("<id>")\n`
-
- if baselineFile.Valid() {
- cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
- cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
-
- msg += fmt.Sprintf(``+
- `2. You can update the baseline by executing the following\n`+
- ` command:\n`+
- ` cp \\\n`+
- ` "'"$PWD"$'/%s" \\\n`+
- ` "'"$PWD"$'/%s"\n`+
- ` To submit the revised baseline.txt to the main Android\n`+
- ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
- } else {
- msg += fmt.Sprintf(``+
- `2. You can add a baseline file of existing lint failures\n`+
- ` to the build rule of %s.\n`, d.Name())
- }
- // Note the message ends with a ' (single quote), to close the $' ... ' .
- msg += `************************************************************\n'`
-
- cmd.FlagWithArg("--error-message:api-lint ", msg)
- }
-
- // Add "check released" options. (Detect incompatible API changes from the last public release)
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
- doCheckReleased = true
-
- if len(d.Javadoc.properties.Out) > 0 {
- ctx.PropertyErrorf("out", "out property may not be combined with check_api")
- }
-
- apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
- removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
- updatedBaselineOutput := android.PathForModuleOut(ctx, "last_released_baseline.txt")
-
- d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "check_last_released_api.timestamp")
-
- cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
- cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
-
- if baselineFile.Valid() {
- cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
- cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
- }
-
- // Note this string includes quote ($' ... '), which decodes the "\n"s.
- msg := `$'\n******************************\n` +
- `You have tried to change the API from what has been previously released in\n` +
- `an SDK. Please fix the errors listed above.\n` +
- `******************************\n'`
-
- cmd.FlagWithArg("--error-message:compatibility:released ", msg)
- }
-
- impRule := android.NewRuleBuilder(pctx, ctx)
- impCmd := impRule.Command()
- // An action that copies the ninja generated rsp file to a new location. This allows us to
- // add a large number of inputs to a file without exceeding bash command length limits (which
- // would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
- // rsp file to be ${output}.rsp.
- impCmd.Text("cp").
- FlagWithRspFileInputList("", android.PathForModuleOut(ctx, "metalava-implicits.rsp"), cmd.GetImplicits()).
- Output(implicitsRsp)
- impRule.Build("implicitsGen", "implicits generation")
- cmd.Implicit(implicitsRsp)
-
- if generateStubs {
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
- FlagWithArg("-C ", stubsDir.String()).
- FlagWithArg("-D ", stubsDir.String())
- }
-
- if Bool(d.properties.Write_sdk_values) {
- d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-d").
- FlagWithOutput("-o ", d.metadataZip).
- FlagWithArg("-C ", d.metadataDir.String()).
- FlagWithArg("-D ", d.metadataDir.String())
- }
-
- // TODO: We don't really need two separate API files, but this is a reminiscence of how
- // we used to run metalava separately for API lint and the "last_released" check. Unify them.
- if doApiLint {
- rule.Command().Text("touch").Output(d.apiLintTimestamp)
- }
- if doCheckReleased {
- rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
- }
-
- rule.Restat()
-
- zipSyncCleanupCmd(rule, srcJarDir)
-
- rule.Build("metalava", "metalava merged")
-
- if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
-
- if len(d.Javadoc.properties.Out) > 0 {
- ctx.PropertyErrorf("out", "out property may not be combined with check_api")
- }
-
- apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
- removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
- baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
-
- if baselineFile.Valid() {
- ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
- }
-
- d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- // Diff command line.
- // -F matches the closest "opening" line, such as "package android {"
- // and " public class Intent {".
- diff := `diff -u -F '{ *$'`
-
- rule.Command().Text("( true")
- rule.Command().
- Text(diff).
- Input(apiFile).Input(d.apiFile)
-
- rule.Command().
- Text(diff).
- Input(removedApiFile).Input(d.removedApiFile)
-
- msg := fmt.Sprintf(`\n******************************\n`+
- `You have tried to change the API from what has been previously approved.\n\n`+
- `To make these errors go away, you have two choices:\n`+
- ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
- ` to the new methods, etc. shown in the above diff.\n\n`+
- ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
- ` m %s-update-current-api\n\n`+
- ` To submit the revised current.txt to the main Android repository,\n`+
- ` you will need approval.\n`+
- `******************************\n`, ctx.ModuleName())
-
- rule.Command().
- Text("touch").Output(d.checkCurrentApiTimestamp).
- Text(") || (").
- Text("echo").Flag("-e").Flag(`"` + msg + `"`).
- Text("; exit 38").
- Text(")")
-
- rule.Build("metalavaCurrentApiCheck", "check current API")
-
- d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
-
- // update API rule
- rule = android.NewRuleBuilder(pctx, ctx)
-
- rule.Command().Text("( true")
-
- rule.Command().
- Text("cp").Flag("-f").
- Input(d.apiFile).Flag(apiFile.String())
-
- rule.Command().
- Text("cp").Flag("-f").
- Input(d.removedApiFile).Flag(removedApiFile.String())
-
- msg = "failed to update public API"
-
- rule.Command().
- Text("touch").Output(d.updateCurrentApiTimestamp).
- Text(") || (").
- Text("echo").Flag("-e").Flag(`"` + msg + `"`).
- Text("; exit 38").
- Text(")")
-
- rule.Build("metalavaCurrentApiUpdate", "update current API")
- }
-
- if String(d.properties.Check_nullability_warnings) != "" {
- if d.nullabilityWarningsFile == nil {
- ctx.PropertyErrorf("check_nullability_warnings",
- "Cannot specify check_nullability_warnings unless validating nullability")
- }
-
- checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
-
- d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "check_nullability_warnings.timestamp")
-
- msg := fmt.Sprintf(`\n******************************\n`+
- `The warnings encountered during nullability annotation validation did\n`+
- `not match the checked in file of expected warnings. The diffs are shown\n`+
- `above. You have two options:\n`+
- ` 1. Resolve the differences by editing the nullability annotations.\n`+
- ` 2. Update the file of expected warnings by running:\n`+
- ` cp %s %s\n`+
- ` and submitting the updated file as part of your change.`,
- d.nullabilityWarningsFile, checkNullabilityWarnings)
-
- rule := android.NewRuleBuilder(pctx, ctx)
-
- rule.Command().
- Text("(").
- Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
- Text("&&").
- Text("touch").Output(d.checkNullabilityWarningsTimestamp).
- Text(") || (").
- Text("echo").Flag("-e").Flag(`"` + msg + `"`).
- Text("; exit 38").
- Text(")")
-
- rule.Build("nullabilityWarningsCheck", "nullability warnings check")
- }
-}
-
-//
// Exported Droiddoc Directory
//
var droiddocTemplateTag = dependencyTag{name: "droiddoc-template"}
-var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
-var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
-var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
type ExportedDroiddocDirProperties struct {
// path to the directory containing Droiddoc related files.
@@ -1662,19 +913,6 @@
return module
}
-func StubsDefaultsFactory() android.Module {
- module := &DocDefaults{}
-
- module.AddProperties(
- &JavadocProperties{},
- &DroidstubsProperties{},
- )
-
- android.InitDefaultsModule(module)
-
- return module
-}
-
func zipSyncCmd(ctx android.ModuleContext, rule *android.RuleBuilder,
srcJarDir android.ModuleOutPath, srcJars android.Paths) android.OutputPath {
@@ -1699,94 +937,3 @@
func zipSyncCleanupCmd(rule *android.RuleBuilder, srcJarDir android.ModuleOutPath) {
rule.Command().Text("rm -rf").Text(srcJarDir.String())
}
-
-var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
-
-type PrebuiltStubsSourcesProperties struct {
- Srcs []string `android:"path"`
-}
-
-type PrebuiltStubsSources struct {
- android.ModuleBase
- android.DefaultableModuleBase
- prebuilt android.Prebuilt
- android.SdkBase
-
- properties PrebuiltStubsSourcesProperties
-
- stubsSrcJar android.ModuleOutPath
-}
-
-func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{p.stubsSrcJar}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
- return d.stubsSrcJar
-}
-
-func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
- if len(p.properties.Srcs) != 1 {
- ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
- return
- }
-
- localSrcDir := p.properties.Srcs[0]
- // Although PathForModuleSrc can return nil if either the path doesn't exist or
- // the path components are invalid it won't in this case because no components
- // are specified and the module directory must exist in order to get this far.
- srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
-
- // Glob the contents of the directory just in case the directory does not exist.
- srcGlob := localSrcDir + "/**/*"
- srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
-
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("soong_zip").
- Flag("-write_if_changed").
- Flag("-jar").
- FlagWithOutput("-o ", p.stubsSrcJar).
- FlagWithArg("-C ", srcDir.String()).
- FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
-
- rule.Restat()
-
- rule.Build("zip src", "Create srcjar from prebuilt source")
-}
-
-func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
- return &p.prebuilt
-}
-
-func (p *PrebuiltStubsSources) Name() string {
- return p.prebuilt.Name(p.ModuleBase.Name())
-}
-
-// prebuilt_stubs_sources imports a set of java source files as if they were
-// generated by droidstubs.
-//
-// By default, a prebuilt_stubs_sources has a single variant that expects a
-// set of `.java` files generated by droidstubs.
-//
-// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
-// for host modules.
-//
-// Intended only for use by sdk snapshots.
-func PrebuiltStubsSourcesFactory() android.Module {
- module := &PrebuiltStubsSources{}
-
- module.AddProperties(&module.properties)
-
- android.InitPrebuiltModule(module, &module.properties.Srcs)
- android.InitSdkAwareModule(module)
- InitDroiddocModule(module, android.HostAndDeviceSupported)
- return module
-}
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
new file mode 100644
index 0000000..2b324ae
--- /dev/null
+++ b/java/droiddoc_test.go
@@ -0,0 +1,147 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestDroiddoc(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droidstubs {
+ name: "bar-stubs",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ api_levels_annotations_dirs: [
+ "droiddoc-templates-sdk",
+ ],
+ api_levels_annotations_enabled: true,
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ ":bar-stubs",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ "bar-doc/b.java": nil,
+ })
+ barStubs := ctx.ModuleForTests("bar-stubs", "android_common")
+ barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err)
+ }
+ if len(barStubsOutputs) != 1 {
+ t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
+ }
+
+ barStubsOutput := barStubsOutputs[0]
+ barDoc := ctx.ModuleForTests("bar-doc", "android_common")
+ javaDoc := barDoc.Rule("javadoc").RelativeToTop()
+ if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
+ t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+ }
+
+ expected := "-sourcepath out/soong/.intermediates/bar-doc/android_common/srcjars "
+ if !strings.Contains(javaDoc.RuleParams.Command, expected) {
+ t.Errorf("bar-doc command does not contain flag %q, but should\n%q", expected, javaDoc.RuleParams.Command)
+ }
+
+ aidl := barDoc.Rule("aidl")
+ if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(aidl.Output); !inList(w, g) {
+ t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
+ }
+
+ if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("aidl inputs must be %q, but was %q", w, g)
+ }
+}
+
+func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+ testJavaError(t, "flags is set. Cannot set args", `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+ filegroup {
+ name: "bar-doc-aidl-srcs",
+ srcs: ["bar-doc/IBar.aidl"],
+ path: "bar-doc",
+ }
+ droidstubs {
+ name: "bar-stubs",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ exclude_srcs: [
+ "bar-doc/b.java"
+ ],
+ api_levels_annotations_dirs: [
+ "droiddoc-templates-sdk",
+ ],
+ api_levels_annotations_enabled: true,
+ }
+ droiddoc {
+ name: "bar-doc",
+ srcs: [
+ ":bar-stubs",
+ "bar-doc/IFoo.aidl",
+ ":bar-doc-aidl-srcs",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ hdf: [
+ "android.whichdoc offline",
+ ],
+ knowntags: [
+ "bar-doc/known_oj_tags.txt",
+ ],
+ proofread_file: "libcore-proofread.txt",
+ todo_file: "libcore-docs-todo.html",
+ flags: ["-offlinemode -title \"libcore\""],
+ args: "-offlinemode -title \"libcore\"",
+ }
+ `)
+}
diff --git a/java/droidstubs.go b/java/droidstubs.go
new file mode 100644
index 0000000..e453e62
--- /dev/null
+++ b/java/droidstubs.go
@@ -0,0 +1,900 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/java/config"
+ "android/soong/remoteexec"
+)
+
+func init() {
+ RegisterStubsBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
+
+ ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
+ ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
+
+ ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
+}
+
+//
+// Droidstubs
+//
+type Droidstubs struct {
+ Javadoc
+ android.SdkBase
+
+ properties DroidstubsProperties
+ apiFile android.WritablePath
+ apiXmlFile android.WritablePath
+ lastReleasedApiXmlFile android.WritablePath
+ privateApiFile android.WritablePath
+ removedApiFile android.WritablePath
+ removedDexApiFile android.WritablePath
+ nullabilityWarningsFile android.WritablePath
+
+ checkCurrentApiTimestamp android.WritablePath
+ updateCurrentApiTimestamp android.WritablePath
+ checkLastReleasedApiTimestamp android.WritablePath
+ apiLintTimestamp android.WritablePath
+ apiLintReport android.WritablePath
+
+ checkNullabilityWarningsTimestamp android.WritablePath
+
+ annotationsZip android.WritablePath
+ apiVersionsXml android.WritablePath
+
+ apiFilePath android.Path
+ removedApiFilePath android.Path
+
+ metadataZip android.WritablePath
+ metadataDir android.WritablePath
+}
+
+type DroidstubsProperties struct {
+ // The generated public API filename by Metalava, defaults to <module>_api.txt
+ Api_filename *string
+
+ // the generated removed API filename by Metalava, defaults to <module>_removed.txt
+ Removed_api_filename *string
+
+ // the generated removed Dex API filename by Metalava.
+ Removed_dex_api_filename *string
+
+ Check_api struct {
+ Last_released ApiToCheck
+
+ Current ApiToCheck
+
+ Api_lint struct {
+ Enabled *bool
+
+ // If set, performs api_lint on any new APIs not found in the given signature file
+ New_since *string `android:"path"`
+
+ // If not blank, path to the baseline txt file for approved API lint violations.
+ Baseline_file *string `android:"path"`
+ }
+ }
+
+ // user can specify the version of previous released API file in order to do compatibility check.
+ Previous_api *string `android:"path"`
+
+ // is set to true, Metalava will allow framework SDK to contain annotations.
+ Annotations_enabled *bool
+
+ // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
+ Merge_annotations_dirs []string
+
+ // a list of top-level directories containing Java stub files to merge show/hide annotations from.
+ Merge_inclusion_annotations_dirs []string
+
+ // a file containing a list of classes to do nullability validation for.
+ Validate_nullability_from_list *string
+
+ // a file containing expected warnings produced by validation of nullability annotations.
+ Check_nullability_warnings *string
+
+ // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
+ Create_doc_stubs *bool
+
+ // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
+ // Has no effect if create_doc_stubs: true.
+ Output_javadoc_comments *bool
+
+ // if set to false then do not write out stubs. Defaults to true.
+ //
+ // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
+ Generate_stubs *bool
+
+ // if set to true, provides a hint to the build system that this rule uses a lot of memory,
+ // whicih can be used for scheduling purposes
+ High_mem *bool
+
+ // is set to true, Metalava will allow framework SDK to contain API levels annotations.
+ Api_levels_annotations_enabled *bool
+
+ // the dirs which Metalava extracts API levels annotations from.
+ Api_levels_annotations_dirs []string
+
+ // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
+ Api_levels_jar_filename *string
+
+ // if set to true, collect the values used by the Dev tools and
+ // write them in files packaged with the SDK. Defaults to false.
+ Write_sdk_values *bool
+}
+
+// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
+// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
+// a droiddoc module to generate documentation.
+func DroidstubsFactory() android.Module {
+ module := &Droidstubs{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostAndDeviceSupported)
+ android.InitSdkAwareModule(module)
+ return module
+}
+
+// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
+// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
+// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
+// module when symbols needed by the source files are provided by java_library_host modules.
+func DroidstubsHostFactory() android.Module {
+ module := &Droidstubs{}
+
+ module.AddProperties(&module.properties,
+ &module.Javadoc.properties)
+
+ InitDroiddocModule(module, android.HostSupported)
+ return module
+}
+
+func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{d.stubsSrcJar}, nil
+ case ".docs.zip":
+ return android.Paths{d.docZip}, nil
+ case ".api.txt", android.DefaultDistTag:
+ // This is the default dist path for dist properties that have no tag property.
+ return android.Paths{d.apiFilePath}, nil
+ case ".removed-api.txt":
+ return android.Paths{d.removedApiFilePath}, nil
+ case ".annotations.zip":
+ return android.Paths{d.annotationsZip}, nil
+ case ".api_versions.xml":
+ return android.Paths{d.apiVersionsXml}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (d *Droidstubs) ApiFilePath() android.Path {
+ return d.apiFilePath
+}
+
+func (d *Droidstubs) RemovedApiFilePath() android.Path {
+ return d.removedApiFilePath
+}
+
+func (d *Droidstubs) StubsSrcJar() android.Path {
+ return d.stubsSrcJar
+}
+
+var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
+var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
+var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
+
+func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
+ d.Javadoc.addDeps(ctx)
+
+ if len(d.properties.Merge_annotations_dirs) != 0 {
+ for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
+ for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
+ }
+ }
+
+ if len(d.properties.Api_levels_annotations_dirs) != 0 {
+ for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
+ ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
+ }
+ }
+}
+
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Api_filename) != "" {
+ filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+ d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
+ cmd.FlagWithOutput("--api ", d.apiFile)
+ d.apiFilePath = d.apiFile
+ } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
+ // If check api is disabled then make the source file available for export.
+ d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
+ }
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
+ apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
+ String(d.properties.Removed_api_filename) != "" {
+ filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
+ d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
+ cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
+ d.removedApiFilePath = d.removedApiFile
+ } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
+ // If check api is disabled then make the source removed api file available for export.
+ d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+ }
+
+ if String(d.properties.Removed_dex_api_filename) != "" {
+ d.removedDexApiFile = android.PathForModuleOut(ctx, "metalava", String(d.properties.Removed_dex_api_filename))
+ cmd.FlagWithOutput("--removed-dex-api ", d.removedDexApiFile)
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
+ cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
+ }
+
+ if stubsDir.Valid() {
+ if Bool(d.properties.Create_doc_stubs) {
+ cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
+ } else {
+ cmd.FlagWithArg("--stubs ", stubsDir.String())
+ if !Bool(d.properties.Output_javadoc_comments) {
+ cmd.Flag("--exclude-documentation-from-stubs")
+ }
+ }
+ }
+}
+
+func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ if Bool(d.properties.Annotations_enabled) {
+ cmd.Flag("--include-annotations")
+
+ validatingNullability :=
+ android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
+ String(d.properties.Validate_nullability_from_list) != ""
+
+ migratingNullability := String(d.properties.Previous_api) != ""
+ if migratingNullability {
+ previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
+ cmd.FlagWithInput("--migrate-nullness ", previousApi)
+ }
+
+ if s := String(d.properties.Validate_nullability_from_list); s != "" {
+ cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
+ }
+
+ if validatingNullability {
+ d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
+ cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
+ }
+
+ d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
+ cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
+
+ if len(d.properties.Merge_annotations_dirs) != 0 {
+ d.mergeAnnoDirFlags(ctx, cmd)
+ }
+
+ // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
+ cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
+ FlagWithArg("--hide ", "SuperfluousPrefix").
+ FlagWithArg("--hide ", "AnnotationExtraction")
+ }
+}
+
+func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
+ } else {
+ ctx.PropertyErrorf("merge_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
+ } else {
+ ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
+ "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ if !Bool(d.properties.Api_levels_annotations_enabled) {
+ return
+ }
+
+ d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
+
+ if len(d.properties.Api_levels_annotations_dirs) == 0 {
+ ctx.PropertyErrorf("api_levels_annotations_dirs",
+ "has to be non-empty if api levels annotations was enabled!")
+ }
+
+ cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
+ cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
+ cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
+
+ filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
+
+ ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
+ if t, ok := m.(*ExportedDroiddocDir); ok {
+ for _, dep := range t.deps {
+ if strings.HasSuffix(dep.String(), filename) {
+ cmd.Implicit(dep)
+ }
+ }
+ cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
+ } else {
+ ctx.PropertyErrorf("api_levels_annotations_dirs",
+ "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
+ }
+ })
+}
+
+func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
+ srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths,
+ implicitsRsp, homeDir android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
+ rule.Command().Text("rm -rf").Flag(homeDir.String())
+ rule.Command().Text("mkdir -p").Flag(homeDir.String())
+
+ cmd := rule.Command()
+ cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
+
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
+ rule.Remoteable(android.RemoteRuleSupports{RBE: true})
+ if sandbox {
+ execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+ labels := map[string]string{"type": "tool", "name": "metalava"}
+ // TODO: metalava pool rejects these jobs
+ pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
+ rule.Rewrapper(&remoteexec.REParams{
+ Labels: labels,
+ ExecStrategy: execStrategy,
+ ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+ Platform: map[string]string{remoteexec.PoolKey: pool},
+ })
+ } else {
+ execStrategy := remoteexec.LocalExecStrategy
+ labels := map[string]string{"type": "compile", "lang": "java", "compiler": "metalava", "shallow": "true"}
+ pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
+
+ inputs := []string{
+ ctx.Config().HostJavaToolPath(ctx, "metalava").String(),
+ homeDir.String(),
+ }
+ if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
+ inputs = append(inputs, strings.Split(v, ",")...)
+ }
+ cmd.Text((&remoteexec.REParams{
+ Labels: labels,
+ ExecStrategy: execStrategy,
+ Inputs: inputs,
+ RSPFiles: []string{implicitsRsp.String()},
+ ToolchainInputs: []string{config.JavaCmd(ctx).String()},
+ Platform: map[string]string{remoteexec.PoolKey: pool},
+ EnvironmentVariables: []string{"ANDROID_PREFS_ROOT"},
+ }).NoVarTemplate(ctx.Config().RBEWrapper()))
+ }
+ }
+
+ cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
+ Flag(config.JavacVmFlags).
+ Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
+ FlagWithArg("-encoding ", "UTF-8").
+ FlagWithArg("-source ", javaVersion.String()).
+ FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
+ FlagWithInput("@", srcJarList)
+
+ if !sandbox {
+ if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
+ cmd.Implicit(android.PathForSource(ctx, javaHome))
+ }
+
+ cmd.FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
+
+ if implicitsRsp != nil {
+ cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
+ }
+ }
+
+ if len(bootclasspath) > 0 {
+ cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
+ }
+
+ if len(classpath) > 0 {
+ cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
+ }
+
+ if len(sourcepaths) > 0 {
+ cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
+ } else {
+ cmd.FlagWithArg("-sourcepath ", `""`)
+ }
+
+ cmd.Flag("--no-banner").
+ Flag("--color").
+ Flag("--quiet").
+ Flag("--format=v2").
+ FlagWithArg("--repeat-errors-max ", "10").
+ FlagWithArg("--hide ", "UnresolvedImport")
+
+ return cmd
+}
+
+func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ deps := d.Javadoc.collectDeps(ctx)
+
+ javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), sdkContext(d))
+
+ // Create rule for metalava
+
+ srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ sandbox := proptools.Bool(d.Javadoc.properties.Sandbox)
+ if sandbox {
+ rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
+ android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
+ SandboxInputs()
+ }
+
+ if BoolDefault(d.properties.High_mem, false) {
+ // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
+ rule.HighMem()
+ }
+
+ generateStubs := BoolDefault(d.properties.Generate_stubs, true)
+ var stubsDir android.OptionalPath
+ if generateStubs {
+ d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
+ stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
+ rule.Command().Text("rm -rf").Text(stubsDir.String())
+ rule.Command().Text("mkdir -p").Text(stubsDir.String())
+ }
+
+ srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
+
+ implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
+ homeDir := android.PathForModuleOut(ctx, "metalava", "home")
+ cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
+ deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp, homeDir,
+ sandbox)
+ cmd.Implicits(d.Javadoc.implicits)
+
+ d.stubsFlags(ctx, cmd, stubsDir)
+
+ d.annotationsFlags(ctx, cmd)
+ d.inclusionAnnotationsFlags(ctx, cmd)
+ d.apiLevelsAnnotationsFlags(ctx, cmd)
+
+ if android.InList("--generate-documentation", d.Javadoc.args) {
+ // Currently Metalava have the ability to invoke Javadoc in a separate process.
+ // Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
+ // "--generate-documentation" arg. This is not needed when Metalava removes this feature.
+ d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
+ }
+
+ cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
+ for _, o := range d.Javadoc.properties.Out {
+ cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
+ }
+
+ // Add options for the other optional tasks: API-lint and check-released.
+ // We generate separate timestamp files for them.
+
+ doApiLint := false
+ doCheckReleased := false
+
+ // Add API lint options.
+
+ if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
+ doApiLint = true
+
+ newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
+ if newSince.Valid() {
+ cmd.FlagWithInput("--api-lint ", newSince.Path())
+ } else {
+ cmd.Flag("--api-lint")
+ }
+ d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
+ cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
+
+ // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
+ if d.Name() != "android.car-system-stubs-docs" &&
+ d.Name() != "android.car-stubs-docs" {
+ cmd.Flag("--lints-as-errors")
+ cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
+ }
+
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
+ d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
+
+ // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
+ // However, because $' ... ' doesn't expand environmental variables, we can't just embed
+ // $PWD, so we have to terminate $'...', use "$PWD", then start $' ... ' again,
+ // which is why we have '"$PWD"$' in it.
+ //
+ // TODO: metalava also has a slightly different message hardcoded. Should we unify this
+ // message and metalava's one?
+ msg := `$'` + // Enclose with $' ... '
+ `************************************************************\n` +
+ `Your API changes are triggering API Lint warnings or errors.\n` +
+ `To make these errors go away, fix the code according to the\n` +
+ `error and/or warning messages above.\n` +
+ `\n` +
+ `If it is not possible to do so, there are workarounds:\n` +
+ `\n` +
+ `1. You can suppress the errors with @SuppressLint("<id>")\n`
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
+
+ msg += fmt.Sprintf(``+
+ `2. You can update the baseline by executing the following\n`+
+ ` command:\n`+
+ ` cp \\\n`+
+ ` "'"$PWD"$'/%s" \\\n`+
+ ` "'"$PWD"$'/%s"\n`+
+ ` To submit the revised baseline.txt to the main Android\n`+
+ ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
+ } else {
+ msg += fmt.Sprintf(``+
+ `2. You can add a baseline file of existing lint failures\n`+
+ ` to the build rule of %s.\n`, d.Name())
+ }
+ // Note the message ends with a ' (single quote), to close the $' ... ' .
+ msg += `************************************************************\n'`
+
+ cmd.FlagWithArg("--error-message:api-lint ", msg)
+ }
+
+ // Add "check released" options. (Detect incompatible API changes from the last public release)
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
+ doCheckReleased = true
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
+ updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
+
+ d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
+
+ cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
+ cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
+
+ if baselineFile.Valid() {
+ cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
+ cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
+ }
+
+ // Note this string includes quote ($' ... '), which decodes the "\n"s.
+ msg := `$'\n******************************\n` +
+ `You have tried to change the API from what has been previously released in\n` +
+ `an SDK. Please fix the errors listed above.\n` +
+ `******************************\n'`
+
+ cmd.FlagWithArg("--error-message:compatibility:released ", msg)
+ }
+
+ if !sandbox {
+ // When sandboxing is enabled RuleBuilder tracks all the inputs needed for remote execution.
+ // Without it we have to do it manually.
+ impRule := android.NewRuleBuilder(pctx, ctx)
+ impCmd := impRule.Command()
+ // An action that copies the ninja generated rsp file to a new location. This allows us to
+ // add a large number of inputs to a file without exceeding bash command length limits (which
+ // would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
+ // rsp file to be ${output}.rsp.
+ impCmd.Text("cp").
+ FlagWithRspFileInputList("", android.PathForModuleOut(ctx, "metalava-implicits.rsp"), cmd.GetImplicits()).
+ Output(implicitsRsp)
+ impRule.Build("implicitsGen", "implicits generation")
+ cmd.Implicit(implicitsRsp)
+ }
+
+ if generateStubs {
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+ FlagWithArg("-C ", stubsDir.String()).
+ FlagWithArg("-D ", stubsDir.String())
+ }
+
+ if Bool(d.properties.Write_sdk_values) {
+ d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-d").
+ FlagWithOutput("-o ", d.metadataZip).
+ FlagWithArg("-C ", d.metadataDir.String()).
+ FlagWithArg("-D ", d.metadataDir.String())
+ }
+
+ // TODO: We don't really need two separate API files, but this is a reminiscence of how
+ // we used to run metalava separately for API lint and the "last_released" check. Unify them.
+ if doApiLint {
+ rule.Command().Text("touch").Output(d.apiLintTimestamp)
+ }
+ if doCheckReleased {
+ rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
+ }
+
+ // TODO(b/183630617): rewrapper doesn't support restat rules
+ if !sandbox {
+ rule.Restat()
+ }
+
+ zipSyncCleanupCmd(rule, srcJarDir)
+
+ rule.Build("metalava", "metalava merged")
+
+ if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
+
+ if len(d.Javadoc.properties.Out) > 0 {
+ ctx.PropertyErrorf("out", "out property may not be combined with check_api")
+ }
+
+ apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
+ removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
+ baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
+
+ if baselineFile.Valid() {
+ ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
+ }
+
+ d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ // Diff command line.
+ // -F matches the closest "opening" line, such as "package android {"
+ // and " public class Intent {".
+ diff := `diff -u -F '{ *$'`
+
+ rule.Command().Text("( true")
+ rule.Command().
+ Text(diff).
+ Input(apiFile).Input(d.apiFile)
+
+ rule.Command().
+ Text(diff).
+ Input(removedApiFile).Input(d.removedApiFile)
+
+ msg := fmt.Sprintf(`\n******************************\n`+
+ `You have tried to change the API from what has been previously approved.\n\n`+
+ `To make these errors go away, you have two choices:\n`+
+ ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
+ ` to the new methods, etc. shown in the above diff.\n\n`+
+ ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
+ ` m %s-update-current-api\n\n`+
+ ` To submit the revised current.txt to the main Android repository,\n`+
+ ` you will need approval.\n`+
+ `******************************\n`, ctx.ModuleName())
+
+ rule.Command().
+ Text("touch").Output(d.checkCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build("metalavaCurrentApiCheck", "check current API")
+
+ d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
+
+ // update API rule
+ rule = android.NewRuleBuilder(pctx, ctx)
+
+ rule.Command().Text("( true")
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.apiFile).Flag(apiFile.String())
+
+ rule.Command().
+ Text("cp").Flag("-f").
+ Input(d.removedApiFile).Flag(removedApiFile.String())
+
+ msg = "failed to update public API"
+
+ rule.Command().
+ Text("touch").Output(d.updateCurrentApiTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build("metalavaCurrentApiUpdate", "update current API")
+ }
+
+ if String(d.properties.Check_nullability_warnings) != "" {
+ if d.nullabilityWarningsFile == nil {
+ ctx.PropertyErrorf("check_nullability_warnings",
+ "Cannot specify check_nullability_warnings unless validating nullability")
+ }
+
+ checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
+
+ d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
+
+ msg := fmt.Sprintf(`\n******************************\n`+
+ `The warnings encountered during nullability annotation validation did\n`+
+ `not match the checked in file of expected warnings. The diffs are shown\n`+
+ `above. You have two options:\n`+
+ ` 1. Resolve the differences by editing the nullability annotations.\n`+
+ ` 2. Update the file of expected warnings by running:\n`+
+ ` cp %s %s\n`+
+ ` and submitting the updated file as part of your change.`,
+ d.nullabilityWarningsFile, checkNullabilityWarnings)
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ rule.Command().
+ Text("(").
+ Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
+ Text("&&").
+ Text("touch").Output(d.checkNullabilityWarningsTimestamp).
+ Text(") || (").
+ Text("echo").Flag("-e").Flag(`"` + msg + `"`).
+ Text("; exit 38").
+ Text(")")
+
+ rule.Build("nullabilityWarningsCheck", "nullability warnings check")
+ }
+}
+
+func StubsDefaultsFactory() android.Module {
+ module := &DocDefaults{}
+
+ module.AddProperties(
+ &JavadocProperties{},
+ &DroidstubsProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
+}
+
+var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
+
+type PrebuiltStubsSourcesProperties struct {
+ Srcs []string `android:"path"`
+}
+
+type PrebuiltStubsSources struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+ android.SdkBase
+
+ properties PrebuiltStubsSourcesProperties
+
+ stubsSrcJar android.ModuleOutPath
+}
+
+func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{p.stubsSrcJar}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
+ return d.stubsSrcJar
+}
+
+func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+
+ if len(p.properties.Srcs) != 1 {
+ ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
+ return
+ }
+
+ localSrcDir := p.properties.Srcs[0]
+ // Although PathForModuleSrc can return nil if either the path doesn't exist or
+ // the path components are invalid it won't in this case because no components
+ // are specified and the module directory must exist in order to get this far.
+ srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
+
+ // Glob the contents of the directory just in case the directory does not exist.
+ srcGlob := localSrcDir + "/**/*"
+ srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("soong_zip").
+ Flag("-write_if_changed").
+ Flag("-jar").
+ FlagWithOutput("-o ", p.stubsSrcJar).
+ FlagWithArg("-C ", srcDir.String()).
+ FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
+
+ rule.Restat()
+
+ rule.Build("zip src", "Create srcjar from prebuilt source")
+}
+
+func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *PrebuiltStubsSources) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+// prebuilt_stubs_sources imports a set of java source files as if they were
+// generated by droidstubs.
+//
+// By default, a prebuilt_stubs_sources has a single variant that expects a
+// set of `.java` files generated by droidstubs.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
+//
+// Intended only for use by sdk snapshots.
+func PrebuiltStubsSourcesFactory() android.Module {
+ module := &PrebuiltStubsSources{}
+
+ module.AddProperties(&module.properties)
+
+ android.InitPrebuiltModule(module, &module.properties.Srcs)
+ android.InitSdkAwareModule(module)
+ InitDroiddocModule(module, android.HostAndDeviceSupported)
+ return module
+}
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
new file mode 100644
index 0000000..c6db979
--- /dev/null
+++ b/java/droidstubs_test.go
@@ -0,0 +1,158 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestDroidstubs(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droiddoc_exported_dir {
+ name: "droiddoc-templates-sdk",
+ path: ".",
+ }
+
+ droidstubs {
+ name: "bar-stubs",
+ srcs: ["bar-doc/a.java"],
+ api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
+ api_levels_annotations_enabled: true,
+ }
+
+ droidstubs {
+ name: "bar-stubs-other",
+ srcs: ["bar-doc/a.java"],
+ high_mem: true,
+ api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
+ api_levels_annotations_enabled: true,
+ api_levels_jar_filename: "android.other.jar",
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ })
+ testcases := []struct {
+ moduleName string
+ expectedJarFilename string
+ high_mem bool
+ }{
+ {
+ moduleName: "bar-stubs",
+ expectedJarFilename: "android.jar",
+ high_mem: false,
+ },
+ {
+ moduleName: "bar-stubs-other",
+ expectedJarFilename: "android.other.jar",
+ high_mem: true,
+ },
+ }
+ for _, c := range testcases {
+ m := ctx.ModuleForTests(c.moduleName, "android_common")
+ metalava := m.Rule("metalava")
+ rp := metalava.RuleParams
+ expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
+ if actual := rp.Command; !strings.Contains(actual, expected) {
+ t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
+ }
+
+ if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem {
+ t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual)
+ }
+ }
+}
+
+func TestDroidstubsSandbox(t *testing.T) {
+ ctx, _ := testJavaWithFS(t, `
+ droidstubs {
+ name: "bar-stubs",
+ srcs: ["bar-doc/a.java"],
+ sandbox: true,
+ }
+ `,
+ map[string][]byte{
+ "bar-doc/a.java": nil,
+ })
+
+ m := ctx.ModuleForTests("bar-stubs", "android_common")
+ metalava := m.Rule("metalava")
+ if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) {
+ t.Errorf("Expected inputs %q, got %q", w, g)
+ }
+}
+
+func TestDroidstubsWithSystemModules(t *testing.T) {
+ ctx, _ := testJava(t, `
+ droidstubs {
+ name: "stubs-source-system-modules",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ sdk_version: "none",
+ system_modules: "source-system-modules",
+ }
+
+ java_library {
+ name: "source-jar",
+ srcs: [
+ "a.java",
+ ],
+ }
+
+ java_system_modules {
+ name: "source-system-modules",
+ libs: ["source-jar"],
+ }
+
+ droidstubs {
+ name: "stubs-prebuilt-system-modules",
+ srcs: [
+ "bar-doc/a.java",
+ ],
+ sdk_version: "none",
+ system_modules: "prebuilt-system-modules",
+ }
+
+ java_import {
+ name: "prebuilt-jar",
+ jars: ["a.jar"],
+ }
+
+ java_system_modules_import {
+ name: "prebuilt-system-modules",
+ libs: ["prebuilt-jar"],
+ }
+ `)
+
+ checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar")
+
+ checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
+}
+
+func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
+ metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
+ var systemJars []string
+ for _, i := range metalavaRule.Implicits {
+ systemJars = append(systemJars, i.Base())
+ }
+ if len(systemJars) < 1 || systemJars[0] != systemJar {
+ t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
+ }
+}
diff --git a/java/java_test.go b/java/java_test.go
index 31eeb6b..2ade0fe 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -133,6 +133,15 @@
}
}
+// Test that the PrepareForTestWithJavaDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithJavaDefaultModules(t *testing.T) {
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).RunTest(t)
+}
+
func TestJavaLinkType(t *testing.T) {
testJava(t, `
java_library {
@@ -619,7 +628,7 @@
}
t.Run("empty/missing directory", func(t *testing.T) {
- test(t, "empty-directory", []string{})
+ test(t, "empty-directory", nil)
})
t.Run("non-empty set of sources", func(t *testing.T) {
@@ -831,7 +840,7 @@
}
runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
- t.Run(fmt.Sprintf("%#v", info), func(t *testing.T) {
+ t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
errorHandler := android.FixtureExpectsNoErrors
if expectedErrorPattern != "" {
errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
@@ -1220,33 +1229,22 @@
}
func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
- config := TestConfig(t.TempDir(),
- nil,
- `
- java_library {
- name: "foo",
- srcs: [
- ],
- min_sdk_version: "29",
- sdk_version: "system_current",
- lint: {
- baseline_filename: "mybaseline.xml",
- },
- }
- `, map[string][]byte{
- "build/soong/java/lint_defaults.txt": nil,
- "prebuilts/cmdline-tools/tools/bin/lint": nil,
- "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar": nil,
- "framework/aidl": nil,
- "a.java": nil,
- "AndroidManifest.xml": nil,
- "build/make/target/product/security": nil,
- })
- config.TestAllowNonExistentPaths = false
- testJavaErrorWithConfig(t,
- "source path \"mybaseline.xml\" does not exist",
- config,
- )
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.PrepareForTestDisallowNonExistentPaths,
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{`source path "mybaseline.xml" does not exist`})).
+ RunTestWithBp(t, `
+ java_library {
+ name: "foo",
+ srcs: [
+ ],
+ min_sdk_version: "29",
+ sdk_version: "system_current",
+ lint: {
+ baseline_filename: "mybaseline.xml",
+ },
+ }
+ `)
}
func TestJavaLintUsesCorrectBpConfig(t *testing.T) {
@@ -1372,246 +1370,6 @@
}
}
-func TestDroiddoc(t *testing.T) {
- ctx, _ := testJavaWithFS(t, `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
- filegroup {
- name: "bar-doc-aidl-srcs",
- srcs: ["bar-doc/IBar.aidl"],
- path: "bar-doc",
- }
- droidstubs {
- name: "bar-stubs",
- srcs: [
- "bar-doc/a.java",
- ],
- exclude_srcs: [
- "bar-doc/b.java"
- ],
- api_levels_annotations_dirs: [
- "droiddoc-templates-sdk",
- ],
- api_levels_annotations_enabled: true,
- }
- droiddoc {
- name: "bar-doc",
- srcs: [
- ":bar-stubs",
- "bar-doc/IFoo.aidl",
- ":bar-doc-aidl-srcs",
- ],
- custom_template: "droiddoc-templates-sdk",
- hdf: [
- "android.whichdoc offline",
- ],
- knowntags: [
- "bar-doc/known_oj_tags.txt",
- ],
- proofread_file: "libcore-proofread.txt",
- todo_file: "libcore-docs-todo.html",
- flags: ["-offlinemode -title \"libcore\""],
- }
- `,
- map[string][]byte{
- "bar-doc/a.java": nil,
- "bar-doc/b.java": nil,
- })
- barStubs := ctx.ModuleForTests("bar-stubs", "android_common")
- barStubsOutputs, err := barStubs.Module().(*Droidstubs).OutputFiles("")
- if err != nil {
- t.Errorf("Unexpected error %q retrieving \"bar-stubs\" output file", err)
- }
- if len(barStubsOutputs) != 1 {
- t.Errorf("Expected one output from \"bar-stubs\" got %s", barStubsOutputs)
- }
-
- barStubsOutput := barStubsOutputs[0]
- barDoc := ctx.ModuleForTests("bar-doc", "android_common")
- javaDoc := barDoc.Rule("javadoc").RelativeToTop()
- if g, w := javaDoc.Implicits.Strings(), barStubsOutput.String(); !inList(w, g) {
- t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
- }
-
- expected := "-sourcepath out/soong/.intermediates/bar-doc/android_common/srcjars "
- if !strings.Contains(javaDoc.RuleParams.Command, expected) {
- t.Errorf("bar-doc command does not contain flag %q, but should\n%q", expected, javaDoc.RuleParams.Command)
- }
-
- aidl := barDoc.Rule("aidl")
- if g, w := javaDoc.Implicits.Strings(), aidl.Output.String(); !inList(w, g) {
- t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
- }
-
- if g, w := aidl.Implicits.Strings(), []string{"bar-doc/IBar.aidl", "bar-doc/IFoo.aidl"}; !reflect.DeepEqual(w, g) {
- t.Errorf("aidl inputs must be %q, but was %q", w, g)
- }
-}
-
-func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
- testJavaError(t, "flags is set. Cannot set args", `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
- filegroup {
- name: "bar-doc-aidl-srcs",
- srcs: ["bar-doc/IBar.aidl"],
- path: "bar-doc",
- }
- droidstubs {
- name: "bar-stubs",
- srcs: [
- "bar-doc/a.java",
- ],
- exclude_srcs: [
- "bar-doc/b.java"
- ],
- api_levels_annotations_dirs: [
- "droiddoc-templates-sdk",
- ],
- api_levels_annotations_enabled: true,
- }
- droiddoc {
- name: "bar-doc",
- srcs: [
- ":bar-stubs",
- "bar-doc/IFoo.aidl",
- ":bar-doc-aidl-srcs",
- ],
- custom_template: "droiddoc-templates-sdk",
- hdf: [
- "android.whichdoc offline",
- ],
- knowntags: [
- "bar-doc/known_oj_tags.txt",
- ],
- proofread_file: "libcore-proofread.txt",
- todo_file: "libcore-docs-todo.html",
- flags: ["-offlinemode -title \"libcore\""],
- args: "-offlinemode -title \"libcore\"",
- }
- `)
-}
-
-func TestDroidstubs(t *testing.T) {
- ctx, _ := testJavaWithFS(t, `
- droiddoc_exported_dir {
- name: "droiddoc-templates-sdk",
- path: ".",
- }
-
- droidstubs {
- name: "bar-stubs",
- srcs: ["bar-doc/a.java"],
- api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
- api_levels_annotations_enabled: true,
- }
-
- droidstubs {
- name: "bar-stubs-other",
- srcs: ["bar-doc/a.java"],
- high_mem: true,
- api_levels_annotations_dirs: ["droiddoc-templates-sdk"],
- api_levels_annotations_enabled: true,
- api_levels_jar_filename: "android.other.jar",
- }
- `,
- map[string][]byte{
- "bar-doc/a.java": nil,
- })
- testcases := []struct {
- moduleName string
- expectedJarFilename string
- high_mem bool
- }{
- {
- moduleName: "bar-stubs",
- expectedJarFilename: "android.jar",
- high_mem: false,
- },
- {
- moduleName: "bar-stubs-other",
- expectedJarFilename: "android.other.jar",
- high_mem: true,
- },
- }
- for _, c := range testcases {
- m := ctx.ModuleForTests(c.moduleName, "android_common")
- metalava := m.Rule("metalava")
- rp := metalava.RuleParams
- expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename
- if actual := rp.Command; !strings.Contains(actual, expected) {
- t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual)
- }
-
- if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem {
- t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual)
- }
- }
-}
-
-func TestDroidstubsWithSystemModules(t *testing.T) {
- ctx, _ := testJava(t, `
- droidstubs {
- name: "stubs-source-system-modules",
- srcs: [
- "bar-doc/a.java",
- ],
- sdk_version: "none",
- system_modules: "source-system-modules",
- }
-
- java_library {
- name: "source-jar",
- srcs: [
- "a.java",
- ],
- }
-
- java_system_modules {
- name: "source-system-modules",
- libs: ["source-jar"],
- }
-
- droidstubs {
- name: "stubs-prebuilt-system-modules",
- srcs: [
- "bar-doc/a.java",
- ],
- sdk_version: "none",
- system_modules: "prebuilt-system-modules",
- }
-
- java_import {
- name: "prebuilt-jar",
- jars: ["a.jar"],
- }
-
- java_system_modules_import {
- name: "prebuilt-system-modules",
- libs: ["prebuilt-jar"],
- }
- `)
-
- checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar")
-
- checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
-}
-
-func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
- metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
- var systemJars []string
- for _, i := range metalavaRule.Implicits {
- systemJars = append(systemJars, i.Base())
- }
- if len(systemJars) < 1 || systemJars[0] != systemJar {
- t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
- }
-}
-
func TestJarGenrules(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
diff --git a/java/lint.go b/java/lint.go
index 938e2b0..475e8dc 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -182,11 +182,6 @@
cacheDir android.WritablePath
homeDir android.WritablePath
srcjarDir android.WritablePath
-
- deps android.Paths
-
- remoteInputs android.Paths
- remoteRSPInputs android.Paths
}
func lintRBEExecStrategy(ctx android.ModuleContext) string {
@@ -194,39 +189,6 @@
}
func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
- var deps android.Paths
- var remoteInputs android.Paths
- var remoteRSPInputs android.Paths
-
- // Paths passed to trackInputDependency will be added as dependencies of the rule that runs
- // lint and passed as inputs to the remote execution proxy.
- trackInputDependency := func(paths ...android.Path) {
- deps = append(deps, paths...)
- remoteInputs = append(remoteInputs, paths...)
- }
-
- // Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
- // lint, but the RSP file will be used by the remote execution proxy to find the files so that
- // it doesn't overflow command line limits.
- trackRSPDependency := func(paths android.Paths, rsp android.Path) {
- deps = append(deps, paths...)
- remoteRSPInputs = append(remoteRSPInputs, rsp)
- }
-
- var resourcesList android.WritablePath
- if len(l.resources) > 0 {
- // The list of resources may be too long to put on the command line, but
- // we can't use the rsp file because it is already being used for srcs.
- // Insert a second rule to write out the list of resources to a file.
- resourcesList = android.PathForModuleOut(ctx, "resources.list")
- resListRule := android.NewRuleBuilder(pctx, ctx)
- resListRule.Command().Text("cp").
- FlagWithRspFileInputList("", resourcesList.ReplaceExtension(ctx, "rsp"), l.resources).
- Output(resourcesList)
- resListRule.Build("lint_resources_list", "lint resources list")
- trackRSPDependency(l.resources, resourcesList)
- }
-
projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
// Lint looks for a lint.xml file next to the project.xml file, give it one.
configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
@@ -235,20 +197,6 @@
srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
- // TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
- // by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
- // not the extracted files.
- trackRSPDependency(l.srcJars, srcJarList)
-
- // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
- // lint separately.
- srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
- srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
- rule.Command().Text("cp").
- FlagWithRspFileInputList("", srcsListRsp, l.srcs).
- Output(srcsList)
- trackRSPDependency(l.srcs, srcsList)
- rule.Temporary(srcsList)
cmd := rule.Command().
BuiltTool("lint-project-xml").
@@ -263,32 +211,31 @@
cmd.Flag("--test")
}
if l.manifest != nil {
- cmd.FlagWithArg("--manifest ", cmd.PathForInput(l.manifest))
- trackInputDependency(l.manifest)
+ cmd.FlagWithInput("--manifest ", l.manifest)
}
if l.mergedManifest != nil {
- cmd.FlagWithArg("--merged_manifest ", cmd.PathForInput(l.mergedManifest))
- trackInputDependency(l.mergedManifest)
+ cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
}
- cmd.FlagWithInput("--srcs ", srcsList)
+ // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
+ // lint separately.
+ srcsList := android.PathForModuleOut(ctx, "lint-srcs.list")
+ cmd.FlagWithRspFileInputList("--srcs ", srcsList, l.srcs)
cmd.FlagWithInput("--generated_srcs ", srcJarList)
- if resourcesList != nil {
- cmd.FlagWithInput("--resources ", resourcesList)
+ if len(l.resources) > 0 {
+ resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
+ cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
}
if l.classes != nil {
- cmd.FlagWithArg("--classes ", cmd.PathForInput(l.classes))
- trackInputDependency(l.classes)
+ cmd.FlagWithInput("--classes ", l.classes)
}
- cmd.FlagForEachArg("--classpath ", cmd.PathsForInputs(l.classpath))
- trackInputDependency(l.classpath...)
+ cmd.FlagForEachInput("--classpath ", l.classpath)
- cmd.FlagForEachArg("--extra_checks_jar ", cmd.PathsForInputs(l.extraLintCheckJars))
- trackInputDependency(l.extraLintCheckJars...)
+ cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
cmd.FlagWithArg("--root_dir ", "$PWD")
@@ -309,11 +256,6 @@
configXML: configXMLPath,
cacheDir: cacheDir,
homeDir: homeDir,
-
- deps: deps,
-
- remoteInputs: remoteInputs,
- remoteRSPInputs: remoteRSPInputs,
}
}
@@ -424,8 +366,7 @@
Flag("--exitcode").
Flags(l.properties.Lint.Flags).
Implicit(annotationsZipPath).
- Implicit(apiVersionsXMLPath).
- Implicits(lintPaths.deps)
+ Implicit(apiVersionsXMLPath)
rule.Temporary(lintPaths.projectXML)
rule.Temporary(lintPaths.configXML)
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 03e82c2..c3d13ae 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -221,12 +221,40 @@
metadata android.Path
}
+// isModulePreferredByCompatConfig checks to see whether the module is preferred for use by
+// platform compat config.
+func isModulePreferredByCompatConfig(module android.Module) bool {
+ // A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
+ // ignored.
+ if s, ok := module.(android.SdkAware); ok {
+ if !s.ContainingSdk().Unversioned() {
+ return false
+ }
+ }
+
+ // A prebuilt module should only be used when it is preferred.
+ if pi, ok := module.(android.PrebuiltInterface); ok {
+ if p := pi.Prebuilt(); p != nil {
+ return p.UsePrebuilt()
+ }
+ }
+
+ // Otherwise, a module should only be used if it has not been replaced by a prebuilt.
+ return !module.IsReplacedByPrebuilt()
+}
+
func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var compatConfigMetadata android.Paths
ctx.VisitAllModules(func(module android.Module) {
+ if !module.Enabled() {
+ return
+ }
if c, ok := module.(platformCompatConfigMetadataProvider); ok {
+ if !isModulePreferredByCompatConfig(module) {
+ return
+ }
metadata := c.compatConfigMetadata()
compatConfigMetadata = append(compatConfigMetadata, metadata)
}
diff --git a/java/testing.go b/java/testing.go
index 295b8d0..221ceb1 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -45,12 +45,29 @@
// Make java build components available to the test.
android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
android.FixtureRegisterWithContext(registerJavaPluginBuildComponents),
+ // Additional files needed in tests that disallow non-existent source files.
+ // This includes files that are needed by all, or at least most, instances of a java module type.
+ android.MockFS{
+ // Needed for linter used by java_library.
+ "build/soong/java/lint_defaults.txt": nil,
+ // Needed for apps that do not provide their own.
+ "build/make/target/product/security": nil,
+ }.AddToFixture(),
)
// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers(
// Make sure that all the module types used in the defaults are registered.
PrepareForTestWithJavaBuildComponents,
+ // Additional files needed when test disallows non-existent source.
+ android.MockFS{
+ // Needed for framework-res
+ defaultJavaDir + "/AndroidManifest.xml": nil,
+ // Needed for framework
+ defaultJavaDir + "/framework/aidl": nil,
+ // Needed for various deps defined in GatherRequiredDepsForTest()
+ defaultJavaDir + "/a.java": nil,
+ }.AddToFixture(),
// The java default module definitions.
android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
// Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
diff --git a/python/binary.go b/python/binary.go
index 6061ad4..5b0f080 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -61,7 +61,7 @@
func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index 166f68c..ef4672a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -64,9 +64,8 @@
ExecStrategy string
// Inputs is a list of input paths or ninja variables.
Inputs []string
- // RSPFile is the name of the ninja variable used by the rule as a placeholder for an rsp
- // input.
- RSPFile string
+ // RSPFiles is the name of the files used by the rule as a placeholder for an rsp input.
+ RSPFiles []string
// OutputFiles is a list of output file paths or ninja variables as placeholders for rule
// outputs.
OutputFiles []string
@@ -134,8 +133,8 @@
args += " --inputs=" + strings.Join(r.Inputs, ",")
}
- if r.RSPFile != "" {
- args += " --input_list_paths=" + r.RSPFile
+ if len(r.RSPFiles) > 0 {
+ args += " --input_list_paths=" + strings.Join(r.RSPFiles, ",")
}
if len(r.OutputFiles) > 0 {
diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go
index 875aa6a..b117b89 100644
--- a/remoteexec/remoteexec_test.go
+++ b/remoteexec/remoteexec_test.go
@@ -45,14 +45,14 @@
Inputs: []string{"$in"},
OutputFiles: []string{"$out"},
ExecStrategy: "remote",
- RSPFile: "$out.rsp",
+ RSPFiles: []string{"$out.rsp", "out2.rsp"},
ToolchainInputs: []string{"clang++"},
Platform: map[string]string{
ContainerImageKey: DefaultImage,
PoolKey: "default",
},
},
- want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
+ want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
},
}
for _, test := range tests {
diff --git a/response/Android.bp b/response/Android.bp
new file mode 100644
index 0000000..e19981f
--- /dev/null
+++ b/response/Android.bp
@@ -0,0 +1,16 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-response",
+ pkgPath: "android/soong/response",
+ deps: [
+ ],
+ srcs: [
+ "response.go",
+ ],
+ testSrcs: [
+ "response_test.go",
+ ],
+}
diff --git a/response/response.go b/response/response.go
new file mode 100644
index 0000000..b65503e
--- /dev/null
+++ b/response/response.go
@@ -0,0 +1,112 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package response
+
+import (
+ "io"
+ "io/ioutil"
+ "strings"
+ "unicode"
+)
+
+const noQuote = '\x00'
+
+// ReadRspFile reads a file in Ninja's response file format and returns its contents.
+func ReadRspFile(r io.Reader) ([]string, error) {
+ var files []string
+ var file []byte
+
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ return nil, err
+ }
+
+ isEscaping := false
+ quotingStart := byte(noQuote)
+ for _, c := range buf {
+ switch {
+ case isEscaping:
+ if quotingStart == '"' {
+ if !(c == '"' || c == '\\') {
+ // '\"' or '\\' will be escaped under double quoting.
+ file = append(file, '\\')
+ }
+ }
+ file = append(file, c)
+ isEscaping = false
+ case c == '\\' && quotingStart != '\'':
+ isEscaping = true
+ case quotingStart == noQuote && (c == '\'' || c == '"'):
+ quotingStart = c
+ case quotingStart != noQuote && c == quotingStart:
+ quotingStart = noQuote
+ case quotingStart == noQuote && unicode.IsSpace(rune(c)):
+ // Current character is a space outside quotes
+ if len(file) != 0 {
+ files = append(files, string(file))
+ }
+ file = file[:0]
+ default:
+ file = append(file, c)
+ }
+ }
+
+ if len(file) != 0 {
+ files = append(files, string(file))
+ }
+
+ return files, nil
+}
+
+func rspUnsafeChar(r rune) bool {
+ switch {
+ case 'A' <= r && r <= 'Z',
+ 'a' <= r && r <= 'z',
+ '0' <= r && r <= '9',
+ r == '_',
+ r == '+',
+ r == '-',
+ r == '.',
+ r == '/':
+ return false
+ default:
+ return true
+ }
+}
+
+var rspEscaper = strings.NewReplacer(`'`, `'\''`)
+
+// WriteRspFile writes a list of files to a file in Ninja's response file format.
+func WriteRspFile(w io.Writer, files []string) error {
+ for i, f := range files {
+ if i != 0 {
+ _, err := io.WriteString(w, " ")
+ if err != nil {
+ return err
+ }
+ }
+
+ if strings.IndexFunc(f, rspUnsafeChar) != -1 {
+ f = `'` + rspEscaper.Replace(f) + `'`
+ }
+
+ _, err := io.WriteString(w, f)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/response/response_test.go b/response/response_test.go
new file mode 100644
index 0000000..4d1fb41
--- /dev/null
+++ b/response/response_test.go
@@ -0,0 +1,123 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package response
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func TestReadRspFile(t *testing.T) {
+ testCases := []struct {
+ name, in string
+ out []string
+ }{
+ {
+ name: "single quoting test case 1",
+ in: `./cmd '"'-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "single quoting test case 2",
+ in: `./cmd '-C`,
+ out: []string{"./cmd", `-C`},
+ },
+ {
+ name: "single quoting test case 3",
+ in: `./cmd '\"'-C`,
+ out: []string{"./cmd", `\"-C`},
+ },
+ {
+ name: "single quoting test case 4",
+ in: `./cmd '\\'-C`,
+ out: []string{"./cmd", `\\-C`},
+ },
+ {
+ name: "none quoting test case 1",
+ in: `./cmd \'-C`,
+ out: []string{"./cmd", `'-C`},
+ },
+ {
+ name: "none quoting test case 2",
+ in: `./cmd \\-C`,
+ out: []string{"./cmd", `\-C`},
+ },
+ {
+ name: "none quoting test case 3",
+ in: `./cmd \"-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "double quoting test case 1",
+ in: `./cmd "'"-C`,
+ out: []string{"./cmd", `'-C`},
+ },
+ {
+ name: "double quoting test case 2",
+ in: `./cmd "\\"-C`,
+ out: []string{"./cmd", `\-C`},
+ },
+ {
+ name: "double quoting test case 3",
+ in: `./cmd "\""-C`,
+ out: []string{"./cmd", `"-C`},
+ },
+ {
+ name: "ninja rsp file",
+ in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
+ out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ got, err := ReadRspFile(bytes.NewBuffer([]byte(testCase.in)))
+ if err != nil {
+ t.Errorf("unexpected error: %q", err)
+ }
+ if !reflect.DeepEqual(got, testCase.out) {
+ t.Errorf("expected %q got %q", testCase.out, got)
+ }
+ })
+ }
+}
+
+func TestWriteRspFile(t *testing.T) {
+ testCases := []struct {
+ name string
+ in []string
+ out string
+ }{
+ {
+ name: "ninja rsp file",
+ in: []string{"a", "b", "@", "foo'bar", `foo"bar`},
+ out: "a b '@' 'foo'\\''bar' 'foo\"bar'",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ buf := &bytes.Buffer{}
+ err := WriteRspFile(buf, testCase.in)
+ if err != nil {
+ t.Errorf("unexpected error: %q", err)
+ }
+ if buf.String() != testCase.out {
+ t.Errorf("expected %q got %q", testCase.out, buf.String())
+ }
+ })
+ }
+}
diff --git a/rust/compiler.go b/rust/compiler.go
index 200af90..41b7371 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -97,13 +97,25 @@
// list of C shared library dependencies
Shared_libs []string `android:"arch_variant"`
- // list of C static library dependencies. Note, static libraries prefixed by "lib" will be passed to rustc
- // along with "-lstatic=<name>". This will bundle the static library into rlib/static libraries so dependents do
- // not need to also declare the static library as a dependency. Static libraries which are not prefixed by "lib"
- // cannot be passed to rustc with this flag and will not be bundled into rlib/static libraries, and thus must
- // be redeclared in dependents.
+ // list of C static library dependencies. These dependencies do not normally propagate to dependents
+ // and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library.
Static_libs []string `android:"arch_variant"`
+ // Similar to static_libs, but will bundle the static library dependency into a library. This is helpful
+ // to avoid having to redeclare the dependency for dependents of this library, but in some cases may also
+ // result in bloat if multiple dependencies all include the same static library whole.
+ //
+ // The common use case for this is when the static library is unlikely to be a dependency of other modules to avoid
+ // having to redeclare the static library dependency for every dependent module.
+ // If you are not sure what to, for rust_library modules most static dependencies should go in static_libraries,
+ // and for rust_ffi modules most static dependencies should go into whole_static_libraries.
+ //
+ // For rust_ffi static variants, these libraries will be included in the resulting static library archive.
+ //
+ // For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will
+ // include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
+ Whole_static_libs []string `android:"arch_variant"`
+
// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -266,6 +278,7 @@
deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
if !Bool(compiler.Properties.No_stdlibs) {
diff --git a/rust/rust.go b/rust/rust.go
index 8ebdb72..f0d0e36 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -256,6 +256,10 @@
return ""
}
+func (mod *Module) MinSdkVersion() string {
+ return ""
+}
+
func (mod *Module) AlwaysSdk() bool {
return false
}
@@ -269,14 +273,15 @@
}
type Deps struct {
- Dylibs []string
- Rlibs []string
- Rustlibs []string
- Stdlibs []string
- ProcMacros []string
- SharedLibs []string
- StaticLibs []string
- HeaderLibs []string
+ Dylibs []string
+ Rlibs []string
+ Rustlibs []string
+ Stdlibs []string
+ ProcMacros []string
+ SharedLibs []string
+ StaticLibs []string
+ WholeStaticLibs []string
+ HeaderLibs []string
CrtBegin, CrtEnd string
}
@@ -751,7 +756,7 @@
deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
-
+ deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
return deps
}
@@ -911,16 +916,13 @@
exportDep := false
switch {
case cc.IsStaticDepTag(depTag):
- // Only pass -lstatic for rlibs as it results in dylib bloat.
- if lib, ok := ctx.Module().(*Module).compiler.(libraryInterface); ok && lib.rlib() {
- // Link cc static libraries using "-lstatic" so rustc can reason about how to handle these
- // (for example, bundling them into rlibs).
- //
- // rustc does not support linking libraries with the "-l" flag unless they are prefixed by "lib".
- // If we need to link a library that isn't prefixed by "lib", we'll just link to it directly through
- // linkObjects; such a library may need to be redeclared by static dependents.
+ if cc.IsWholeStaticLib(depTag) {
+ // rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
+ // if the library is not prefixed by "lib".
if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+ } else {
+ ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
}
}
@@ -1099,7 +1101,10 @@
cc.SharedDepTag(), deps.SharedLibs...)
actx.AddVariationDependencies(append(commonDepVariations,
blueprint.Variation{Mutator: "link", Variation: "static"}),
- cc.StaticDepTag(), deps.StaticLibs...)
+ cc.StaticDepTag(false), deps.StaticLibs...)
+ actx.AddVariationDependencies(append(commonDepVariations,
+ blueprint.Variation{Mutator: "link", Variation: "static"}),
+ cc.StaticDepTag(true), deps.WholeStaticLibs...)
actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index bed28ec..418bd93 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -149,6 +149,11 @@
srcs: ["foo.rs"],
crate_name: "static",
}
+ rust_ffi_host_static {
+ name: "libwholestatic",
+ srcs: ["foo.rs"],
+ crate_name: "wholestatic",
+ }
rust_ffi_host_shared {
name: "libshared",
srcs: ["foo.rs"],
@@ -164,6 +169,7 @@
srcs: ["foo.rs"],
crate_name: "rlib",
static_libs: ["libstatic"],
+ whole_static_libs: ["libwholestatic"],
}
rust_proc_macro {
name: "libpm",
@@ -204,8 +210,8 @@
t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
}
- if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=static") {
- t.Errorf("-lstatic flag not being passed to rustc for static library")
+ if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
+ t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
}
}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9626a04..a886a18 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -486,6 +486,9 @@
}
`)
+ // TODO(b/183322862): Remove this and fix the issue.
+ errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
+
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -518,6 +521,9 @@
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
`),
+ snapshotTestErrorHandler(checkSnapshotWithoutSource, errorHandler),
+ snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, errorHandler),
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, errorHandler),
)
}
@@ -1816,7 +1822,10 @@
.intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
.intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
.intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+ // TODO(b/183315522): Remove this and fix the issue.
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\Qunrecognized property "arch.arm.shared.export_include_dirs"\E`)),
)
}
@@ -2664,6 +2673,11 @@
}
`)
+ // Mixing the snapshot with the source (irrespective of which one is preferred) causes a problem
+ // due to missing variants.
+ // TODO(b/183204176): Remove this and fix the cause.
+ snapshotWithSourceErrorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\QReplaceDependencies could not find identical variant {os:android,image:,arch:arm64_armv8-a,sdk:,link:shared,version:} for module mynativelib\E`)
+
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -2688,6 +2702,9 @@
checkAllCopyRules(`
myinclude/Test.h -> include/myinclude/Test.h
arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+ snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, snapshotWithSourceErrorHandler),
+ snapshotTestErrorHandler(checkSnapshotPreferredWithSource, snapshotWithSourceErrorHandler),
)
}
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index dffc02a..00073c2 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -66,5 +66,26 @@
checkAllCopyRules(`
.intermediates/myconfig/android_common/myconfig_meta.xml -> compat_configs/myconfig/myconfig_meta.xml
`),
+ snapshotTestChecker(checkSnapshotWithoutSource,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module", "snapshot/compat_configs/myconfig/myconfig_meta.xml")
+ }),
+
+ snapshotTestChecker(checkSnapshotWithSourcePreferred,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+ "out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml",
+ )
+ }),
+
+ snapshotTestChecker(checkSnapshotPreferredWithSource,
+ func(t *testing.T, result *android.TestResult) {
+ // Make sure that the snapshot metadata is collated by the platform compat config singleton.
+ java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+ "snapshot/compat_configs/myconfig/myconfig_meta.xml",
+ )
+ }),
)
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 2be3c9c..208cd58 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -24,93 +24,29 @@
var prepareForSdkTestWithJava = android.GroupFixturePreparers(
java.PrepareForTestWithJavaBuildComponents,
PrepareForTestWithSdkBuildComponents,
+
+ // Ensure that all source paths are provided. This helps ensure that the snapshot generation is
+ // consistent and all files referenced from the snapshot's Android.bp file have actually been
+ // copied into the snapshot.
+ android.PrepareForTestDisallowNonExistentPaths,
+
+ // Files needs by most of the tests.
+ android.MockFS{
+ "Test.java": nil,
+ }.AddToFixture(),
)
-func testSdkWithJava(t *testing.T, bp string) *android.TestResult {
- t.Helper()
-
- fs := map[string][]byte{
- "Test.java": nil,
- "resource.test": nil,
- "aidl/foo/bar/Test.aidl": nil,
-
- // For java_import
- "prebuilt.jar": nil,
-
- // For java_sdk_library
- "api/current.txt": nil,
- "api/removed.txt": nil,
- "api/system-current.txt": nil,
- "api/system-removed.txt": nil,
- "api/test-current.txt": nil,
- "api/test-removed.txt": nil,
- "api/module-lib-current.txt": nil,
- "api/module-lib-removed.txt": nil,
- "api/system-server-current.txt": nil,
- "api/system-server-removed.txt": nil,
- "build/soong/scripts/gen-java-current-api-files.sh": nil,
- "docs/known_doctags": nil,
- "100/public/api/myjavalib.txt": nil,
- "100/public/api/myjavalib-removed.txt": nil,
- "100/system/api/myjavalib.txt": nil,
- "100/system/api/myjavalib-removed.txt": nil,
- "100/module-lib/api/myjavalib.txt": nil,
- "100/module-lib/api/myjavalib-removed.txt": nil,
- "100/system-server/api/myjavalib.txt": nil,
- "100/system-server/api/myjavalib-removed.txt": nil,
- }
-
- // for java_sdk_library tests
- bp = `
-java_system_modules_import {
- name: "core-current-stubs-system-modules",
-}
-java_system_modules_import {
- name: "stable-core-platform-api-stubs-system-modules",
-}
-java_import {
- name: "stable.core.platform.api.stubs",
-}
-java_import {
- name: "android_stubs_current",
-}
-java_import {
- name: "android_system_stubs_current",
-}
-java_import {
- name: "android_test_stubs_current",
-}
-java_import {
- name: "android_module_lib_stubs_current",
-}
-java_import {
- name: "android_system_server_stubs_current",
-}
-java_import {
- name: "core-lambda-stubs",
- sdk_version: "none",
-}
-java_import {
- name: "ext",
- sdk_version: "none",
-}
-java_import {
- name: "framework",
- sdk_version: "none",
-}
-prebuilt_apis {
- name: "sdk",
- api_dirs: ["100"],
-}
-` + bp
-
- return testSdkWithFs(t, bp, fs)
-}
+var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("myjavalib"),
+)
// Contains tests for SDK members provided by the java package.
func TestSdkDependsOnSourceEvenWhenPrebuiltPreferred(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["sdkmember"],
@@ -122,46 +58,24 @@
system_modules: "none",
sdk_version: "none",
}
-
- java_import {
- name: "sdkmember",
- prefer: true,
- jars: ["prebuilt.jar"],
- }
`)
// Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
- java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+ sdkChecker := func(t *testing.T, result *android.TestResult) {
+ java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+ }
CheckSnapshot(t, result, "mysdk", "",
- checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
-
-java_import {
- name: "mysdk_sdkmember@current",
- sdk_member_name: "sdkmember",
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/sdkmember.jar"],
-}
-
-java_import {
- name: "sdkmember",
- prefer: false,
- visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
- jars: ["java/sdkmember.jar"],
-}
-
-sdk_snapshot {
- name: "mysdk@current",
- visibility: ["//visibility:public"],
- java_header_libs: ["mysdk_sdkmember@current"],
-}
-`))
+ snapshotTestChecker(checkSnapshotWithSourcePreferred, sdkChecker),
+ snapshotTestChecker(checkSnapshotPreferredWithSource, sdkChecker),
+ )
}
func TestBasicSdkWithJavaLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ prepareForSdkTestWithApex,
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["sdkmember"],
@@ -242,7 +156,10 @@
}
func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["myjavalib"],
@@ -296,7 +213,10 @@
}
func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
device_supported: false,
@@ -358,7 +278,7 @@
}
func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
host_supported: true,
@@ -426,7 +346,11 @@
}
func TestSnapshotWithJavaImplLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ android.FixtureAddFile("resource.txt", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_libs: ["myjavalib"],
@@ -481,7 +405,11 @@
}
func TestSnapshotWithJavaBootLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl", nil),
+ android.FixtureAddFile("resource.txt", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_boot_libs: ["myjavalib"],
@@ -535,7 +463,10 @@
}
func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+ ).RunTestWithBp(t, `
module_exports {
name: "myexports",
device_supported: false,
@@ -597,7 +528,7 @@
}
func TestSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
java_tests: ["myjavatests"],
@@ -649,7 +580,7 @@
}
func TestHostSnapshotWithJavaTest(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
device_supported: false,
@@ -710,7 +641,7 @@
}
func TestSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_header_libs: ["exported-system-module"],
@@ -808,7 +739,7 @@
}
func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
sdk {
name: "mysdk",
device_supported: false,
@@ -888,7 +819,7 @@
}
func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
module_exports {
name: "myexports",
host_supported: true,
@@ -1021,7 +952,7 @@
}
func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1108,14 +1039,14 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
.intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
-.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
+.intermediates/myjavalib.stubs.source.test/android_common/metalava/myjavalib.stubs.source.test_removed.txt -> sdk_library/test/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1125,7 +1056,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1181,8 +1112,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1191,7 +1122,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1250,8 +1181,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1260,7 +1191,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1336,11 +1267,11 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1350,7 +1281,7 @@
}
func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1443,14 +1374,14 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/metalava/myjavalib.stubs.source.system_removed.txt -> sdk_library/system/myjavalib-removed.txt
.intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.stubs.source.module_lib/android_common/metalava/myjavalib.stubs.source.module_lib_removed.txt -> sdk_library/module-lib/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1461,7 +1392,7 @@
}
func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1537,11 +1468,11 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
.intermediates/myjavalib.stubs.system_server/android_common/javac/myjavalib.stubs.system_server.jar -> sdk_library/system-server/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
-.intermediates/myjavalib.stubs.source.system_server/android_common/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_api.txt -> sdk_library/system-server/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system_server/android_common/metalava/myjavalib.stubs.source.system_server_removed.txt -> sdk_library/system-server/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1551,7 +1482,7 @@
}
func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1613,8 +1544,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
`),
checkMergeZips(
".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
@@ -1623,7 +1554,10 @@
}
func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
- result := testSdkWithJava(t, `
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJavaSdkLibrary,
+ android.FixtureAddFile("docs/known_doctags", nil),
+ ).RunTestWithBp(t, `
sdk {
name: "mysdk",
java_sdk_libs: ["myjavalib"],
@@ -1689,8 +1623,8 @@
`),
checkAllCopyRules(`
.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
-.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
docs/known_doctags -> doctags/docs/known_doctags
`),
)
diff --git a/sdk/sdk.go b/sdk/sdk.go
index e561529..b60fb18 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -486,6 +486,15 @@
// sdk containing sdkmember.
memberName := versionedSdkMember.MemberName()
+ // Convert a panic into a normal error to allow it to be more easily tested for. This is a
+ // temporary workaround, once http://b/183204176 has been fixed this can be removed.
+ // TODO(b/183204176): Remove this after fixing.
+ defer func() {
+ if r := recover(); r != nil {
+ mctx.ModuleErrorf("%s", r)
+ }
+ }()
+
// Replace dependencies on sdkmember with a dependency on the current module which
// is a versioned prebuilt of the sdkmember if required.
mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
diff --git a/sdk/testing.go b/sdk/testing.go
index d21f425..44970f7 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -44,15 +44,14 @@
`),
android.FixtureMergeMockFs(map[string][]byte{
- "build/make/target/product/security": nil,
"apex_manifest.json": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/myapex2-file_contexts": nil,
"system/sepolicy/apex/mysdkapex-file_contexts": nil,
- "myapex.avbpubkey": nil,
- "myapex.pem": nil,
- "myapex.x509.pem": nil,
- "myapex.pk8": nil,
+ "sdk/tests/myapex.avbpubkey": nil,
+ "sdk/tests/myapex.pem": nil,
+ "sdk/tests/myapex.x509.pem": nil,
+ "sdk/tests/myapex.pk8": nil,
}),
)
@@ -81,6 +80,12 @@
{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
}
}),
+
+ // Make sure that every test provides all the source files.
+ android.PrepareForTestDisallowNonExistentPaths,
+ android.MockFS{
+ "Test.java": nil,
+ }.AddToFixture(),
)
var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
@@ -126,6 +131,7 @@
androidBpContents: sdk.GetAndroidBpContentsForTests(),
androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
androidVersionedBpContents: sdk.GetVersionedAndroidBpContentsForTests(),
+ snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
}
buildParams := sdk.BuildParamsForTests()
@@ -184,6 +190,24 @@
return info
}
+// The enum of different sdk snapshot tests performed by CheckSnapshot.
+type snapshotTest int
+
+const (
+ // The enumeration of the different test configurations.
+ // A test with the snapshot/Android.bp file but without the original Android.bp file.
+ checkSnapshotWithoutSource snapshotTest = iota
+
+ // A test with both the original source and the snapshot, with the source preferred.
+ checkSnapshotWithSourcePreferred
+
+ // A test with both the original source and the snapshot, with the snapshot preferred.
+ checkSnapshotPreferredWithSource
+
+ // The directory into which the snapshot will be 'unpacked'.
+ snapshotSubDir = "snapshot"
+)
+
// Check the snapshot build rules.
//
// Takes a list of functions which check different facets of the snapshot build rules.
@@ -215,31 +239,58 @@
// Populate a mock filesystem with the files that would have been copied by
// the rules.
fs := android.MockFS{}
- snapshotSubDir := "snapshot"
for _, dest := range snapshotBuildInfo.snapshotContents {
fs[filepath.Join(snapshotSubDir, dest)] = nil
}
fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
- preparer := result.Preparer()
+ // The preparers from the original source fixture.
+ sourcePreparers := result.Preparer()
- // Process the generated bp file to make sure it is valid. Use the same preparer as was used to
- // produce this result.
+ // Preparer to combine the snapshot and the source.
+ snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
+
+ var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
+ customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
+
+ // TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
+ // files the snapshot needs are actually copied into the snapshot.
+
+ // Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
+ // it may need to modify parts of the MockFS populated by the snapshot preparer.
+ result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer).
+ ExtendWithErrorHandler(customization.errorHandler).
+ RunTest(t)
+
+ // Perform any additional checks the test need on the result of processing the snapshot.
+ for _, checker := range customization.checkers {
+ checker(t, result)
+ }
+ }
+
t.Run("snapshot without source", func(t *testing.T) {
- android.GroupFixturePreparers(
- preparer,
- // TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
- // files the snapshot needs are actually copied into the snapshot.
+ // Remove the source Android.bp file to make sure it works without.
+ removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
+ delete(fs, "Android.bp")
+ })
- // Add the files (including bp) created for this snapshot to the test fixture.
- fs.AddToFixture(),
+ runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
+ })
- // Remove the source Android.bp file to make sure it works without.
- // TODO(b/183184375): Add a test with the source.
- android.FixtureModifyMockFS(func(fs android.MockFS) {
- delete(fs, "Android.bp")
- }),
- ).RunTest(t)
+ t.Run("snapshot with source preferred", func(t *testing.T) {
+ runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
+ })
+
+ t.Run("snapshot preferred with source", func(t *testing.T) {
+ // Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
+ // "prefer: true,"
+ preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
+ snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
+ unpreferred := string(fs[snapshotBpFile])
+ fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
+ })
+
+ runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
})
}
@@ -313,6 +364,46 @@
}
}
+type resultChecker func(t *testing.T, result *android.TestResult)
+
+// snapshotTestChecker registers a checker that will be run against the result of processing the
+// generated snapshot for the specified snapshotTest.
+func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ customization := info.snapshotTestCustomization(snapshotTest)
+ customization.checkers = append(customization.checkers, checker)
+ }
+}
+
+// snapshotTestErrorHandler registers an error handler to use when processing the snapshot
+// in the specific test case.
+//
+// Generally, the snapshot should work with all the test cases but some do not and just in case
+// there are a lot of issues to resolve, or it will take a lot of time this is a
+// get-out-of-jail-free card that allows progress to be made.
+//
+// deprecated: should only be used as a temporary workaround with an attached to do and bug.
+func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker {
+ return func(info *snapshotBuildInfo) {
+ customization := info.snapshotTestCustomization(snapshotTest)
+ customization.errorHandler = handler
+ }
+}
+
+// Encapsulates information provided by each test to customize a specific snapshotTest.
+type snapshotTestCustomization struct {
+ // Checkers that are run on the result of processing the preferred snapshot in a specific test
+ // case.
+ checkers []resultChecker
+
+ // Specify an error handler for when processing a specific test case.
+ //
+ // In some cases the generated snapshot cannot be used in a test configuration. Those cases are
+ // invariably bugs that need to be resolved but sometimes that can take a while. This provides a
+ // mechanism to temporarily ignore that error.
+ errorHandler android.FixtureErrorHandler
+}
+
// Encapsulates information about the snapshot build structure in order to insulate tests from
// knowing too much about internal structures.
//
@@ -356,4 +447,21 @@
// The final output zip.
outputZip string
+
+ // The test specific customizations for each snapshot test.
+ snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
+}
+
+// snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
+//
+// If no customization was created previously then it creates a default customization.
+func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
+ customization := i.snapshotTestCustomizations[snapshotTest]
+ if customization == nil {
+ customization = &snapshotTestCustomization{
+ errorHandler: android.FixtureExpectsNoErrors,
+ }
+ i.snapshotTestCustomizations[snapshotTest] = customization
+ }
+ return customization
}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 49f4961..1ae557a 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -521,7 +521,7 @@
func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*ShBinary)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
diff --git a/zip/Android.bp b/zip/Android.bp
index b28adbd..14541eb 100644
--- a/zip/Android.bp
+++ b/zip/Android.bp
@@ -25,6 +25,7 @@
"android-archive-zip",
"blueprint-pathtools",
"soong-jar",
+ "soong-response",
],
srcs: [
"zip.go",
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index fc976f6..cbc73ed 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -24,7 +24,6 @@
import (
"flag"
"fmt"
- "io/ioutil"
"os"
"runtime"
"runtime/pprof"
@@ -32,6 +31,7 @@
"strconv"
"strings"
+ "android/soong/response"
"android/soong/zip"
)
@@ -125,12 +125,18 @@
var expandedArgs []string
for _, arg := range os.Args {
if strings.HasPrefix(arg, "@") {
- bytes, err := ioutil.ReadFile(strings.TrimPrefix(arg, "@"))
+ f, err := os.Open(strings.TrimPrefix(arg, "@"))
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
- respArgs := zip.ReadRespFile(bytes)
+
+ respArgs, err := response.ReadRspFile(f)
+ f.Close()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ os.Exit(1)
+ }
expandedArgs = append(expandedArgs, respArgs...)
} else {
expandedArgs = append(expandedArgs, arg)
diff --git a/zip/zip.go b/zip/zip.go
index 088ed0d..a6490d4 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -29,7 +29,8 @@
"sync"
"syscall"
"time"
- "unicode"
+
+ "android/soong/response"
"github.com/google/blueprint/pathtools"
@@ -164,14 +165,12 @@
}
defer f.Close()
- list, err := ioutil.ReadAll(f)
+ arg := b.state
+ arg.SourceFiles, err = response.ReadRspFile(f)
if err != nil {
b.err = err
return b
}
-
- arg := b.state
- arg.SourceFiles = ReadRespFile(list)
for i := range arg.SourceFiles {
arg.SourceFiles[i] = pathtools.MatchEscape(arg.SourceFiles[i])
}
@@ -253,49 +252,6 @@
Filesystem pathtools.FileSystem
}
-const NOQUOTE = '\x00'
-
-func ReadRespFile(bytes []byte) []string {
- var args []string
- var arg []rune
-
- isEscaping := false
- quotingStart := NOQUOTE
- for _, c := range string(bytes) {
- switch {
- case isEscaping:
- if quotingStart == '"' {
- if !(c == '"' || c == '\\') {
- // '\"' or '\\' will be escaped under double quoting.
- arg = append(arg, '\\')
- }
- }
- arg = append(arg, c)
- isEscaping = false
- case c == '\\' && quotingStart != '\'':
- isEscaping = true
- case quotingStart == NOQUOTE && (c == '\'' || c == '"'):
- quotingStart = c
- case quotingStart != NOQUOTE && c == quotingStart:
- quotingStart = NOQUOTE
- case quotingStart == NOQUOTE && unicode.IsSpace(c):
- // Current character is a space outside quotes
- if len(arg) != 0 {
- args = append(args, string(arg))
- }
- arg = arg[:0]
- default:
- arg = append(arg, c)
- }
- }
-
- if len(arg) != 0 {
- args = append(args, string(arg))
- }
-
- return args
-}
-
func zipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
diff --git a/zip/zip_test.go b/zip/zip_test.go
index b456ef8..a37ae41 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -535,78 +535,6 @@
}
}
-func TestReadRespFile(t *testing.T) {
- testCases := []struct {
- name, in string
- out []string
- }{
- {
- name: "single quoting test case 1",
- in: `./cmd '"'-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "single quoting test case 2",
- in: `./cmd '-C`,
- out: []string{"./cmd", `-C`},
- },
- {
- name: "single quoting test case 3",
- in: `./cmd '\"'-C`,
- out: []string{"./cmd", `\"-C`},
- },
- {
- name: "single quoting test case 4",
- in: `./cmd '\\'-C`,
- out: []string{"./cmd", `\\-C`},
- },
- {
- name: "none quoting test case 1",
- in: `./cmd \'-C`,
- out: []string{"./cmd", `'-C`},
- },
- {
- name: "none quoting test case 2",
- in: `./cmd \\-C`,
- out: []string{"./cmd", `\-C`},
- },
- {
- name: "none quoting test case 3",
- in: `./cmd \"-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "double quoting test case 1",
- in: `./cmd "'"-C`,
- out: []string{"./cmd", `'-C`},
- },
- {
- name: "double quoting test case 2",
- in: `./cmd "\\"-C`,
- out: []string{"./cmd", `\-C`},
- },
- {
- name: "double quoting test case 3",
- in: `./cmd "\""-C`,
- out: []string{"./cmd", `"-C`},
- },
- {
- name: "ninja rsp file",
- in: "'a'\nb\n'@'\n'foo'\\''bar'\n'foo\"bar'",
- out: []string{"a", "b", "@", "foo'bar", `foo"bar`},
- },
- }
-
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- got := ReadRespFile([]byte(testCase.in))
- if !reflect.DeepEqual(got, testCase.out) {
- t.Errorf("expected %q got %q", testCase.out, got)
- }
- })
- }
-}
-
func TestSrcJar(t *testing.T) {
mockFs := pathtools.MockFs(map[string][]byte{
"wrong_package.java": []byte("package foo;"),