Merge ""current" is implicitly added to stubs.versions"
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/android_test.go b/android/android_test.go
index 2a697fb..fb82e37 100644
--- a/android/android_test.go
+++ b/android/android_test.go
@@ -22,5 +22,3 @@
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
-
-var emptyTestFixtureFactory = NewFixtureFactory(nil)
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 230b1ec..8eda9b2 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -141,14 +141,14 @@
// bp module and then returns the config and the custom module called "foo".
func buildContextAndCustomModuleFoo(t *testing.T, bp string) (*TestContext, *customModule) {
t.Helper()
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
// Enable androidmk Singleton
PrepareForTestWithAndroidMk,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("custom", customModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
module := result.ModuleForTests("foo", "").Module().(*customModule)
return result.TestContext, module
diff --git a/android/apex.go b/android/apex.go
index 0d5cac8..257bdad 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -140,9 +140,24 @@
// DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
// this module. For example, a static lib dependency usually returns true here, while a
// shared lib dependency to a stub library returns false.
+ //
+ // This method must not be called directly without first ignoring dependencies whose tags
+ // implement ExcludeFromApexContentsTag. Calls from within the func passed to WalkPayloadDeps()
+ // are fine as WalkPayloadDeps() will ignore those dependencies automatically. Otherwise, use
+ // IsDepInSameApex instead.
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
}
+func IsDepInSameApex(ctx BaseModuleContext, module, dep Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ if _, ok := depTag.(ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the same
+ // apex as the parent.
+ return false
+ }
+ return module.(DepIsInSameApex).DepIsInSameApex(ctx, dep)
+}
+
// ApexModule is the interface that a module type is expected to implement if the module has to be
// built differently depending on whether the module is destined for an APEX or not (i.e., installed
// to one of the regular partitions).
@@ -260,6 +275,10 @@
//
// Unless the tag also implements the AlwaysRequireApexVariantTag this will prevent an apex variant
// from being created for the module.
+//
+// At the moment the sdk.sdkRequirementsMutator relies on the fact that the existing tags which
+// implement this interface do not define dependencies onto members of an sdk_snapshot. If that
+// changes then sdk.sdkRequirementsMutator will need fixing.
type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag
@@ -434,6 +453,23 @@
}
}
+// AvailableToSameApexes returns true if the two modules are apex_available to
+// exactly the same set of APEXes (and platform), i.e. if their apex_available
+// properties have the same elements.
+func AvailableToSameApexes(mod1, mod2 ApexModule) bool {
+ mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available)
+ mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available)
+ if len(mod1ApexAvail) != len(mod2ApexAvail) {
+ return false
+ }
+ for i, v := range mod1ApexAvail {
+ if v != mod2ApexAvail[i] {
+ return false
+ }
+ }
+ return true
+}
+
type byApexName []ApexInfo
func (a byApexName) Len() int { return len(a) }
diff --git a/android/arch_test.go b/android/arch_test.go
index 09cb523..633ddaa 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -358,12 +358,12 @@
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForArchTest,
// Test specific preparer
OptionalFixturePreparer(tt.preparer),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
ctx := result.TestContext
if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) {
@@ -441,7 +441,7 @@
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForArchTest,
// Test specific preparer
OptionalFixturePreparer(tt.preparer),
@@ -455,7 +455,7 @@
}
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
ctx := result.TestContext
diff --git a/android/bazel.go b/android/bazel.go
index 683495b..cc02152 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,150 @@
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.
+ bp2buildModuleDoNotConvertList = []string{
+ "generated_android_ids",
+ "libBionicBenchmarksUtils",
+ "libbionic_spawn_benchmark",
+ "libc_jemalloc_wrapper",
+ "libc_bootstrap",
+ "libc_init_static",
+ "libc_init_dynamic",
+ "libc_tzcode",
+ "libc_freebsd",
+ "libc_freebsd_large_stack",
+ "libc_netbsd",
+ "libc_openbsd_ndk",
+ "libc_openbsd_large_stack",
+ "libc_openbsd",
+ "libc_gdtoa",
+ "libc_fortify",
+ "libc_bionic",
+ "libc_bionic_ndk",
+ "libc_bionic_systrace",
+ "libc_pthread",
+ "libc_syscalls",
+ "libc_aeabi",
+ "libc_ndk",
+ "libc_nopthread",
+ "libc_common",
+ "libc_static_dispatch",
+ "libc_dynamic_dispatch",
+ "libc_common_static",
+ "libc_common_shared",
+ "libc_unwind_static",
+ "libc_nomalloc",
+ "libasync_safe",
+ "libc_malloc_debug_backtrace",
+ "libsystemproperties",
+ "libdl_static",
+ "liblinker_main",
+ "liblinker_malloc",
+ "liblinker_debuggerd_stub",
+ "libbionic_tests_headers_posix",
+ "libc_dns",
+ "note_memtag_heap_async",
+ "note_memtag_heap_sync",
+ }
+
+ // Used for quicker lookups
+ bp2buildModuleDoNotConvert = map[string]bool{}
+)
+
+func init() {
+ for _, moduleName := range bp2buildModuleDoNotConvertList {
+ bp2buildModuleDoNotConvert[moduleName] = 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 +267,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_handler.go b/android/bazel_handler.go
index 0595d68..abc793f 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,6 +27,7 @@
"sync"
"android/soong/bazel/cquery"
+
"github.com/google/blueprint/bootstrap"
"android/soong/bazel"
@@ -37,7 +38,6 @@
const (
getAllFiles CqueryRequestType = iota
- getCcObjectFiles
getAllFilesAndCcObjectFiles
)
@@ -56,10 +56,6 @@
// Returns result files built by building the given bazel target label.
GetOutputFiles(label string, archType ArchType) ([]string, bool)
- // Returns object files produced by compiling the given cc-related target.
- // Retrieves these files from Bazel's CcInfo provider.
- GetCcObjectFiles(label string, archType ArchType) ([]string, bool)
-
// TODO(cparsons): Other cquery-related methods should be added here.
// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
@@ -116,11 +112,6 @@
return result, ok
}
-func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
- result, ok := m.AllFiles[label]
- return result, ok
-}
-
func (m MockBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
result, ok := m.AllFiles[label]
return result, result, ok
@@ -154,16 +145,6 @@
return ret, ok
}
-func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
- rawString, ok := bazelCtx.cquery(label, cquery.GetCcObjectFiles, archType)
- var returnResult []string
- if ok {
- bazelOutput := strings.TrimSpace(rawString)
- returnResult = cquery.GetCcObjectFiles.ParseResult(bazelOutput).([]string)
- }
- return returnResult, ok
-}
-
func (bazelCtx *bazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
var outputFiles []string
var ccObjects []string
@@ -183,10 +164,6 @@
panic("unimplemented")
}
-func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
- panic("unimplemented")
-}
-
func (n noopBazelContext) GetOutputFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
panic("unimplemented")
}
@@ -332,8 +309,13 @@
name = "sourceroot",
path = "%s",
)
+
+local_repository(
+ name = "rules_cc",
+ path = "%s/build/bazel/rules_cc",
+)
`
- return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+ return []byte(fmt.Sprintf(formatString, context.workspaceDir, context.workspaceDir))
}
func (context *bazelContext) mainBzlFileContents() []byte {
@@ -344,73 +326,39 @@
# This file is generated by soong_build. Do not edit.
#####################################################
-def _x86_64_transition_impl(settings, attr):
+def _config_node_transition_impl(settings, attr):
return {
- "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64",
+ "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_%s" % attr.arch,
}
-def _x86_transition_impl(settings, attr):
- return {
- "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86",
- }
-
-def _arm64_transition_impl(settings, attr):
- return {
- "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm64",
- }
-
-def _arm_transition_impl(settings, attr):
- return {
- "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm",
- }
-
-x86_64_transition = transition(
- implementation = _x86_64_transition_impl,
+_config_node_transition = transition(
+ implementation = _config_node_transition_impl,
inputs = [],
outputs = [
"//command_line_option:platforms",
],
)
-x86_transition = transition(
- implementation = _x86_transition_impl,
- inputs = [],
- outputs = [
- "//command_line_option:platforms",
- ],
+def _passthrough_rule_impl(ctx):
+ return [DefaultInfo(files = depset(ctx.files.deps))]
+
+config_node = rule(
+ implementation = _passthrough_rule_impl,
+ attrs = {
+ "arch" : attr.string(mandatory = True),
+ "deps" : attr.label_list(cfg = _config_node_transition),
+ "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+ },
)
-arm64_transition = transition(
- implementation = _arm64_transition_impl,
- inputs = [],
- outputs = [
- "//command_line_option:platforms",
- ],
-)
-
-arm_transition = transition(
- implementation = _arm_transition_impl,
- inputs = [],
- outputs = [
- "//command_line_option:platforms",
- ],
-)
-
-def _mixed_build_root_impl(ctx):
- all_files = ctx.files.deps_x86_64 + ctx.files.deps_x86 + ctx.files.deps_arm64 + ctx.files.deps_arm
- return [DefaultInfo(files = depset(all_files))]
# Rule representing the root of the build, to depend on all Bazel targets that
# are required for the build. Building this target will build the entire Bazel
# build tree.
mixed_build_root = rule(
- implementation = _mixed_build_root_impl,
+ implementation = _passthrough_rule_impl,
attrs = {
- "deps_x86_64" : attr.label_list(cfg = x86_64_transition),
- "deps_x86" : attr.label_list(cfg = x86_transition),
- "deps_arm64" : attr.label_list(cfg = arm64_transition),
- "deps_arm" : attr.label_list(cfg = arm_transition),
- "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+ "deps" : attr.label_list(),
},
)
@@ -446,44 +394,42 @@
// architecture mapping.
formatString := `
# This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root", "phony_root")
+load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
+
+%s
mixed_build_root(name = "buildroot",
- deps_x86_64 = [%s],
- deps_x86 = [%s],
- deps_arm64 = [%s],
- deps_arm = [%s],
+ deps = [%s],
)
phony_root(name = "phonyroot",
deps = [":buildroot"],
)
`
- var deps_x86_64 []string = nil
- var deps_x86 []string = nil
- var deps_arm64 []string = nil
- var deps_arm []string = nil
+ configNodeFormatString := `
+config_node(name = "%s",
+ arch = "%s",
+ deps = [%s],
+)
+`
+
+ configNodesSection := ""
+
+ labelsByArch := map[string][]string{}
for val, _ := range context.requests {
labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))
- switch getArchString(val) {
- case "x86_64":
- deps_x86_64 = append(deps_x86_64, labelString)
- case "x86":
- deps_x86 = append(deps_x86, labelString)
- case "arm64":
- deps_arm64 = append(deps_arm64, labelString)
- case "arm":
- deps_arm = append(deps_arm, labelString)
- default:
- panic(fmt.Sprintf("unhandled architecture %s for %v", getArchString(val), val))
- }
+ archString := getArchString(val)
+ labelsByArch[archString] = append(labelsByArch[archString], labelString)
}
- return []byte(fmt.Sprintf(formatString,
- strings.Join(deps_x86_64, ",\n "),
- strings.Join(deps_x86, ",\n "),
- strings.Join(deps_arm64, ",\n "),
- strings.Join(deps_arm, ",\n ")))
+ configNodeLabels := []string{}
+ for archString, labels := range labelsByArch {
+ configNodeLabels = append(configNodeLabels, fmt.Sprintf("\":%s\"", archString))
+ labelsString := strings.Join(labels, ",\n ")
+ configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+ }
+
+ return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(configNodeLabels, ",\n ")))
}
func indent(original string) string {
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 19706f5..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
}
@@ -1005,6 +1012,10 @@
return ioutil.ReadFile(absolutePath(path.String()))
}
+func (c *deviceConfig) WithDexpreopt() bool {
+ return c.config.productVariables.WithDexpreopt
+}
+
func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
}
@@ -1389,7 +1400,10 @@
}
func (c *deviceConfig) BoardSepolicyVers() string {
- return String(c.config.productVariables.BoardSepolicyVers)
+ if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
+ return ver
+ }
+ return c.PlatformSepolicyVersion()
}
func (c *deviceConfig) BoardReqdMaskPolicy() []string {
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
index d30ff69..b8a176e 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -19,14 +19,14 @@
)
func TestCSuiteConfig(t *testing.T) {
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithArchMutator,
FixtureRegisterWithContext(registerCSuiteBuildComponents),
FixtureWithRootAndroidBp(`
csuite_config { name: "plain"}
csuite_config { name: "with_manifest", test_config: "manifest.xml" }
`),
- )
+ ).RunTest(t)
variants := result.ModuleVariantsForTests("plain")
if len(variants) > 1 {
diff --git a/android/defaults_test.go b/android/defaults_test.go
index b33cb52..a7542ab 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -83,10 +83,10 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForDefaultsTest,
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
foo := result.Module("foo", "").(*defaultsTestModule)
@@ -114,11 +114,11 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForDefaultsTest,
PrepareForTestWithAllowMissingDependencies,
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
missingDefaults := result.ModuleForTests("missing_defaults", "").Output("out")
missingTransitiveDefaults := result.ModuleForTests("missing_transitive_defaults", "").Output("out")
diff --git a/android/deptag_test.go b/android/deptag_test.go
index 7f55896..eb4fa89 100644
--- a/android/deptag_test.go
+++ b/android/deptag_test.go
@@ -80,13 +80,13 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithArchMutator,
FixtureWithRootAndroidBp(bp),
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory)
}),
- )
+ ).RunTest(t)
config := result.Config
diff --git a/android/env.go b/android/env.go
index 289d803..725a145 100644
--- a/android/env.go
+++ b/android/env.go
@@ -18,12 +18,16 @@
"android/soong/shared"
)
-// This file supports dependencies on environment variables. During build manifest generation,
-// any dependency on an environment variable is added to a list. During the singleton phase
-// a JSON file is written containing the current value of all used environment variables.
-// The next time the top-level build script is run, it uses the soong_env executable to
-// compare the contents of the environment variables, rewriting the file if necessary to cause
-// a manifest regeneration.
+// This file supports dependencies on environment variables. During build
+// manifest generation, any dependency on an environment variable is added to a
+// list. At the end of the build, a JSON file called soong.environment.used is
+// written containing the current value of all used environment variables. The
+// next time the top-level build script is run, soong_ui parses the compare the
+// contents of the used environment variables, then, if they changed, deletes
+// soong.environment.used to cause a rebuild.
+//
+// The dependency of build.ninja on soong.environment.used is declared in
+// build.ninja.d
var originalEnv map[string]string
@@ -34,30 +38,3 @@
panic(err)
}
}
-
-func EnvSingleton() Singleton {
- return &envSingleton{}
-}
-
-type envSingleton struct{}
-
-func (c *envSingleton) GenerateBuildActions(ctx SingletonContext) {
- envDeps := ctx.Config().EnvDeps()
-
- envFile := PathForOutput(ctx, "soong.environment.used")
- if ctx.Failed() {
- return
- }
-
- data, err := shared.EnvFileContents(envDeps)
- if err != nil {
- ctx.Errorf(err.Error())
- }
-
- err = WriteFileToOutputDir(envFile, data, 0666)
- if err != nil {
- ctx.Errorf(err.Error())
- }
-
- ctx.AddNinjaFileDeps(envFile.String())
-}
diff --git a/android/filegroup.go b/android/filegroup.go
index 2eb4741..2f13ab8 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -30,7 +30,7 @@
// https://docs.bazel.build/versions/master/be/general.html#filegroup
type bazelFilegroupAttributes struct {
- Srcs bazel.LabelList
+ Srcs bazel.LabelListAttribute
}
type bazelFilegroup struct {
@@ -53,12 +53,14 @@
func FilegroupBp2Build(ctx TopDownMutatorContext) {
fg, ok := ctx.Module().(*fileGroup)
- if !ok || !fg.ConvertWithBp2build() {
+ if !ok || !fg.ConvertWithBp2build(ctx) {
return
}
+ srcs := bazel.MakeLabelListAttribute(
+ BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
attrs := &bazelFilegroupAttributes{
- Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
+ Srcs: srcs,
}
props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
diff --git a/android/fixture.go b/android/fixture.go
index 6e4819c..303c95c 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -30,8 +30,8 @@
// first creating a base Fixture (which is essentially empty) and then applying FixturePreparer
// instances to it to modify the environment.
//
-// FixtureFactory
-// ==============
+// FixtureFactory (deprecated)
+// ===========================
// These are responsible for creating fixtures. Factories are immutable and are intended to be
// initialized once and reused to create multiple fixtures. Each factory has a list of fixture
// preparers that prepare a fixture for running a test. Factories can also be used to create other
@@ -43,7 +43,9 @@
// intended to be immutable and able to prepare multiple Fixture objects simultaneously without
// them sharing any data.
//
-// FixturePreparers are only ever invoked once per test fixture. Prior to invocation the list of
+// They provide the basic capabilities for running tests too.
+//
+// FixturePreparers are only ever applied once per test fixture. Prior to application the list of
// FixturePreparers are flattened and deduped while preserving the order they first appear in the
// list. This makes it easy to reuse, group and combine FixturePreparers together.
//
@@ -119,14 +121,58 @@
// Some files to use in tests in the java package.
//
// var javaMockFS = android.MockFS{
-// "api/current.txt": nil,
-// "api/removed.txt": nil,
+// "api/current.txt": nil,
+// "api/removed.txt": nil,
// ...
// }
//
-// A package private factory for use for testing java within the java package.
+// A package private preparer for use for testing java within the java package.
//
-// var javaFixtureFactory = NewFixtureFactory(
+// var prepareForJavaTest = android.GroupFixturePreparers(
+// PrepareForIntegrationTestWithJava,
+// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+// ctx.RegisterModuleType("test_module", testModule)
+// }),
+// javaMockFS.AddToFixture(),
+// ...
+// }
+//
+// func TestJavaStuff(t *testing.T) {
+// result := android.GroupFixturePreparers(
+// prepareForJavaTest,
+// android.FixtureWithRootAndroidBp(`java_library {....}`),
+// android.MockFS{...}.AddToFixture(),
+// ).RunTest(t)
+// ... test result ...
+// }
+//
+// package cc
+// var PrepareForTestWithCC = android.GroupFixturePreparers(
+// android.PrepareForArchMutator,
+// android.prepareForPrebuilts,
+// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+// ...
+// )
+//
+// package apex
+//
+// var PrepareForApex = GroupFixturePreparers(
+// ...
+// )
+//
+// Use modules and mutators from java, cc and apex. Any duplicate preparers (like
+// android.PrepareForArchMutator) will be automatically deduped.
+//
+// var prepareForApexTest = android.GroupFixturePreparers(
+// PrepareForJava,
+// PrepareForCC,
+// PrepareForApex,
+// )
+//
+// // FixtureFactory instances have been deprecated, this remains for informational purposes to
+// // help explain some of the existing code but will be removed along with FixtureFactory.
+//
+// var javaFixtureFactory = android.NewFixtureFactory(
// PrepareForIntegrationTestWithJava,
// FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
// ctx.RegisterModuleType("test_module", testModule)
@@ -146,7 +192,7 @@
// package cc
// var PrepareForTestWithCC = GroupFixturePreparers(
// android.PrepareForArchMutator,
-// android.prepareForPrebuilts,
+// android.prepareForPrebuilts,
// FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
// ...
// )
@@ -170,52 +216,10 @@
//
// This is configured with a set of FixturePreparer objects that are used to
// initialize each Fixture instance this creates.
+//
+// deprecated: Use FixturePreparer instead.
type FixtureFactory interface {
-
- // Creates a copy of this instance and adds some additional preparers.
- //
- // Before the preparers are used they are combined with the preparers provided when the factory
- // was created, any groups of preparers are flattened, and the list is deduped so that each
- // preparer is only used once. See the file documentation in android/fixture.go for more details.
- Extend(preparers ...FixturePreparer) FixtureFactory
-
- // Create a Fixture.
- Fixture(t *testing.T, preparers ...FixturePreparer) Fixture
-
- // ExtendWithErrorHandler creates a new FixtureFactory that will use the supplied error handler
- // to check the errors (may be 0) reported by the test.
- //
- // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
- // errors are reported.
- ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory
-
- // Run the test, checking any errors reported and returning a TestResult instance.
- //
- // Shorthand for Fixture(t, preparers...).RunTest()
- RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult
-
- // Run the test with the supplied Android.bp file.
- //
- // Shorthand for RunTest(t, android.FixtureWithRootAndroidBp(bp))
- RunTestWithBp(t *testing.T, bp string) *TestResult
-
- // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
- // the test fixture.
- //
- // In order to allow the Config object to be customized separately to the TestContext a lot of
- // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
- // from the test and then have the TestContext created and configured automatically. e.g.
- // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
- //
- // This method allows those methods to be migrated to use the test fixture pattern without
- // requiring that every test that uses those methods be migrated at the same time. That allows
- // those tests to benefit from correctness in the order of registration quickly.
- //
- // This method discards the config (along with its mock file system, product variables,
- // environment, etc.) that may have been set up by FixturePreparers.
- //
- // deprecated
- RunTestWithConfig(t *testing.T, config Config) *TestResult
+ FixturePreparer
}
// Create a new FixtureFactory that will apply the supplied preparers.
@@ -224,14 +228,17 @@
// the package level setUp method. It has to be a pointer to the variable as the variable will not
// have been initialized at the time the factory is created. If it is nil then a test specific
// temporary directory will be created instead.
+//
+// deprecated: The functionality provided by FixtureFactory will be merged into FixturePreparer
func NewFixtureFactory(buildDirSupplier *string, preparers ...FixturePreparer) FixtureFactory {
- return &fixtureFactory{
+ f := &fixtureFactory{
buildDirSupplier: buildDirSupplier,
- preparers: dedupAndFlattenPreparers(nil, preparers),
-
- // Set the default error handler.
- errorHandler: FixtureExpectsNoErrors,
+ compositeFixturePreparer: compositeFixturePreparer{
+ preparers: dedupAndFlattenPreparers(nil, preparers),
+ },
}
+ f.initBaseFixturePreparer(f)
+ return f
}
// A set of mock files to add to the mock file system.
@@ -416,7 +423,8 @@
// Before preparing the fixture the list of preparers is flattened by replacing each
// instance of GroupFixturePreparers with its contents.
func GroupFixturePreparers(preparers ...FixturePreparer) FixturePreparer {
- return &compositeFixturePreparer{dedupAndFlattenPreparers(nil, preparers)}
+ all := dedupAndFlattenPreparers(nil, preparers)
+ return newFixturePreparer(all)
}
// NullFixturePreparer is a preparer that does nothing.
@@ -432,20 +440,58 @@
}
}
-type simpleFixturePreparerVisitor func(preparer *simpleFixturePreparer)
-
-// FixturePreparer is an opaque interface that can change a fixture.
+// FixturePreparer provides the ability to create, modify and then run tests within a fixture.
type FixturePreparer interface {
- // visit calls the supplied visitor with each *simpleFixturePreparer instances in this preparer,
- visit(simpleFixturePreparerVisitor)
-}
+ // Return the flattened and deduped list of simpleFixturePreparer pointers.
+ list() []*simpleFixturePreparer
-type fixturePreparers []FixturePreparer
+ // Creates a copy of this instance and adds some additional preparers.
+ //
+ // Before the preparers are used they are combined with the preparers provided when the factory
+ // was created, any groups of preparers are flattened, and the list is deduped so that each
+ // preparer is only used once. See the file documentation in android/fixture.go for more details.
+ //
+ // deprecated: Use GroupFixturePreparers() instead.
+ Extend(preparers ...FixturePreparer) FixturePreparer
-func (f fixturePreparers) visit(visitor simpleFixturePreparerVisitor) {
- for _, p := range f {
- p.visit(visitor)
- }
+ // Create a Fixture.
+ Fixture(t *testing.T) Fixture
+
+ // ExtendWithErrorHandler creates a new FixturePreparer that will use the supplied error handler
+ // to check the errors (may be 0) reported by the test.
+ //
+ // The default handlers is FixtureExpectsNoErrors which will fail the go test immediately if any
+ // errors are reported.
+ ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer
+
+ // Run the test, checking any errors reported and returning a TestResult instance.
+ //
+ // Shorthand for Fixture(t).RunTest()
+ RunTest(t *testing.T) *TestResult
+
+ // Run the test with the supplied Android.bp file.
+ //
+ // preparer.RunTestWithBp(t, bp) is shorthand for
+ // android.GroupFixturePreparers(preparer, android.FixtureWithRootAndroidBp(bp)).RunTest(t)
+ RunTestWithBp(t *testing.T, bp string) *TestResult
+
+ // RunTestWithConfig is a temporary method added to help ease the migration of existing tests to
+ // the test fixture.
+ //
+ // In order to allow the Config object to be customized separately to the TestContext a lot of
+ // existing test code has `test...WithConfig` funcs that allow the Config object to be supplied
+ // from the test and then have the TestContext created and configured automatically. e.g.
+ // testCcWithConfig, testCcErrorWithConfig, testJavaWithConfig, etc.
+ //
+ // This method allows those methods to be migrated to use the test fixture pattern without
+ // requiring that every test that uses those methods be migrated at the same time. That allows
+ // those tests to benefit from correctness in the order of registration quickly.
+ //
+ // This method discards the config (along with its mock file system, product variables,
+ // environment, etc.) that may have been set up by FixturePreparers.
+ //
+ // deprecated
+ RunTestWithConfig(t *testing.T, config Config) *TestResult
}
// dedupAndFlattenPreparers removes any duplicates and flattens any composite FixturePreparer
@@ -458,48 +504,73 @@
// preparers - a list of additional unflattened, undeduped preparers that will be applied after the
// base preparers.
//
-// Returns a deduped and flattened list of the preparers minus any that exist in the base preparers.
-func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers fixturePreparers) []*simpleFixturePreparer {
- var list []*simpleFixturePreparer
+// Returns a deduped and flattened list of the preparers starting with the ones in base with any
+// additional ones from the preparers list added afterwards.
+func dedupAndFlattenPreparers(base []*simpleFixturePreparer, preparers []FixturePreparer) []*simpleFixturePreparer {
+ if len(preparers) == 0 {
+ return base
+ }
+
+ list := make([]*simpleFixturePreparer, len(base))
visited := make(map[*simpleFixturePreparer]struct{})
// Mark the already flattened and deduped preparers, if any, as having been seen so that
- // duplicates of these in the additional preparers will be discarded.
- for _, s := range base {
+ // duplicates of these in the additional preparers will be discarded. Add them to the output
+ // list.
+ for i, s := range base {
visited[s] = struct{}{}
+ list[i] = s
}
- preparers.visit(func(preparer *simpleFixturePreparer) {
- if _, seen := visited[preparer]; !seen {
- visited[preparer] = struct{}{}
- list = append(list, preparer)
+ for _, p := range preparers {
+ for _, s := range p.list() {
+ if _, seen := visited[s]; !seen {
+ visited[s] = struct{}{}
+ list = append(list, s)
+ }
}
- })
+ }
+
return list
}
// compositeFixturePreparer is a FixturePreparer created from a list of fixture preparers.
type compositeFixturePreparer struct {
+ baseFixturePreparer
+ // The flattened and deduped list of simpleFixturePreparer pointers encapsulated within this
+ // composite preparer.
preparers []*simpleFixturePreparer
}
-func (c *compositeFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
- for _, p := range c.preparers {
- p.visit(visitor)
+func (c *compositeFixturePreparer) list() []*simpleFixturePreparer {
+ return c.preparers
+}
+
+func newFixturePreparer(preparers []*simpleFixturePreparer) FixturePreparer {
+ if len(preparers) == 1 {
+ return preparers[0]
}
+ p := &compositeFixturePreparer{
+ preparers: preparers,
+ }
+ p.initBaseFixturePreparer(p)
+ return p
}
// simpleFixturePreparer is a FixturePreparer that applies a function to a fixture.
type simpleFixturePreparer struct {
+ baseFixturePreparer
function func(fixture *fixture)
}
-func (s *simpleFixturePreparer) visit(visitor simpleFixturePreparerVisitor) {
- visitor(s)
+func (s *simpleFixturePreparer) list() []*simpleFixturePreparer {
+ return []*simpleFixturePreparer{s}
}
func newSimpleFixturePreparer(preparer func(fixture *fixture)) FixturePreparer {
- return &simpleFixturePreparer{function: preparer}
+ p := &simpleFixturePreparer{function: preparer}
+ p.initBaseFixturePreparer(p)
+ return p
}
// FixtureErrorHandler determines how to respond to errors reported by the code under test.
@@ -635,82 +706,64 @@
NinjaDeps []string
}
-var _ FixtureFactory = (*fixtureFactory)(nil)
-
-type fixtureFactory struct {
- buildDirSupplier *string
- preparers []*simpleFixturePreparer
- errorHandler FixtureErrorHandler
-}
-
-func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixtureFactory {
- // Create a new slice to avoid accidentally sharing the preparers slice from this factory with
- // the extending factories.
- var all []*simpleFixturePreparer
- all = append(all, f.preparers...)
- all = append(all, dedupAndFlattenPreparers(f.preparers, preparers)...)
- // Copy the existing factory.
- extendedFactory := &fixtureFactory{}
- *extendedFactory = *f
- // Use the extended list of preparers.
- extendedFactory.preparers = all
- return extendedFactory
-}
-
-func (f *fixtureFactory) Fixture(t *testing.T, preparers ...FixturePreparer) Fixture {
- var buildDir string
- if f.buildDirSupplier == nil {
- // Create a new temporary directory for this run. It will be automatically cleaned up when the
- // test finishes.
- buildDir = t.TempDir()
- } else {
- // Retrieve the buildDir from the supplier.
- buildDir = *f.buildDirSupplier
- }
+func createFixture(t *testing.T, buildDir string, preparers []*simpleFixturePreparer) Fixture {
config := TestConfig(buildDir, nil, "", nil)
ctx := NewTestContext(config)
fixture := &fixture{
- factory: f,
- t: t,
- config: config,
- ctx: ctx,
- mockFS: make(MockFS),
- errorHandler: f.errorHandler,
+ preparers: preparers,
+ t: t,
+ config: config,
+ ctx: ctx,
+ mockFS: make(MockFS),
+ // Set the default error handler.
+ errorHandler: FixtureExpectsNoErrors,
}
- for _, preparer := range f.preparers {
- preparer.function(fixture)
- }
-
- for _, preparer := range dedupAndFlattenPreparers(f.preparers, preparers) {
+ for _, preparer := range preparers {
preparer.function(fixture)
}
return fixture
}
-func (f *fixtureFactory) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixtureFactory {
- newFactory := &fixtureFactory{}
- *newFactory = *f
- newFactory.errorHandler = errorHandler
- return newFactory
+type baseFixturePreparer struct {
+ self FixturePreparer
}
-func (f *fixtureFactory) RunTest(t *testing.T, preparers ...FixturePreparer) *TestResult {
+func (b *baseFixturePreparer) initBaseFixturePreparer(self FixturePreparer) {
+ b.self = self
+}
+
+func (b *baseFixturePreparer) Extend(preparers ...FixturePreparer) FixturePreparer {
+ all := dedupAndFlattenPreparers(b.self.list(), preparers)
+ return newFixturePreparer(all)
+}
+
+func (b *baseFixturePreparer) Fixture(t *testing.T) Fixture {
+ return createFixture(t, t.TempDir(), b.self.list())
+}
+
+func (b *baseFixturePreparer) ExtendWithErrorHandler(errorHandler FixtureErrorHandler) FixturePreparer {
+ return b.self.Extend(newSimpleFixturePreparer(func(fixture *fixture) {
+ fixture.errorHandler = errorHandler
+ }))
+}
+
+func (b *baseFixturePreparer) RunTest(t *testing.T) *TestResult {
t.Helper()
- fixture := f.Fixture(t, preparers...)
+ fixture := b.self.Fixture(t)
return fixture.RunTest()
}
-func (f *fixtureFactory) RunTestWithBp(t *testing.T, bp string) *TestResult {
+func (b *baseFixturePreparer) RunTestWithBp(t *testing.T, bp string) *TestResult {
t.Helper()
- return f.RunTest(t, FixtureWithRootAndroidBp(bp))
+ return GroupFixturePreparers(b.self, FixtureWithRootAndroidBp(bp)).RunTest(t)
}
-func (f *fixtureFactory) RunTestWithConfig(t *testing.T, config Config) *TestResult {
+func (b *baseFixturePreparer) RunTestWithConfig(t *testing.T, config Config) *TestResult {
t.Helper()
// Create the fixture as normal.
- fixture := f.Fixture(t).(*fixture)
+ fixture := b.self.Fixture(t).(*fixture)
// Discard the mock filesystem as otherwise that will override the one in the config.
fixture.mockFS = nil
@@ -729,9 +782,49 @@
return fixture.RunTest()
}
+var _ FixtureFactory = (*fixtureFactory)(nil)
+
+type fixtureFactory struct {
+ compositeFixturePreparer
+
+ buildDirSupplier *string
+}
+
+// Override to preserve the buildDirSupplier.
+func (f *fixtureFactory) Extend(preparers ...FixturePreparer) FixturePreparer {
+ // If there is no buildDirSupplier then just use the default implementation.
+ if f.buildDirSupplier == nil {
+ return f.baseFixturePreparer.Extend(preparers...)
+ }
+
+ all := dedupAndFlattenPreparers(f.preparers, preparers)
+
+ // Create a new factory which uses the same buildDirSupplier as the previous one.
+ extendedFactory := &fixtureFactory{
+ buildDirSupplier: f.buildDirSupplier,
+ compositeFixturePreparer: compositeFixturePreparer{
+ preparers: all,
+ },
+ }
+ extendedFactory.initBaseFixturePreparer(extendedFactory)
+ return extendedFactory
+}
+
+func (f *fixtureFactory) Fixture(t *testing.T) Fixture {
+ // If there is no buildDirSupplier then just use the default implementation.
+ if f.buildDirSupplier == nil {
+ return f.baseFixturePreparer.Fixture(t)
+ }
+
+ // Retrieve the buildDir from the supplier.
+ buildDir := *f.buildDirSupplier
+
+ return createFixture(t, buildDir, f.preparers)
+}
+
type fixture struct {
- // The factory used to create this fixture.
- factory *fixtureFactory
+ // The preparers used to create this fixture.
+ preparers []*simpleFixturePreparer
// The gotest state of the go test within which this was created.
t *testing.T
@@ -839,6 +932,21 @@
return result
}
+// Preparer will return a FixturePreparer encapsulating all the preparers used to create the fixture
+// that produced this result.
+//
+// e.g. assuming that this result was created by running:
+// factory.Extend(preparer1, preparer2).RunTest(t, preparer3, preparer4)
+//
+// Then this method will be equivalent to running:
+// GroupFixturePreparers(preparer1, preparer2, preparer3, preparer4)
+//
+// This is intended for use by tests whose output is Android.bp files to verify that those files
+// are valid, e.g. tests of the snapshots produced by the sdk module type.
+func (r *TestResult) Preparer() FixturePreparer {
+ return newFixturePreparer(r.fixture.preparers)
+}
+
// Module returns the module with the specific name and of the specified variant.
func (r *TestResult) Module(name string, variant string) Module {
return r.ModuleForTests(name, variant).Module()
diff --git a/android/fixture_test.go b/android/fixture_test.go
index 209bee6..8f04715 100644
--- a/android/fixture_test.go
+++ b/android/fixture_test.go
@@ -39,48 +39,44 @@
preparer2Then1 := GroupFixturePreparers(preparer2, preparer1)
- buildDir := "build"
- factory := NewFixtureFactory(&buildDir, preparer1, preparer2, preparer1, preparer1Then2)
+ group := GroupFixturePreparers(preparer1, preparer2, preparer1, preparer1Then2)
- extension := factory.Extend(preparer4, preparer2)
+ extension := group.Extend(preparer4, preparer2)
- extension.Fixture(t, preparer1, preparer2, preparer2Then1, preparer3)
+ GroupFixturePreparers(extension, preparer1, preparer2, preparer2Then1, preparer3).Fixture(t)
AssertDeepEquals(t, "preparers called in wrong order",
[]string{"preparer1", "preparer2", "preparer4", "preparer3"}, list)
}
func TestFixtureValidateMockFS(t *testing.T) {
- buildDir := "<unused>"
- factory := NewFixtureFactory(&buildDir)
-
t.Run("absolute path", func(t *testing.T) {
AssertPanicMessageContains(t, "source path validation failed", "Path is outside directory: /abs/path/Android.bp", func() {
- factory.Fixture(t, FixtureAddFile("/abs/path/Android.bp", nil))
+ FixtureAddFile("/abs/path/Android.bp", nil).Fixture(t)
})
})
t.Run("not canonical", func(t *testing.T) {
AssertPanicMessageContains(t, "source path validation failed", `path "path/with/../in/it/Android.bp" is not a canonical path, use "path/in/it/Android.bp" instead`, func() {
- factory.Fixture(t, FixtureAddFile("path/with/../in/it/Android.bp", nil))
+ FixtureAddFile("path/with/../in/it/Android.bp", nil).Fixture(t)
})
})
t.Run("FixtureAddFile", func(t *testing.T) {
AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
- factory.Fixture(t, FixtureAddFile("out/Android.bp", nil))
+ FixtureAddFile("out/Android.bp", nil).Fixture(t)
})
})
t.Run("FixtureMergeMockFs", func(t *testing.T) {
AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
- factory.Fixture(t, FixtureMergeMockFs(MockFS{
+ FixtureMergeMockFs(MockFS{
"out/Android.bp": nil,
- }))
+ }).Fixture(t)
})
})
t.Run("FixtureModifyMockFS", func(t *testing.T) {
AssertPanicMessageContains(t, "source path validation failed", `cannot add output path "out/Android.bp" to the mock file system`, func() {
- factory.Fixture(t, FixtureModifyMockFS(func(fs MockFS) {
+ FixtureModifyMockFS(func(fs MockFS) {
fs["out/Android.bp"] = nil
- }))
+ }).Fixture(t)
})
})
}
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
index 83e83ce..1f09568 100644
--- a/android/license_kind_test.go
+++ b/android/license_kind_test.go
@@ -97,13 +97,14 @@
func TestLicenseKind(t *testing.T) {
for _, test := range licenseKindTests {
t.Run(test.name, func(t *testing.T) {
- licenseTestFixtureFactory.
- Extend(
- FixtureRegisterWithContext(func(ctx RegistrationContext) {
- ctx.RegisterModuleType("mock_license", newMockLicenseModule)
- }),
- test.fs.AddToFixture(),
- ).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
+ GroupFixturePreparers(
+ prepareForLicenseTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+ }),
+ test.fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
RunTest(t)
})
}
diff --git a/android/license_test.go b/android/license_test.go
index a564827..2b09a4f 100644
--- a/android/license_test.go
+++ b/android/license_test.go
@@ -5,7 +5,7 @@
)
// Common test set up for license tests.
-var licenseTestFixtureFactory = emptyTestFixtureFactory.Extend(
+var prepareForLicenseTest = GroupFixturePreparers(
// General preparers in alphabetical order.
PrepareForTestWithDefaults,
prepareForTestWithLicenses,
@@ -179,7 +179,8 @@
for _, test := range licenseTests {
t.Run(test.name, func(t *testing.T) {
// Customize the common license text fixture factory.
- licenseTestFixtureFactory.Extend(
+ GroupFixturePreparers(
+ prepareForLicenseTest,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("rule", newMockRuleModule)
}),
diff --git a/android/licenses_test.go b/android/licenses_test.go
index a581932..913dc88 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -470,7 +470,8 @@
for _, test := range licensesTests {
t.Run(test.name, func(t *testing.T) {
// Customize the common license text fixture factory.
- result := licenseTestFixtureFactory.Extend(
+ result := GroupFixturePreparers(
+ prepareForLicenseTest,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
diff --git a/android/module_test.go b/android/module_test.go
index 99bf30a..9ac9291 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -179,11 +179,9 @@
}
`
- emptyTestFixtureFactory.
+ prepareForModuleTests.
ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
- RunTest(t,
- prepareForModuleTests,
- FixtureWithRootAndroidBp(bp))
+ RunTestWithBp(t, bp)
}
func TestValidateCorrectBuildParams(t *testing.T) {
@@ -268,9 +266,7 @@
"\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
}
- emptyTestFixtureFactory.
+ prepareForModuleTests.
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
- RunTest(t,
- prepareForModuleTests,
- FixtureWithRootAndroidBp(bp))
+ RunTestWithBp(t, bp)
}
diff --git a/android/mutator.go b/android/mutator.go
index 9552aa1..e25e2e8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -202,7 +202,7 @@
RegisterPrebuiltsPostDepsMutators,
RegisterVisibilityRuleEnforcer,
RegisterLicensesDependencyChecker,
- RegisterNeverallowMutator,
+ registerNeverallowMutator,
RegisterOverridePostDepsMutators,
}
@@ -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
@@ -539,7 +539,7 @@
Name: &name,
}
- b := t.CreateModule(factory, &nameProp, attrs).(BazelTargetModule)
+ b := t.createModuleWithoutInheritance(factory, &nameProp, attrs).(BazelTargetModule)
b.SetBazelTargetModuleProperties(bazelProps)
return b
}
@@ -608,6 +608,11 @@
return module
}
+func (t *topDownMutatorContext) createModuleWithoutInheritance(factory ModuleFactory, props ...interface{}) Module {
+ module := t.bp.CreateModule(ModuleFactoryAdaptor(factory), props...).(Module)
+ return module
+}
+
func (b *bottomUpMutatorContext) MutatorName() string {
return b.bp.MutatorName()
}
diff --git a/android/mutator_test.go b/android/mutator_test.go
index 46d26d1..21eebd2 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -65,7 +65,7 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithAllowMissingDependencies,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
@@ -74,7 +74,7 @@
})
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
foo := result.ModuleForTests("foo", "").Module().(*mutatorTestModule)
@@ -90,7 +90,7 @@
var moduleStrings []string
- emptyTestFixtureFactory.RunTest(t,
+ GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
@@ -128,7 +128,7 @@
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
want := []string{
// Initial name.
@@ -187,7 +187,7 @@
finalGot := map[string]int{}
- emptyTestFixtureFactory.RunTest(t,
+ GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
dep1Tag := struct {
blueprint.BaseDependencyTag
@@ -224,7 +224,7 @@
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
finalWant := map[string]int{
"common_dep_1{variant:a}": 1,
@@ -249,7 +249,7 @@
}
}
- emptyTestFixtureFactory.RunTest(t,
+ GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
@@ -265,5 +265,5 @@
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
}),
FixtureWithRootAndroidBp(`test {name: "foo"}`),
- )
+ ).RunTest(t)
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 1caf5a8..08e221a 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -633,21 +633,21 @@
}
func setupTestFromFiles(t *testing.T, bps MockFS) (ctx *TestContext, errs []error) {
- result := emptyTestFixtureFactory.
+ result := GroupFixturePreparers(
+ FixtureModifyContext(func(ctx *TestContext) {
+ ctx.RegisterModuleType("test_module", newTestModule)
+ ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
+ ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
+ ctx.PreArchMutators(RegisterNamespaceMutator)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("rename", renameMutator)
+ })
+ }),
+ bps.AddToFixture(),
+ ).
// Ignore errors for now so tests can check them later.
ExtendWithErrorHandler(FixtureIgnoreErrors).
- RunTest(t,
- FixtureModifyContext(func(ctx *TestContext) {
- ctx.RegisterModuleType("test_module", newTestModule)
- ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
- ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
- ctx.PreArchMutators(RegisterNamespaceMutator)
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("rename", renameMutator)
- })
- }),
- bps.AddToFixture(),
- )
+ RunTest(t)
return result.TestContext, result.Errs
}
@@ -697,7 +697,7 @@
testModule, ok := candidate.(*testModule)
if ok {
if testModule.properties.Id == id {
- module = TestingModule{testModule}
+ module = newTestingModule(ctx.config, testModule)
}
}
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 7455e6a..a385bbc 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -42,7 +42,7 @@
// counts as a match
// - it has none of the "Without" properties matched (same rules as above)
-func RegisterNeverallowMutator(ctx RegisterMutatorsContext) {
+func registerNeverallowMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("neverallow", neverallowMutator).Parallel()
}
@@ -661,6 +661,22 @@
// Overrides the default neverallow rules for the supplied config.
//
// For testing only.
-func SetTestNeverallowRules(config Config, testRules []Rule) {
+func setTestNeverallowRules(config Config, testRules []Rule) {
config.Once(neverallowRulesKey, func() interface{} { return testRules })
}
+
+// Prepares for a test by setting neverallow rules and enabling the mutator.
+//
+// If the supplied rules are nil then the default rules are used.
+func PrepareForTestWithNeverallowRules(testRules []Rule) FixturePreparer {
+ return GroupFixturePreparers(
+ FixtureModifyConfig(func(config Config) {
+ if testRules != nil {
+ setTestNeverallowRules(config, testRules)
+ }
+ }),
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PostDepsMutators(registerNeverallowMutator)
+ }),
+ )
+}
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 5ac97e7..268346a 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -292,25 +292,19 @@
ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
- ctx.PostDepsMutators(RegisterNeverallowMutator)
}),
)
func TestNeverallow(t *testing.T) {
for _, test := range neverallowTests {
t.Run(test.name, func(t *testing.T) {
- emptyTestFixtureFactory.
+ GroupFixturePreparers(
+ prepareForNeverAllowTest,
+ PrepareForTestWithNeverallowRules(test.rules),
+ test.fs.AddToFixture(),
+ ).
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
- RunTest(t,
- prepareForNeverAllowTest,
- FixtureModifyConfig(func(config Config) {
- // If the test has its own rules then use them instead of the default ones.
- if test.rules != nil {
- SetTestNeverallowRules(config, test.rules)
- }
- }),
- test.fs.AddToFixture(),
- )
+ RunTest(t)
})
}
}
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
index 7e5864d..947c257 100644
--- a/android/ninja_deps_test.go
+++ b/android/ninja_deps_test.go
@@ -57,13 +57,13 @@
"test_ninja_deps/exists": nil,
}
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
}),
fs.AddToFixture(),
- )
+ ).RunTest(t)
// Verify that the ninja file has a dependency on the test_ninja_deps directory.
if g, w := result.NinjaDeps, "test_ninja_deps"; !InList(w, g) {
diff --git a/android/package_test.go b/android/package_test.go
index d5b4db4..3bd30cc 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -61,13 +61,13 @@
func TestPackage(t *testing.T) {
for _, test := range packageTests {
t.Run(test.name, func(t *testing.T) {
- emptyTestFixtureFactory.
+ GroupFixturePreparers(
+ PrepareForTestWithArchMutator,
+ PrepareForTestWithPackageModule,
+ test.fs.AddToFixture(),
+ ).
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
- RunTest(t,
- PrepareForTestWithArchMutator,
- PrepareForTestWithPackageModule,
- test.fs.AddToFixture(),
- )
+ RunTest(t)
})
}
}
diff --git a/android/packaging.go b/android/packaging.go
index 9b901ce..72c0c17 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -59,7 +59,8 @@
packagingBase() *PackagingBase
// AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
- // When adding the dependencies, depTag is used as the tag.
+ // When adding the dependencies, depTag is used as the tag. If `deps` modules are meant to
+ // be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
@@ -167,6 +168,24 @@
return ret
}
+// PackagingItem is a marker interface for dependency tags.
+// Direct dependencies with a tag implementing PackagingItem are packaged in CopyDepsToZip().
+type PackagingItem interface {
+ // IsPackagingItem returns true if the dep is to be packaged
+ IsPackagingItem() bool
+}
+
+// DepTag provides default implementation of PackagingItem interface.
+// PackagingBase-derived modules can define their own dependency tag by embedding this, which
+// can be passed to AddDeps() or AddDependencies().
+type PackagingItemAlwaysDepTag struct {
+}
+
+// IsPackagingItem returns true if the dep is to be packaged
+func (PackagingItemAlwaysDepTag) IsPackagingItem() bool {
+ return true
+}
+
// See PackageModule.AddDeps
func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
for _, t := range p.getSupportedTargets(ctx) {
@@ -182,16 +201,15 @@
// See PackageModule.CopyDepsToZip
func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
m := make(map[string]PackagingSpec)
- ctx.WalkDeps(func(child Module, parent Module) bool {
- if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
- return false
+ ctx.VisitDirectDeps(func(child Module) {
+ if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
+ return
}
- for _, ps := range child.PackagingSpecs() {
+ for _, ps := range child.TransitivePackagingSpecs() {
if _, ok := m[ps.relPathInPackage]; !ok {
m[ps.relPathInPackage] = ps
}
}
- return true
})
builder := NewRuleBuilder(pctx, ctx)
diff --git a/android/packaging_test.go b/android/packaging_test.go
index eb7f26f..f91dc5d 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -56,7 +56,9 @@
type packageTestModule struct {
ModuleBase
PackagingBase
-
+ properties struct {
+ Install_deps []string `android:`
+ }
entries []string
}
@@ -64,6 +66,7 @@
module := &packageTestModule{}
InitPackageModule(module)
InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+ module.AddProperties(&module.properties)
return module
}
@@ -71,11 +74,18 @@
module := &packageTestModule{}
InitPackageModule(module)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
+ module.AddProperties(&module.properties)
return module
}
+type packagingDepTag struct {
+ blueprint.BaseDependencyTag
+ PackagingItemAlwaysDepTag
+}
+
func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- m.AddDeps(ctx, installDepTag{})
+ m.AddDeps(ctx, packagingDepTag{})
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.properties.Install_deps...)
}
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -96,14 +106,14 @@
moduleFactory = packageTestModuleFactory
}
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithArchMutator,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("component", componentTestModuleFactory)
ctx.RegisterModuleType("package_module", moduleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
p := result.Module("package", archVariant).(*packageTestModule)
actual := p.entries
@@ -337,4 +347,21 @@
},
}
`, []string{"lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t, multiTarget,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ install_deps: ["bar"],
+ }
+ `, []string{"lib64/foo"})
}
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 8726ea7..568f868 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -157,14 +157,14 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithArchMutator,
PrepareForTestWithFilegroup,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
m := result.Module("foo", "android_arm64_armv8-a").(*pathDepsMutatorTestModule)
diff --git a/android/paths.go b/android/paths.go
index f648c55..ba1ab11 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
@@ -259,6 +286,16 @@
return p.path
}
+// RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the
+// result of calling Path.RelativeToTop on it.
+func (p OptionalPath) RelativeToTop() OptionalPath {
+ if !p.valid {
+ return p
+ }
+ p.path = p.path.RelativeToTop()
+ return p
+}
+
// String returns the string version of the Path, or "" if it isn't valid.
func (p OptionalPath) String() string {
if p.valid {
@@ -271,6 +308,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 +391,7 @@
GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
Module() Module
+ ModuleType() string
OtherModuleName(m blueprint.Module) string
OtherModuleDir(m blueprint.Module) string
}
@@ -435,6 +487,9 @@
// already be resolved by either deps mutator or path deps mutator.
func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
m, _ := ctx.GetDirectDep(dep)
+ if m == nil {
+ panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name()))
+ }
otherLabel := bazelModuleLabel(ctx, m, tag)
label := bazelModuleLabel(ctx, ctx.Module(), "")
if samePackage(label, otherLabel) {
@@ -450,7 +505,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)
@@ -511,6 +566,9 @@
if module == nil {
return nil, missingDependencyError{[]string{moduleName}}
}
+ if aModule, ok := module.(Module); ok && !aModule.Enabled() {
+ return nil, missingDependencyError{[]string{moduleName}}
+ }
if outProducer, ok := module.(OutputFileProducer); ok {
outputFiles, err := outProducer.OutputFiles(tag)
if err != nil {
@@ -906,6 +964,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 {
@@ -931,9 +1003,8 @@
}
type basePath struct {
- path string
- config Config
- rel string
+ path string
+ rel string
}
func (p basePath) Ext() string {
@@ -964,6 +1035,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{}
@@ -977,7 +1056,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
}
@@ -993,7 +1072,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
}
@@ -1087,7 +1166,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
@@ -1119,7 +1198,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)
@@ -1132,13 +1211,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
}
@@ -1153,8 +1236,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 {
@@ -1170,6 +1263,11 @@
basePath
}
+func (t toolDepPath) RelativeToTop() Path {
+ ensureTestOnly()
+ return t
+}
+
var _ Path = toolDepPath{}
// pathForBuildToolDep returns a toolDepPath representing the given path string.
@@ -1178,7 +1276,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
@@ -1191,7 +1289,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
@@ -1347,7 +1445,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))
@@ -1425,7 +1529,8 @@
reportPathError(ctx, err)
}
- outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ outputPath := OutputPath{basePath{"", ""},
+ ctx.Config().buildDir,
ctx.Config().BazelContext.OutputBase()}
return BazelOutPath{
@@ -1451,7 +1556,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{}
@@ -1484,7 +1595,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.
@@ -1502,7 +1619,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.
@@ -1519,6 +1642,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
@@ -1527,8 +1653,22 @@
makePath bool
}
-func (p InstallPath) buildDir() string {
- return p.config.buildDir
+// Will panic if called from outside a test environment.
+func ensureTestOnly() {
+ if PrefixInList(os.Args, "-test.") {
+ return
+ }
+ panic(fmt.Errorf("Not in test. Command line:\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 {
@@ -1543,9 +1683,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)
}
}
@@ -1554,9 +1694,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)
}
}
@@ -1639,7 +1779,8 @@
}
base := InstallPath{
- basePath: basePath{partionPath, ctx.Config(), ""},
+ basePath: basePath{partionPath, ""},
+ buildDir: ctx.Config().buildDir,
partitionDir: partionPath,
makePath: false,
}
@@ -1649,7 +1790,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,
}
@@ -1784,7 +1926,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 {
@@ -1793,8 +1935,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 {
@@ -1808,10 +1958,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/paths_test.go b/android/paths_test.go
index c5fc10e..465ea3b 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1005,14 +1005,14 @@
"foo/src_special/$": nil,
}
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
ctx.RegisterModuleType("filegroup", FileGroupFactory)
}),
mockFS.AddToFixture(),
- )
+ ).RunTest(t)
m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
@@ -1203,13 +1203,13 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
PrepareForTestWithAllowMissingDependencies,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
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/prebuilt_test.go b/android/prebuilt_test.go
index 32af5df..ced37fe 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -284,7 +284,7 @@
t.Errorf("windows is assumed to be disabled by default")
}
- result := emptyTestFixtureFactory.Extend(
+ result := GroupFixturePreparers(
PrepareForTestWithArchMutator,
PrepareForTestWithPrebuilts,
PrepareForTestWithOverrides,
diff --git a/android/queryview.go b/android/queryview.go
index 12d14df..224652e 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -68,7 +68,7 @@
Command: fmt.Sprintf(
`rm -rf "${outDir}/"* && `+
`mkdir -p "${outDir}" && `+
- `echo WORKSPACE: cat "%s" > "${outDir}/.queryview-depfile.d" && `+
+ `echo WORKSPACE: $$(cat "%s") > "${outDir}/.queryview-depfile.d" && `+
`BUILDER="%s" && `+
`echo BUILDER=$$BUILDER && `+
`cd "$$(dirname "$$BUILDER")" && `+
diff --git a/android/register.go b/android/register.go
index c9e66e9..4c8088d 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
@@ -186,6 +192,15 @@
t.register(ctx)
}
+ if ctx.config.BazelContext.BazelEnabled() {
+ // Hydrate the configuration of bp2build-enabled module types. This is
+ // required as a signal to identify which modules should be deferred to
+ // Bazel in mixed builds, if it is enabled.
+ for t, _ := range bp2buildMutators {
+ ctx.config.bp2buildModuleTypeConfig[t] = true
+ }
+ }
+
mutators := collateGloballyRegisteredMutators()
mutators.registerAll(ctx)
@@ -206,7 +221,6 @@
// Register env and ninjadeps last so that they can track all used environment variables and
// Ninja file dependencies stored in the config.
- singleton{false, "env", EnvSingleton},
singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
)
@@ -263,8 +277,9 @@
// ctx := android.NewTestContext(config)
// RegisterBuildComponents(ctx)
var InitRegistrationContext RegistrationContext = &initRegistrationContext{
- moduleTypes: make(map[string]ModuleFactory),
- singletonTypes: make(map[string]SingletonFactory),
+ moduleTypes: make(map[string]ModuleFactory),
+ singletonTypes: make(map[string]SingletonFactory),
+ preSingletonTypes: make(map[string]SingletonFactory),
}
// Make sure the TestContext implements RegistrationContext.
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 3415aed..d2a7d8d 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,15 +581,24 @@
srcs: ["bar"],
sbox: true,
}
+ rule_builder_test {
+ name: "foo_sbox_inputs",
+ srcs: ["bar"],
+ sbox: true,
+ sbox_inputs: true,
+ }
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForRuleBuilderTest,
FixtureWithRootAndroidBp(bp),
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,48 +611,85 @@
AssertBoolEquals(t, "RuleParams.Restat", wantRestat, params.RuleParams.Restat)
- wantImplicits := append([]string{"bar"}, extraImplicits...)
- AssertArrayString(t, "Implicits", wantImplicits, params.Implicits.Strings())
+ wantInputs := []string{"rsp_in"}
+ AssertArrayString(t, "Inputs", wantInputs, params.Inputs.Strings())
- AssertStringEquals(t, "Output", wantOutput, params.Output.String())
+ 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 {
t.Errorf("want ImplicitOutputs = [], got %q", params.ImplicitOutputs.Strings())
}
- AssertStringEquals(t, "Depfile", wantDepfile, params.Depfile.String())
+ AssertPathRelativeToTopEquals(t, "Depfile", wantDepfile, params.Depfile)
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)
}
- buildDir := result.Config.BuildDir()
-
t.Run("module", func(t *testing.T) {
- outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
- check(t, result.ModuleForTests("foo", "").Rule("rule"),
- "cp bar "+outFile,
- outFile, outFile+".d", true, nil, nil)
+ outFile := "out/soong/.intermediates/foo/gen/foo"
+ rspFile := "out/soong/.intermediates/foo/rsp"
+ rspFile2 := "out/soong/.intermediates/foo/rsp2"
+ module := result.ModuleForTests("foo", "")
+ check(t, module.Rule("rule"), module.Output(rspFile2),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
})
t.Run("sbox", func(t *testing.T) {
- outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
+ 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(buildDir, "host", result.Config.PrebuiltOS(), "bin/sbox")
- sandboxPath := shared.TempDirForOutDir(buildDir)
+ 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"), module.Output(rspFile2),
+ 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")
cmd := `rm -rf ` + outDir + `/gen && ` +
sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
- check(t, result.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
- cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
+ module := result.ModuleForTests("foo_sbox_inputs", "")
+ check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
+ cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
})
t.Run("singleton", func(t *testing.T) {
- outFile := filepath.Join(buildDir, "singleton/gen/baz")
- check(t, result.SingletonForTests("rule_builder_test").Rule("rule"),
- "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
+ outFile := filepath.Join("out/soong/singleton/gen/baz")
+ 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"), singleton.Output(rspFile2),
+ "cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
+ outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
})
}
@@ -651,10 +738,10 @@
},
}
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForRuleBuilderTest,
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
index 41dd4bb..eb5554c 100644
--- a/android/singleton_module_test.go
+++ b/android/singleton_module_test.go
@@ -56,11 +56,10 @@
name: "test_singleton_module",
}
`
- result := emptyTestFixtureFactory.
- RunTest(t,
- prepareForSingletonModuleTest,
- FixtureWithRootAndroidBp(bp),
- )
+ result := GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ FixtureWithRootAndroidBp(bp),
+ ).RunTest(t)
ops := result.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
@@ -78,19 +77,16 @@
}
`
- emptyTestFixtureFactory.
+ prepareForSingletonModuleTest.
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
`\QDuplicate SingletonModule "test_singleton_module", previously used in\E`,
- })).RunTest(t,
- prepareForSingletonModuleTest,
- FixtureWithRootAndroidBp(bp),
- )
+ })).RunTestWithBp(t, bp)
}
func TestUnusedSingletonModule(t *testing.T) {
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
prepareForSingletonModuleTest,
- )
+ ).RunTest(t)
singleton := result.SingletonForTests("test_singleton_module").Singleton()
sm := singleton.(*singletonModuleSingletonAdaptor).sm
@@ -113,17 +109,16 @@
}
`
- emptyTestFixtureFactory.
+ GroupFixturePreparers(
+ prepareForSingletonModuleTest,
+ FixtureRegisterWithContext(func(ctx RegistrationContext) {
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+ })
+ }),
+ ).
ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
`\QGenerateAndroidBuildActions already called for variant\E`,
})).
- RunTest(t,
- prepareForSingletonModuleTest,
- FixtureRegisterWithContext(func(ctx RegistrationContext) {
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
- })
- }),
- FixtureWithRootAndroidBp(bp),
- )
+ RunTestWithBp(t, bp)
}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index a72b160..8f252d9 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -278,7 +278,7 @@
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
tc.preparer,
PrepareForTestWithDefaults,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -291,7 +291,7 @@
}),
fs.AddToFixture(),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
foo := result.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
AssertDeepEquals(t, "foo cflags", tc.fooExpectedFlags, foo.props.Cflags)
diff --git a/android/testing.go b/android/testing.go
index af360fa..ce27fca 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)
}
@@ -401,9 +411,6 @@
globalOrder.mutatorOrder.enforceOrdering(mutators)
mutators.registerAll(ctx.Context)
- // Register the env singleton with this context before sorting.
- ctx.RegisterSingletonType("env", EnvSingleton)
-
// Ensure that the singletons used in the test are in the same order as they are used at runtime.
globalOrder.singletonOrder.enforceOrdering(ctx.singletons)
ctx.singletons.registerAll(ctx.Context)
@@ -479,7 +486,7 @@
}
}
- return TestingModule{module}
+ return newTestingModule(ctx.config, module)
}
func (ctx *TestContext) ModuleVariantsForTests(name string) []string {
@@ -499,8 +506,8 @@
n := ctx.SingletonName(s)
if n == name {
return TestingSingleton{
- singleton: s.(*singletonAdaptor).Singleton,
- provider: s.(testBuildProvider),
+ baseTestingComponent: newBaseTestingComponent(ctx.config, s.(testBuildProvider)),
+ singleton: s.(*singletonAdaptor).Singleton,
}
}
allSingletonNames = append(allSingletonNames, n)
@@ -522,62 +529,199 @@
type TestingBuildParams struct {
BuildParams
RuleParams blueprint.RuleParams
+
+ config Config
}
-func newTestingBuildParams(provider testBuildProvider, bparams BuildParams) TestingBuildParams {
+// RelativeToTop creates a new instance of this which has had any usages of the current test's
+// temporary and test specific build directory replaced with a path relative to the notional top.
+//
+// The parts of this structure which are changed are:
+// * BuildParams
+// * Args
+// * All Path, Paths, WritablePath and WritablePaths fields.
+//
+// * RuleParams
+// * Command
+// * Depfile
+// * Rspfile
+// * RspfileContent
+// * SymlinkOutputs
+// * CommandDeps
+// * CommandOrderOnly
+//
+// See PathRelativeToTop for more details.
+//
+// deprecated: this is no longer needed as TestingBuildParams are created in this form.
+func (p TestingBuildParams) RelativeToTop() TestingBuildParams {
+ // If this is not a valid params then just return it back. That will make it easy to use with the
+ // Maybe...() methods.
+ if p.Rule == nil {
+ return p
+ }
+ if p.config.config == nil {
+ return p
+ }
+ // 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.
+ rparams := p.RuleParams
+ rparams.Command = normalizeStringRelativeToTop(p.config, rparams.Command)
+ rparams.Depfile = normalizeStringRelativeToTop(p.config, rparams.Depfile)
+ rparams.Rspfile = normalizeStringRelativeToTop(p.config, rparams.Rspfile)
+ rparams.RspfileContent = normalizeStringRelativeToTop(p.config, rparams.RspfileContent)
+ rparams.SymlinkOutputs = normalizeStringArrayRelativeToTop(p.config, rparams.SymlinkOutputs)
+ rparams.CommandDeps = normalizeStringArrayRelativeToTop(p.config, rparams.CommandDeps)
+ rparams.CommandOrderOnly = normalizeStringArrayRelativeToTop(p.config, rparams.CommandOrderOnly)
+
return TestingBuildParams{
BuildParams: bparams,
- RuleParams: provider.RuleParamsForTests()[bparams.Rule],
+ RuleParams: rparams,
}
}
-func maybeBuildParamsFromRule(provider testBuildProvider, rule string) (TestingBuildParams, []string) {
+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
+ provider testBuildProvider
+}
+
+func newBaseTestingComponent(config Config, provider testBuildProvider) baseTestingComponent {
+ return baseTestingComponent{config, provider}
+}
+
+// A function that will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func normalizeStringRelativeToTop(config Config, s string) string {
+ // The buildDir usually looks something like: /tmp/testFoo2345/001
+ //
+ // Replace any usage of the buildDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+ // "out/soong".
+ outSoongDir := filepath.Clean(config.buildDir)
+ re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
+ s = re.ReplaceAllString(s, "out/soong")
+
+ // Replace any usage of the buildDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+ // "out". This must come after the previous replacement otherwise this would replace
+ // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
+ outDir := filepath.Dir(outSoongDir)
+ re = regexp.MustCompile(`\Q` + outDir + `\E\b`)
+ s = re.ReplaceAllString(s, "out")
+
+ return s
+}
+
+// normalizeStringArrayRelativeToTop creates a new slice constructed by applying
+// normalizeStringRelativeToTop to each item in the slice.
+func normalizeStringArrayRelativeToTop(config Config, slice []string) []string {
+ newSlice := make([]string, len(slice))
+ for i, s := range slice {
+ newSlice[i] = normalizeStringRelativeToTop(config, s)
+ }
+ return newSlice
+}
+
+// normalizeStringMapRelativeToTop creates a new map constructed by applying
+// normalizeStringRelativeToTop to each value in the map.
+func normalizeStringMapRelativeToTop(config Config, m map[string]string) map[string]string {
+ newMap := map[string]string{}
+ for k, v := range m {
+ newMap[k] = normalizeStringRelativeToTop(config, v)
+ }
+ return newMap
+}
+
+func (b baseTestingComponent) newTestingBuildParams(bparams BuildParams) TestingBuildParams {
+ return TestingBuildParams{
+ config: b.config,
+ BuildParams: bparams,
+ RuleParams: b.provider.RuleParamsForTests()[bparams.Rule],
+ }.RelativeToTop()
+}
+
+func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
var searchedRules []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
searchedRules = append(searchedRules, p.Rule.String())
if strings.Contains(p.Rule.String(), rule) {
- return newTestingBuildParams(provider, p), searchedRules
+ return b.newTestingBuildParams(p), searchedRules
}
}
return TestingBuildParams{}, searchedRules
}
-func buildParamsFromRule(provider testBuildProvider, rule string) TestingBuildParams {
- p, searchRules := maybeBuildParamsFromRule(provider, rule)
+func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
+ p, searchRules := b.maybeBuildParamsFromRule(rule)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
}
return p
}
-func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
- for _, p := range provider.BuildParamsForTests() {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+ for _, p := range b.provider.BuildParamsForTests() {
if strings.Contains(p.Description, desc) {
- return newTestingBuildParams(provider, p)
+ return b.newTestingBuildParams(p)
}
}
return TestingBuildParams{}
}
-func buildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
- p := maybeBuildParamsFromDescription(provider, desc)
+func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
+ p := b.maybeBuildParamsFromDescription(desc)
if p.Rule == nil {
panic(fmt.Errorf("couldn't find description %q", desc))
}
return p
}
-func maybeBuildParamsFromOutput(provider testBuildProvider, file string) (TestingBuildParams, []string) {
+func (b baseTestingComponent) maybeBuildParamsFromOutput(file string) (TestingBuildParams, []string) {
var searchedOutputs []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
for _, f := range outputs {
- if f.String() == file || f.Rel() == file {
- return newTestingBuildParams(provider, p), nil
+ if f.String() == file || f.Rel() == file || PathRelativeToTop(f) == file {
+ return b.newTestingBuildParams(p), nil
}
searchedOutputs = append(searchedOutputs, f.Rel())
}
@@ -585,18 +729,18 @@
return TestingBuildParams{}, searchedOutputs
}
-func buildParamsFromOutput(provider testBuildProvider, file string) TestingBuildParams {
- p, searchedOutputs := maybeBuildParamsFromOutput(provider, file)
+func (b baseTestingComponent) buildParamsFromOutput(file string) TestingBuildParams {
+ p, searchedOutputs := b.maybeBuildParamsFromOutput(file)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
- file, searchedOutputs))
+ panic(fmt.Errorf("couldn't find output %q.\nall outputs:\n %s\n",
+ file, strings.Join(searchedOutputs, "\n ")))
}
return p
}
-func allOutputs(provider testBuildProvider) []string {
+func (b baseTestingComponent) allOutputs() []string {
var outputFullPaths []string
- for _, p := range provider.BuildParamsForTests() {
+ for _, p := range b.provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
@@ -607,64 +751,94 @@
return outputFullPaths
}
+// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeRule(rule string) TestingBuildParams {
+ r, _ := b.maybeBuildParamsFromRule(rule)
+ return r
+}
+
+// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
+func (b baseTestingComponent) Rule(rule string) TestingBuildParams {
+ return b.buildParamsFromRule(rule)
+}
+
+// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
+// BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
+ return b.maybeBuildParamsFromDescription(desc)
+}
+
+// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
+// found.
+func (b baseTestingComponent) Description(desc string) TestingBuildParams {
+ return b.buildParamsFromDescription(desc)
+}
+
+// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Returns an empty BuildParams if no rule is found.
+func (b baseTestingComponent) MaybeOutput(file string) TestingBuildParams {
+ p, _ := b.maybeBuildParamsFromOutput(file)
+ return p
+}
+
+// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
+// value matches the provided string. Panics if no rule is found.
+func (b baseTestingComponent) Output(file string) TestingBuildParams {
+ return b.buildParamsFromOutput(file)
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (b baseTestingComponent) AllOutputs() []string {
+ return b.allOutputs()
+}
+
// TestingModule is wrapper around an android.Module that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingModule struct {
+ baseTestingComponent
module Module
}
+func newTestingModule(config Config, module Module) TestingModule {
+ return TestingModule{
+ newBaseTestingComponent(config, module),
+ module,
+ }
+}
+
// Module returns the Module wrapped by the TestingModule.
func (m TestingModule) Module() Module {
return m.module
}
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeRule(rule string) TestingBuildParams {
- r, _ := maybeBuildParamsFromRule(m.module, rule)
- return r
+// VariablesForTestsRelativeToTop returns a copy of the Module.VariablesForTests() with every value
+// having any temporary build dir usages replaced with paths relative to a notional top.
+func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string {
+ return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
}
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
-func (m TestingModule) Rule(rule string) TestingBuildParams {
- return buildParamsFromRule(m.module, rule)
-}
+// OutputFiles calls OutputFileProducer.OutputFiles on the encapsulated module, exits the test
+// immediately if there is an error and otherwise returns the result of calling Paths.RelativeToTop
+// on the returned Paths.
+func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
+ producer, ok := m.module.(OutputFileProducer)
+ if !ok {
+ t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
+ }
+ paths, err := producer.OutputFiles(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
-// BuildParams if no rule is found.
-func (m TestingModule) MaybeDescription(desc string) TestingBuildParams {
- return maybeBuildParamsFromDescription(m.module, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
-// found.
-func (m TestingModule) Description(desc string) TestingBuildParams {
- return buildParamsFromDescription(m.module, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Returns an empty BuildParams if no rule is found.
-func (m TestingModule) MaybeOutput(file string) TestingBuildParams {
- p, _ := maybeBuildParamsFromOutput(m.module, file)
- return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Panics if no rule is found.
-func (m TestingModule) Output(file string) TestingBuildParams {
- return buildParamsFromOutput(m.module, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (m TestingModule) AllOutputs() []string {
- return allOutputs(m.module)
+ return paths.RelativeToTop()
}
// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
// ctx.Build parameters for verification in tests.
type TestingSingleton struct {
+ baseTestingComponent
singleton Singleton
- provider testBuildProvider
}
// Singleton returns the Singleton wrapped by the TestingSingleton.
@@ -672,48 +846,6 @@
return s.singleton
}
-// MaybeRule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeRule(rule string) TestingBuildParams {
- r, _ := maybeBuildParamsFromRule(s.provider, rule)
- return r
-}
-
-// Rule finds a call to ctx.Build with BuildParams.Rule set to a rule with the given name. Panics if no rule is found.
-func (s TestingSingleton) Rule(rule string) TestingBuildParams {
- return buildParamsFromRule(s.provider, rule)
-}
-
-// MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string. Returns an empty
-// BuildParams if no rule is found.
-func (s TestingSingleton) MaybeDescription(desc string) TestingBuildParams {
- return maybeBuildParamsFromDescription(s.provider, desc)
-}
-
-// Description finds a call to ctx.Build with BuildParams.Description set to a the given string. Panics if no rule is
-// found.
-func (s TestingSingleton) Description(desc string) TestingBuildParams {
- return buildParamsFromDescription(s.provider, desc)
-}
-
-// MaybeOutput finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Returns an empty BuildParams if no rule is found.
-func (s TestingSingleton) MaybeOutput(file string) TestingBuildParams {
- p, _ := maybeBuildParamsFromOutput(s.provider, file)
- return p
-}
-
-// Output finds a call to ctx.Build with a BuildParams.Output or BuildParams.Outputs whose String() or Rel()
-// value matches the provided string. Panics if no rule is found.
-func (s TestingSingleton) Output(file string) TestingBuildParams {
- return buildParamsFromOutput(s.provider, file)
-}
-
-// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
-func (s TestingSingleton) AllOutputs() []string {
- return allOutputs(s.provider)
-}
-
func FailIfErrored(t *testing.T, errs []error) {
t.Helper()
if len(errs) > 0 {
@@ -822,7 +954,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)
}
@@ -848,19 +980,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
@@ -877,23 +1004,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) {
@@ -941,3 +1058,20 @@
}
return result
}
+
+// StringRelativeToTop will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func StringRelativeToTop(config Config, command string) string {
+ return normalizeStringRelativeToTop(config, command)
+}
+
+// StringsRelativeToTop will return a new slice such that each item in the new slice is the result
+// of calling StringRelativeToTop on the corresponding item in the input slice.
+func StringsRelativeToTop(config Config, command []string) []string {
+ return normalizeStringArrayRelativeToTop(config, command)
+}
diff --git a/android/util.go b/android/util.go
index 506f8f7..a0394f6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -193,6 +193,17 @@
return
}
+// FilterListPred returns the elements of the given list for which the predicate
+// returns true. Order is kept.
+func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
+ for _, l := range list {
+ if pred(l) {
+ filtered = append(filtered, l)
+ }
+ }
+ return
+}
+
// RemoveListFromList removes the strings belonging to the filter list from the
// given list and returns the result
func RemoveListFromList(list []string, filter_out []string) (result []string) {
diff --git a/android/util_test.go b/android/util_test.go
index fa26c77..09bec01 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -18,6 +18,7 @@
"fmt"
"reflect"
"strconv"
+ "strings"
"testing"
)
@@ -299,6 +300,14 @@
}
}
+func TestFilterListPred(t *testing.T) {
+ pred := func(s string) bool { return strings.HasPrefix(s, "a/") }
+ AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "b/a", "a/b"}, pred), []string{"a/c", "a/b"})
+ AssertArrayString(t, "filter", FilterListPred([]string{"b/c", "a/a", "b/b"}, pred), []string{"a/a"})
+ AssertArrayString(t, "filter", FilterListPred([]string{"c/c", "b/a", "c/b"}, pred), []string{})
+ AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "a/a", "a/b"}, pred), []string{"a/c", "a/a", "a/b"})
+}
+
func TestRemoveListFromList(t *testing.T) {
input := []string{"a", "b", "c", "d", "a", "c", "d"}
filter := []string{"a", "c"}
diff --git a/android/variable.go b/android/variable.go
index 776a5c7..dff48c2 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -341,6 +341,8 @@
DexpreoptGlobalConfig *string `json:",omitempty"`
+ WithDexpreopt bool `json:",omitempty"`
+
ManifestPackageNameOverrides []string `json:",omitempty"`
CertificateOverrides []string `json:",omitempty"`
PackageNameOverrides []string `json:",omitempty"`
@@ -446,6 +448,63 @@
}
}
+// ProductConfigContext requires the access to the Module to get product config properties.
+type ProductConfigContext interface {
+ Module() Module
+}
+
+// ProductConfigProperty contains the information for a single property (may be a struct) paired
+// with the appropriate ProductConfigVariable.
+type ProductConfigProperty struct {
+ ProductConfigVariable string
+ Property interface{}
+}
+
+// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
+// all it all product variable-specific versions of a property are easily accessed together
+type ProductConfigProperties map[string][]ProductConfigProperty
+
+// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
+// have been set for the module in the given context.
+func ProductVariableProperties(ctx ProductConfigContext) ProductConfigProperties {
+ module := ctx.Module()
+ moduleBase := module.base()
+
+ productConfigProperties := ProductConfigProperties{}
+
+ if moduleBase.variableProperties == nil {
+ return productConfigProperties
+ }
+
+ variableValues := reflect.ValueOf(moduleBase.variableProperties).Elem().FieldByName("Product_variables")
+ for i := 0; i < variableValues.NumField(); i++ {
+ variableValue := variableValues.Field(i)
+ // Check if any properties were set for the module
+ if variableValue.IsZero() {
+ continue
+ }
+ // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
+ productVariableName := variableValues.Type().Field(i).Name
+ for j := 0; j < variableValue.NumField(); j++ {
+ property := variableValue.Field(j)
+ // If the property wasn't set, no need to pass it along
+ if property.IsZero() {
+ continue
+ }
+
+ // e.g. Asflags, Cflags, Enabled, etc.
+ propertyName := variableValue.Type().Field(j).Name
+ productConfigProperties[propertyName] = append(productConfigProperties[propertyName],
+ ProductConfigProperty{
+ ProductConfigVariable: productVariableName,
+ Property: property.Interface(),
+ })
+ }
+ }
+
+ return productConfigProperties
+}
+
func VariableMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool
diff --git a/android/variable_test.go b/android/variable_test.go
index d16e458..928bca6 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -182,7 +182,7 @@
}
`
- emptyTestFixtureFactory.RunTest(t,
+ GroupFixturePreparers(
FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.Eng = proptools.BoolPtr(true)
}),
@@ -204,7 +204,7 @@
})
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
}
var testProductVariableDefaultsProperties = struct {
@@ -288,7 +288,7 @@
}
`
- result := emptyTestFixtureFactory.RunTest(t,
+ result := GroupFixturePreparers(
FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.Eng = boolPtr(true)
}),
@@ -299,7 +299,7 @@
ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
}),
FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
foo := result.ModuleForTests("foo", "").Module().(*productVariablesDefaultsTestModule)
diff --git a/android/visibility_test.go b/android/visibility_test.go
index fdf18ce..ffd7909 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1142,7 +1142,7 @@
func TestVisibility(t *testing.T) {
for _, test := range visibilityTests {
t.Run(test.name, func(t *testing.T) {
- result := emptyTestFixtureFactory.Extend(
+ result := GroupFixturePreparers(
// General preparers in alphabetical order as test infrastructure will enforce correct
// registration order.
PrepareForTestWithArchMutator,
diff --git a/apex/apex.go b/apex/apex.go
index a12f3d2..9d06e1c 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"
@@ -94,8 +93,14 @@
Multilib apexMultilibProperties
// List of boot images that are embedded inside this APEX bundle.
+ //
+ // deprecated: Use Bootclasspath_fragments
+ // TODO(b/177892522): Remove after has been replaced by Bootclasspath_fragments
Boot_images []string
+ // List of bootclasspath fragments that are embedded inside this APEX bundle.
+ Bootclasspath_fragments []string
+
// List of java libraries that are embedded inside this APEX bundle.
Java_libs []string
@@ -568,7 +573,7 @@
certificateTag = dependencyTag{name: "certificate"}
executableTag = dependencyTag{name: "executable", payload: true}
fsTag = dependencyTag{name: "filesystem", payload: true}
- bootImageTag = dependencyTag{name: "bootImage", payload: true}
+ bootImageTag = dependencyTag{name: "bootImage", payload: true, sourceOnly: true}
compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
javaLibTag = dependencyTag{name: "javaLib", payload: true}
jniLibTag = dependencyTag{name: "jniLib", payload: true}
@@ -749,6 +754,7 @@
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Boot_images...)
+ ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Bootclasspath_fragments...)
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
@@ -854,12 +860,7 @@
if required, ok := depTag.(android.AlwaysRequireApexVariantTag); ok && required.AlwaysRequireApexVariant() {
return true
}
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- // The tag defines a dependency that never requires the child module to be part of the same
- // apex as the parent so it does not need an apex variant created.
- return false
- }
- if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+ if !android.IsDepInSameApex(mctx, parent, child) {
return false
}
if excludeVndkLibs {
@@ -1003,11 +1004,7 @@
// If any of the dep is not available to platform, this module is also considered as being
// not available to platform even if it has "//apex_available:platform"
mctx.VisitDirectDeps(func(child android.Module) {
- depTag := mctx.OtherModuleDependencyTag(child)
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- return
- }
- if !am.DepIsInSameApex(mctx, child) {
+ if !android.IsDepInSameApex(mctx, am, child) {
// if the dependency crosses apex boundary, don't consider it
return
}
@@ -1582,9 +1579,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)
@@ -1708,6 +1702,9 @@
filesInfo = append(filesInfo, af)
}
}
+
+ // Track transitive dependencies.
+ return true
}
case javaLibTag:
switch child.(type) {
@@ -1872,7 +1869,10 @@
// like to record requiredNativeLibs even when
// DepIsInSameAPex is false. We also shouldn't do
// this for host.
- if !am.DepIsInSameApex(ctx, am) {
+ //
+ // TODO(jiyong): explain why the same module is passed in twice.
+ // Switching the first am to parent breaks lots of tests.
+ if !android.IsDepInSameApex(ctx, am, am) {
return false
}
@@ -1913,6 +1913,21 @@
filesInfo = append(filesInfo, af)
return true // track transitive dependencies
}
+ } else if java.IsbootImageContentDepTag(depTag) {
+ // Add the contents of the boot image to the apex.
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ if !af.ok() {
+ ctx.PropertyErrorf("boot_images", "boot image content %q is not configured to be compiled into dex", depName)
+ return false
+ }
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("boot_images", "boot image content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+
} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
// nothing
} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
@@ -2195,6 +2210,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go further.
return false
@@ -2278,6 +2295,8 @@
// If `to` is not actually in the same APEX as `from` then it does not need
// apex_available and neither do any of its dependencies.
+ //
+ // It is ok to call DepIsInSameApex() directly from within WalkPayloadDeps().
if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
// As soon as the dependency graph crosses the APEX boundary, don't go
// further.
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 3f2e30e..cc77ab7 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path"
"path/filepath"
@@ -39,8 +38,6 @@
"android/soong/sh"
)
-var buildDir string
-
// names returns name list from white space separated string
func names(s string) (ns []string) {
for _, n := range strings.Split(s, " ") {
@@ -118,10 +115,8 @@
},
)
-var emptyFixtureFactory = android.NewFixtureFactory(&buildDir)
-
var apexFixtureFactory = android.NewFixtureFactory(
- &buildDir,
+ nil,
// General preparers in alphabetical order as test infrastructure will enforce correct
// registration order.
android.PrepareForTestWithAndroidBuildComponents,
@@ -143,14 +138,13 @@
],
}
`),
+ prepareForTestWithMyapex,
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.updatable-file_contexts": nil,
"system/sepolicy/apex/myapex2-file_contexts": nil,
"system/sepolicy/apex/otherapex-file_contexts": nil,
@@ -208,17 +202,9 @@
}),
)
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_apex_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- _ = os.RemoveAll(buildDir)
-}
+var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/myapex-file_contexts": nil,
+})
// ensure that 'result' equals 'expected'
func ensureEquals(t *testing.T, result string, expected string) {
@@ -515,7 +501,7 @@
optFlags := apexRule.Args["opt_flags"]
ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
// Ensure that the NOTICE output is being packaged as an asset.
- ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex_image/NOTICE")
+ ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex_image/NOTICE")
copyCmds := apexRule.Args["copy_commands"]
@@ -1189,7 +1175,7 @@
)
func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
- result := emptyFixtureFactory.Extend(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
+ result := android.GroupFixturePreparers(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, `
cc_library {
name: "libc",
no_libcrt: true,
@@ -1235,7 +1221,7 @@
}
func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
- result := emptyFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
prepareForTestOfRuntimeApexWithHwasan,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.SanitizeDevice = []string{"hwaddress"}
@@ -2477,8 +2463,8 @@
prefix := "TARGET_"
var builder strings.Builder
data.Custom(&builder, name, prefix, "", data)
- androidMk := builder.String()
- installPath := path.Join(buildDir, "../target/product/test_device/vendor/apex")
+ androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+ installPath := "out/target/product/test_device/vendor/apex"
ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
@@ -2525,10 +2511,10 @@
ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
libs := names(ldRule.Args["libFlags"])
// VNDK libs(libvndk/libc++) as they are
- ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
- ensureListContains(t, libs, buildDir+"/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
// non-stable Vendor libs as APEX variants
- ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+ ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
// VNDK libs are not included when use_vndk_as_stable: true
ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -4101,15 +4087,15 @@
`)
apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
- expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex"
- actual := apex.installDir.String()
+ expected := "out/soong/target/product/test_device/" + tc.parition + "/apex"
+ actual := apex.installDir.RelativeToTop().String()
if actual != expected {
t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
}
flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
- expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex"
- actual = flattened.installDir.String()
+ expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex"
+ actual = flattened.installDir.RelativeToTop().String()
if actual != expected {
t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
}
@@ -4503,14 +4489,11 @@
if filepath.Base(output) == base {
foundLibfooJar = true
buildRule := s.Output(output)
- actual := android.NormalizePathForTesting(buildRule.Input)
- if actual != bootDexJarPath {
- t.Errorf("Incorrect boot dex jar path '%s', expected '%s'", actual, bootDexJarPath)
- }
+ android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String())
}
}
if !foundLibfooJar {
- t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs")
+ t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
}
}
@@ -4552,8 +4535,8 @@
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
- checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
- checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
@@ -4659,8 +4642,8 @@
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
- checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
- checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
@@ -4726,8 +4709,8 @@
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
- checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
- checkBootDexJarPath(t, ctx, "libbar", ".intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
@@ -4795,8 +4778,8 @@
`
ctx := testDexpreoptWithApexes(t, bp, "", transform)
- checkBootDexJarPath(t, ctx, "libfoo", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
- checkBootDexJarPath(t, ctx, "libbar", ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
+ checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar")
+ checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
// Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file.
checkHiddenAPIIndexInputs(t, ctx, `
@@ -5090,7 +5073,7 @@
}
// JNI libraries including transitive deps are
for _, jni := range []string{"libjni", "libfoo"} {
- jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile()
+ jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop()
// ... embedded inside APK (jnilibs.zip)
ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
// ... and not directly inside the APEX
@@ -5728,7 +5711,7 @@
// The bar library should depend on the implementation jar.
barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -5779,7 +5762,7 @@
// The bar library should depend on the stubs jar.
barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -5869,7 +5852,7 @@
// The bar library should depend on the implementation jar.
barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
- if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -6340,7 +6323,7 @@
)
m := ctx.ModuleForTests("myapex", "android_common")
- extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+ extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
actual := extractedApex.Inputs
if len(actual) != 1 {
@@ -6447,68 +6430,46 @@
func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
t.Helper()
- bp += cc.GatherRequiredDepsForTest(android.Android)
- bp += java.GatherRequiredDepsForTest()
-
- fs := map[string][]byte{
- "a.java": nil,
- "a.jar": nil,
- "build/make/target/product/security": nil,
- "apex_manifest.json": nil,
- "AndroidManifest.xml": nil,
+ fs := android.MockFS{
+ "a.java": nil,
+ "a.jar": nil,
+ "apex_manifest.json": nil,
+ "AndroidManifest.xml": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
"system/sepolicy/apex/some-updatable-apex-file_contexts": nil,
"system/sepolicy/apex/some-non-updatable-apex-file_contexts": nil,
"system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
"framework/aidl/a.aidl": nil,
}
- cc.GatherRequiredFilesForTest(fs)
- for k, v := range filesForSdkLibrary {
- fs[k] = v
- }
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_key", ApexKeyFactory)
- ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(android.RegisterComponentsMutator)
- android.RegisterPrebuiltMutators(ctx)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterHiddenApiSingletonComponents(ctx)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
-
- ctx.Register()
-
- pathCtx := android.PathContextForTesting(config)
- dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
- transformDexpreoptConfig(dexpreoptConfig)
- dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
-
- // Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
- // product variables.
- config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
- config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- if errmsg == "" {
- android.FailIfErrored(t, errs)
- } else if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, errmsg, errs)
- } else {
- t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+ errorHandler := android.FixtureExpectsNoErrors
+ if errmsg != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
}
- return ctx
+ result := android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ java.PrepareForTestWithHiddenApiBuildComponents,
+ java.PrepareForTestWithJavaDefaultModules,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ PrepareForTestWithApexBuildComponents,
+ android.FixtureModifyConfig(func(config android.Config) {
+ pathCtx := android.PathContextForTesting(config)
+ dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
+ transformDexpreoptConfig(dexpreoptConfig)
+ dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
+
+ // Make sure that any changes to these dexpreopt properties are mirrored in the corresponding
+ // product variables.
+ config.TestProductVariables.BootJars = dexpreoptConfig.BootJars
+ config.TestProductVariables.UpdatableBootJars = dexpreoptConfig.UpdatableBootJars
+ }),
+ fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
+
+ return result.TestContext
}
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -6682,45 +6643,33 @@
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}`
- fs := map[string][]byte{
+ fs := android.MockFS{
"lib1/src/A.java": nil,
"lib2/src/B.java": nil,
"system/sepolicy/apex/myapex-file_contexts": nil,
}
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- android.SetTestNeverallowRules(config, rules)
- updatableBootJars := make([]string, 0, len(apexBootJars))
- for _, apexBootJar := range apexBootJars {
- updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+ errorHandler := android.FixtureExpectsNoErrors
+ if errmsg != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(errmsg)
}
- config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("apex", BundleFactory)
- ctx.RegisterModuleType("apex_key", ApexKeyFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterRequiredBuildComponentsForTest(ctx)
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.PreDepsMutators(RegisterPreDepsMutators)
- ctx.PostDepsMutators(RegisterPostDepsMutators)
- ctx.PostDepsMutators(android.RegisterNeverallowMutator)
-
- ctx.Register()
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- if errmsg == "" {
- android.FailIfErrored(t, errs)
- } else if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, errmsg, errs)
- return
- } else {
- t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
- }
+ android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ java.PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ android.PrepareForTestWithNeverallowRules(rules),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ updatableBootJars := make([]string, 0, len(apexBootJars))
+ for _, apexBootJar := range apexBootJars {
+ updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+ }
+ variables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+ }),
+ fs.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
func TestApexPermittedPackagesRules(t *testing.T) {
@@ -6872,21 +6821,77 @@
}
`)
- // the test 'mytest' is a test for the apex, therefore is linked to the
+ ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+ ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+ mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+ android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+ }
+
+ // These modules are tests for the apex, therefore are linked to the
// actual implementation of mylib instead of its stub.
- ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+ ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+ ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+ ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+}
- // The same should be true for cc_library
- ldFlags = ctx.ModuleForTests("mytestlib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+func TestIndirectTestFor(t *testing.T) {
+ ctx := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib", "myprivlib"],
+ updatable: false,
+ }
- // ... and for cc_benchmark
- ldFlags = ctx.ModuleForTests("mybench", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
- ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
- ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1"],
+ },
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "myprivlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ shared_libs: ["mylib"],
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "mytestlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ shared_libs: ["myprivlib"],
+ stl: "none",
+ test_for: ["myapex"],
+ }
+ `)
+
+ ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+ ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+ mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+ android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+ }
+
+ // The platform variant of mytestlib links to the platform variant of the
+ // internal myprivlib.
+ ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
+
+ // The platform variant of myprivlib links to the platform variant of mylib
+ // and bypasses its stubs.
+ ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
}
// TODO(jungjw): Move this to proptools
@@ -6917,7 +6922,7 @@
m := ctx.ModuleForTests("myapex", "android_common")
// Check extract_apks tool parameters.
- extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+ extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
actual := extractedApex.Args["abis"]
expected := "ARMEABI_V7A,ARM64_V8A"
if actual != expected {
@@ -7408,12 +7413,5 @@
}
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 7e37e42..574166a 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -26,23 +26,32 @@
// Contains tests for boot_image logic from java/boot_image.go as the ART boot image requires
// modules from the ART apex.
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+ java.PrepareForTestWithDexpreopt,
+ PrepareForTestWithApexBuildComponents,
+)
+
+// Some additional files needed for the art apex.
+var prepareForTestWithArtApex = android.FixtureMergeMockFs(android.MockFS{
+ "com.android.art.avbpubkey": nil,
+ "com.android.art.pem": nil,
+ "system/sepolicy/apex/com.android.art-file_contexts": nil,
+})
+
func TestBootImages(t *testing.T) {
- result := apexFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
// Configure some libraries in the art and framework boot images.
dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
- filesForSdkLibrary.AddToFixture(),
- // Some additional files needed for the art apex.
- android.FixtureMergeMockFs(android.MockFS{
- "com.android.art.avbpubkey": nil,
- "com.android.art.pem": nil,
- "system/sepolicy/apex/com.android.art-file_contexts": nil,
- }),
+ prepareForTestWithArtApex,
+
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
java_sdk_library {
name: "foo",
srcs: ["b.java"],
- unsafe_ignore_missing_latest_api: true,
}
java_library {
@@ -86,6 +95,9 @@
boot_image {
name: "art-boot-image",
image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
}
boot_image {
@@ -151,10 +163,163 @@
android.AssertTrimmedStringEquals(t, "invalid paths for "+moduleName, expectedBootImageFiles, strings.Join(allPaths, "\n"))
}
-func TestBootImageInApex(t *testing.T) {
- result := apexFixtureFactory.Extend(
- // Configure some libraries in the framework boot image.
- dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+func TestBootImageInArtApex(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithArtApex,
+
+ // Configure some libraries in the art boot image.
+ dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+ ).RunTestWithBp(t, `
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ boot_images: [
+ "mybootimage",
+ ],
+ // bar (like foo) should be transitively included in this apex because it is part of the
+ // mybootimage boot_image. However, it is kept here to ensure that the apex dedups the files
+ // correctly.
+ java_libs: [
+ "bar",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ // Make sure that a preferred prebuilt doesn't affect the apex.
+ prebuilt_boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ prefer: true,
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "javalib/arm/boot.art",
+ "javalib/arm/boot.oat",
+ "javalib/arm/boot.vdex",
+ "javalib/arm/boot-bar.art",
+ "javalib/arm/boot-bar.oat",
+ "javalib/arm/boot-bar.vdex",
+ "javalib/arm64/boot.art",
+ "javalib/arm64/boot.oat",
+ "javalib/arm64/boot.vdex",
+ "javalib/arm64/boot-bar.art",
+ "javalib/arm64/boot-bar.oat",
+ "javalib/arm64/boot-bar.vdex",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ `bar`,
+ `com.android.art.key`,
+ `mybootimage`,
+ })
+}
+
+func TestBootImageInPrebuiltArtApex(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithArtApex,
+
+ android.FixtureMergeMockFs(android.MockFS{
+ "com.android.art-arm64.apex": nil,
+ "com.android.art-arm.apex": nil,
+ }),
+
+ // Configure some libraries in the art boot image.
+ dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+ ).RunTestWithBp(t, `
+ prebuilt_apex {
+ name: "com.android.art",
+ arch: {
+ arm64: {
+ src: "com.android.art-arm64.apex",
+ },
+ arm: {
+ src: "com.android.art-arm.apex",
+ },
+ },
+ exported_java_libs: ["foo", "bar"],
+ }
+
+ java_import {
+ name: "foo",
+ jars: ["foo.jar"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ java_import {
+ name: "bar",
+ jars: ["bar.jar"],
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+
+ prebuilt_boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: [
+ "com.android.art",
+ ],
+ }
+ `)
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+ `prebuilt_bar`,
+ `prebuilt_foo`,
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "mybootimage", "android_common", []string{
+ `dex2oatd`,
+ `prebuilt_bar`,
+ `prebuilt_foo`,
+ })
+}
+
+func TestBootImageContentsNoName(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ prepareForTestWithMyapex,
).RunTestWithBp(t, `
apex {
name: "myapex",
@@ -175,17 +340,26 @@
name: "foo",
srcs: ["b.java"],
installable: true,
+ apex_available: [
+ "myapex",
+ ],
}
java_library {
name: "bar",
srcs: ["b.java"],
installable: true,
+ apex_available: [
+ "myapex",
+ ],
}
boot_image {
name: "mybootimage",
- image_name: "boot",
+ contents: [
+ "foo",
+ "bar",
+ ],
apex_available: [
"myapex",
],
@@ -193,18 +367,15 @@
`)
ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
- "javalib/arm/boot-bar.art",
- "javalib/arm/boot-bar.oat",
- "javalib/arm/boot-bar.vdex",
- "javalib/arm/boot-foo.art",
- "javalib/arm/boot-foo.oat",
- "javalib/arm/boot-foo.vdex",
- "javalib/arm64/boot-bar.art",
- "javalib/arm64/boot-bar.oat",
- "javalib/arm64/boot-bar.vdex",
- "javalib/arm64/boot-foo.art",
- "javalib/arm64/boot-foo.oat",
- "javalib/arm64/boot-foo.vdex",
+ // This does not include art, oat or vdex files as they are only included for the art boot
+ // image.
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ `myapex.key`,
+ `mybootimage`,
})
}
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/bazel/properties.go b/bazel/properties.go
index abdc107..1763f2d 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "regexp"
"sort"
)
@@ -31,6 +32,8 @@
const BazelTargetModuleNamePrefix = "__bp2build__"
+var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
+
// Label is used to represent a Bazel compatible Label. Also stores the original bp text to support
// string replacement.
type Label struct {
@@ -76,6 +79,92 @@
return uniqueLabelList
}
+const (
+ ARCH_X86 = "x86"
+ ARCH_X86_64 = "x86_64"
+ ARCH_ARM = "arm"
+ ARCH_ARM64 = "arm64"
+)
+
+var (
+ // This is the list of architectures with a Bazel config_setting and
+ // constraint value equivalent. is actually android.ArchTypeList, but the
+ // android package depends on the bazel package, so a cyclic dependency
+ // prevents using that here.
+ selectableArchs = []string{ARCH_X86, ARCH_X86_64, ARCH_ARM, ARCH_ARM64}
+)
+
+// Arch-specific label_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type labelListArchValues struct {
+ X86 LabelList
+ X86_64 LabelList
+ Arm LabelList
+ Arm64 LabelList
+ // TODO(b/181299724): this is currently missing the "common" arch, which
+ // doesn't have an equivalent platform() definition yet.
+}
+
+// LabelListAttribute is used to represent a list of Bazel labels as an
+// attribute.
+type LabelListAttribute struct {
+ // The non-arch specific attribute label list Value. Required.
+ Value LabelList
+
+ // The arch-specific attribute label list values. Optional. If used, these
+ // are generated in a select statement and appended to the non-arch specific
+ // label list Value.
+ ArchValues labelListArchValues
+}
+
+// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
+func MakeLabelListAttribute(value LabelList) LabelListAttribute {
+ return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+}
+
+// HasArchSpecificValues returns true if the attribute contains
+// architecture-specific label_list values.
+func (attrs *LabelListAttribute) HasArchSpecificValues() bool {
+ for _, arch := range selectableArchs {
+ if len(attrs.GetValueForArch(arch).Includes) > 0 || len(attrs.GetValueForArch(arch).Excludes) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// GetValueForArch returns the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
+ switch arch {
+ case ARCH_X86:
+ return attrs.ArchValues.X86
+ case ARCH_X86_64:
+ return attrs.ArchValues.X86_64
+ case ARCH_ARM:
+ return attrs.ArchValues.Arm
+ case ARCH_ARM64:
+ return attrs.ArchValues.Arm64
+ default:
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+}
+
+// SetValueForArch sets the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
+ switch arch {
+ case "x86":
+ attrs.ArchValues.X86 = value
+ case "x86_64":
+ attrs.ArchValues.X86_64 = value
+ case "arm":
+ attrs.ArchValues.Arm = value
+ case "arm64":
+ attrs.ArchValues.Arm64 = value
+ default:
+ panic(fmt.Errorf("Unknown arch: %s", arch))
+ }
+}
+
// StringListAttribute corresponds to the string_list Bazel attribute type with
// support for additional metadata, like configurations.
type StringListAttribute struct {
@@ -89,11 +178,10 @@
// Arch-specific string_list typed Bazel attribute values. This should correspond
// to the types of architectures supported for compilation in arch.go.
type stringListArchValues struct {
- X86 []string
- X86_64 []string
- Arm []string
- Arm64 []string
- Default []string
+ X86 []string
+ X86_64 []string
+ Arm []string
+ Arm64 []string
// TODO(b/181299724): this is currently missing the "common" arch, which
// doesn't have an equivalent platform() definition yet.
}
@@ -101,7 +189,7 @@
// HasArchSpecificValues returns true if the attribute contains
// architecture-specific string_list values.
func (attrs *StringListAttribute) HasArchSpecificValues() bool {
- for _, arch := range []string{"x86", "x86_64", "arm", "arm64", "default"} {
+ for _, arch := range selectableArchs {
if len(attrs.GetValueForArch(arch)) > 0 {
return true
}
@@ -112,16 +200,14 @@
// GetValueForArch returns the string_list attribute value for an architecture.
func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
switch arch {
- case "x86":
+ case ARCH_X86:
return attrs.ArchValues.X86
- case "x86_64":
+ case ARCH_X86_64:
return attrs.ArchValues.X86_64
- case "arm":
+ case ARCH_ARM:
return attrs.ArchValues.Arm
- case "arm64":
+ case ARCH_ARM64:
return attrs.ArchValues.Arm64
- case "default":
- return attrs.ArchValues.Default
default:
panic(fmt.Errorf("Unknown arch: %s", arch))
}
@@ -130,17 +216,35 @@
// SetValueForArch sets the string_list attribute value for an architecture.
func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
switch arch {
- case "x86":
+ case ARCH_X86:
attrs.ArchValues.X86 = value
- case "x86_64":
+ case ARCH_X86_64:
attrs.ArchValues.X86_64 = value
- case "arm":
+ case ARCH_ARM:
attrs.ArchValues.Arm = value
- case "arm64":
+ case ARCH_ARM64:
attrs.ArchValues.Arm64 = value
- case "default":
- attrs.ArchValues.Default = value
default:
panic(fmt.Errorf("Unknown arch: %s", arch))
}
}
+
+// TryVariableSubstitution, replace string substitution formatting within each string in slice with
+// Starlark string.format compatible tag for productVariable.
+func TryVariableSubstitutions(slice []string, productVariable string) ([]string, bool) {
+ ret := make([]string, 0, len(slice))
+ changesMade := false
+ for _, s := range slice {
+ newS, changed := TryVariableSubstitution(s, productVariable)
+ ret = append(ret, newS)
+ changesMade = changesMade || changed
+ }
+ return ret, changesMade
+}
+
+// TryVariableSubstitution, replace string substitution formatting within s with Starlark
+// string.format compatible tag for productVariable.
+func TryVariableSubstitution(s string, productVariable string) (string, bool) {
+ sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
+ return sub, s != sub
+}
diff --git a/bloaty/Android.bp b/bloaty/Android.bp
index b1f1e39..96cc1a5 100644
--- a/bloaty/Android.bp
+++ b/bloaty/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
bootstrap_go_package {
name: "soong-bloaty",
pkgPath: "android/soong/bloaty",
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 21bf4ac..653c489 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -22,7 +22,7 @@
"github.com/google/blueprint"
)
-const bloatyDescriptorExt = "bloaty.csv"
+const bloatyDescriptorExt = ".bloaty.csv"
const protoFilename = "binary_sizes.pb"
var (
@@ -75,7 +75,7 @@
return
}
filePath := ctx.ModuleProvider(m, fileSizeMeasurerKey).(android.ModuleOutPath)
- sizeFile := filePath.ReplaceExtension(ctx, bloatyDescriptorExt)
+ sizeFile := filePath.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
ctx.Build(pctx, android.BuildParams{
Rule: bloaty,
Description: "bloaty " + filePath.Rel(),
diff --git a/bootstrap_test.sh b/bootstrap_test.sh
index 87f5e31..6c5338a 100755
--- a/bootstrap_test.sh
+++ b/bootstrap_test.sh
@@ -235,6 +235,86 @@
grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
}
+function test_soong_build_rerun_iff_environment_changes() {
+ setup
+
+ mkdir -p cherry
+ cat > cherry/Android.bp <<'EOF'
+bootstrap_go_package {
+ name: "cherry",
+ pkgPath: "android/soong/cherry",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "cherry.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+EOF
+
+ cat > cherry/cherry.go <<'EOF'
+package cherry
+
+import (
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+var (
+ pctx = android.NewPackageContext("cherry")
+)
+
+func init() {
+ android.RegisterSingletonType("cherry", CherrySingleton)
+}
+
+func CherrySingleton() android.Singleton {
+ return &cherrySingleton{}
+}
+
+type cherrySingleton struct{}
+
+func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ cherryRule := ctx.Rule(pctx, "cherry",
+ blueprint.RuleParams{
+ Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
+ CommandDeps: []string{},
+ Description: "Cherry",
+ })
+
+ outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
+ var deps android.Paths
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: cherryRule,
+ Output: outputFile,
+ Inputs: deps,
+ })
+}
+EOF
+
+ export CHERRY=TASTY
+ run_soong
+ grep -q "CHERRY IS TASTY" out/soong/build.ninja \
+ || fail "first value of environment variable is not used"
+
+ export CHERRY=RED
+ run_soong
+ grep -q "CHERRY IS RED" out/soong/build.ninja \
+ || fail "second value of environment variable not used"
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed when environment variable did not"
+ fi
+
+}
+
function test_add_file_to_soong_build() {
setup
run_soong
@@ -308,12 +388,28 @@
grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
}
+function test_null_build_after_docs {
+ setup
+ run_soong
+ local mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+ prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
+ run_soong
+ local mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+ if [[ "$mtime1" != "$mtime2" ]]; then
+ fail "Output Ninja file changed on null build"
+ fi
+}
+
test_bazel_smoke
test_smoke
test_null_build
+test_null_build_after_docs
test_soong_build_rebuilt_if_blueprint_changes
test_add_file_to_glob
test_add_android_bp
test_change_android_bp
test_delete_android_bp
test_add_file_to_soong_build
+test_soong_build_rerun_iff_environment_changes
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.go b/bp2build/build_conversion.go
index 9c98c76..e93b3dc 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -370,9 +370,20 @@
// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
//
- // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
- // value of unset attributes.
- return "", nil
+ // In Bazel-parlance, we would use "attr.<type>(default = <default
+ // value>)" to set the default value of unset attributes. In the cases
+ // where the bp2build converter didn't set the default value within the
+ // mutator when creating the BazelTargetModule, this would be a zero
+ // value. For those cases, we return a non-surprising default value so
+ // generated BUILD files are syntactically correct.
+ switch propertyValue.Kind() {
+ case reflect.Slice:
+ return "[]", nil
+ case reflect.Map:
+ return "{}", nil
+ default:
+ return "", nil
+ }
}
var ret string
@@ -404,9 +415,34 @@
case reflect.Struct:
// Special cases where the bp2build sends additional information to the codegenerator
// by wrapping the attributes in a custom struct type.
- if labels, ok := propertyValue.Interface().(bazel.LabelList); ok {
+ if labels, ok := propertyValue.Interface().(bazel.LabelListAttribute); ok {
// TODO(b/165114590): convert glob syntax
- return prettyPrint(reflect.ValueOf(labels.Includes), indent)
+ ret, err := prettyPrint(reflect.ValueOf(labels.Value.Includes), indent)
+ if err != nil {
+ return ret, err
+ }
+
+ if !labels.HasArchSpecificValues() {
+ // Select statement not needed.
+ return ret, nil
+ }
+
+ ret += " + " + "select({\n"
+ for _, arch := range android.ArchTypeList() {
+ value := labels.GetValueForArch(arch.Name)
+ if len(value.Includes) > 0 {
+ ret += makeIndent(indent + 1)
+ list, _ := prettyPrint(reflect.ValueOf(value.Includes), indent+1)
+ ret += fmt.Sprintf("\"%s\": %s,\n", platformArchMap[arch], list)
+ }
+ }
+
+ ret += makeIndent(indent + 1)
+ ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
+
+ ret += makeIndent(indent)
+ ret += "})"
+ return ret, err
} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
return fmt.Sprintf("%q", label.Label), nil
} else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok {
@@ -432,8 +468,7 @@
}
ret += makeIndent(indent + 1)
- list, _ := prettyPrint(reflect.ValueOf(stringList.GetValueForArch("default")), indent+1)
- ret += fmt.Sprintf("\"%s\": %s,\n", "//conditions:default", list)
+ ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
ret += makeIndent(indent)
ret += "})"
@@ -533,6 +568,13 @@
func escapeString(s string) string {
s = strings.ReplaceAll(s, "\\", "\\\\")
+
+ // b/184026959: Reverse the application of some common control sequences.
+ // These must be generated literally in the BUILD file.
+ s = strings.ReplaceAll(s, "\t", "\\t")
+ s = strings.ReplaceAll(s, "\n", "\\n")
+ s = strings.ReplaceAll(s, "\r", "\\r")
+
return strings.ReplaceAll(s, "\"", "\\\"")
}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 89acbe9..49897b3 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -241,6 +241,22 @@
string_prop = "a",
)`,
},
+ {
+ bp: `custom {
+ name: "control_characters",
+ string_list_prop: ["\t", "\n"],
+ string_prop: "a\t\n\r",
+ bazel_module: { bp2build_available: true },
+}`,
+ expectedBazelTarget: `custom(
+ name = "control_characters",
+ string_list_prop = [
+ "\t",
+ "\n",
+ ],
+ string_prop = "a\t\n\r",
+)`,
+ },
}
dir := "."
@@ -412,7 +428,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"})
@@ -502,8 +518,6 @@
expectedBazelTargets: []string{
`filegroup(
name = "fg_foo",
- srcs = [
- ],
)`,
},
},
@@ -1101,8 +1115,8 @@
"out",
],
srcs = [
- "srcs-from-3",
"in1",
+ "srcs-from-3",
],
)`,
description: "genrule applies properties from genrule_defaults transitively",
@@ -1144,7 +1158,7 @@
}
}
-func TestAllowlistingBp2buildTargets(t *testing.T) {
+func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
testCases := []struct {
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
@@ -1222,6 +1236,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/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1d4e322..9461739 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -70,11 +70,12 @@
],
local_include_dirs = [
"include",
+ ".",
],
srcs = [
"a/b/bar.h",
- "a/b/foo.h",
"a/b/c.c",
+ "a/b/foo.h",
],
)`,
},
@@ -120,6 +121,7 @@
],
local_include_dirs = [
"include",
+ ".",
],
srcs = [
"a/b/c.c",
@@ -156,6 +158,9 @@
copts = [
"-fno-addrsig",
],
+ local_include_dirs = [
+ ".",
+ ],
srcs = [
"x/y/z.c",
],
@@ -167,12 +172,71 @@
deps = [
":bar",
],
+ local_include_dirs = [
+ ".",
+ ],
srcs = [
"a/b/c.c",
],
)`,
},
},
+ {
+ description: "cc_object with include_build_dir: false",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ filesystem: map[string]string{
+ "a/b/c.c": "",
+ "x/y/z.c": "",
+ },
+ blueprint: `cc_object {
+ name: "foo",
+ srcs: ["a/b/c.c"],
+ include_build_directory: false,
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ copts = [
+ "-fno-addrsig",
+ ],
+ srcs = [
+ "a/b/c.c",
+ ],
+)`,
+ },
+ },
+ {
+ description: "cc_object with product variable",
+ moduleTypeUnderTest: "cc_object",
+ moduleTypeUnderTestFactory: cc.ObjectFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+ blueprint: `cc_object {
+ name: "foo",
+ include_build_directory: false,
+ product_variables: {
+ platform_sdk_version: {
+ asflags: ["-DPLATFORM_SDK_VERSION=%d"],
+ },
+ },
+
+ bazel_module: { bp2build_available: true },
+}
+`,
+ expectedBazelTargets: []string{`cc_object(
+ name = "foo",
+ asflags = [
+ "-DPLATFORM_SDK_VERSION={Platform_sdk_version}",
+ ],
+ copts = [
+ "-fno-addrsig",
+ ],
+)`,
+ },
+ },
}
dir := "."
@@ -242,9 +306,13 @@
moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
blueprint: `cc_object {
name: "foo",
+ srcs: ["a.cpp"],
arch: {
x86: {
- cflags: ["-fPIC"],
+ cflags: ["-fPIC"], // string list
+ },
+ arm: {
+ srcs: ["arch/arm/file.S"], // label list
},
},
bazel_module: { bp2build_available: true },
@@ -259,8 +327,18 @@
"@bazel_tools//platforms:x86_32": [
"-fPIC",
],
- "//conditions:default": [
+ "//conditions:default": [],
+ }),
+ local_include_dirs = [
+ ".",
+ ],
+ srcs = [
+ "a.cpp",
+ ] + select({
+ "@bazel_tools//platforms:arm": [
+ "arch/arm/file.S",
],
+ "//conditions:default": [],
}),
)`,
},
@@ -272,17 +350,22 @@
moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
blueprint: `cc_object {
name: "foo",
+ srcs: ["base.cpp"],
arch: {
x86: {
+ srcs: ["x86.cpp"],
cflags: ["-fPIC"],
},
x86_64: {
+ srcs: ["x86_64.cpp"],
cflags: ["-fPIC"],
},
arm: {
+ srcs: ["arm.cpp"],
cflags: ["-Wall"],
},
arm64: {
+ srcs: ["arm64.cpp"],
cflags: ["-Wall"],
},
},
@@ -307,8 +390,27 @@
"@bazel_tools//platforms:x86_64": [
"-fPIC",
],
- "//conditions:default": [
+ "//conditions:default": [],
+ }),
+ local_include_dirs = [
+ ".",
+ ],
+ srcs = [
+ "base.cpp",
+ ] + select({
+ "@bazel_tools//platforms:arm": [
+ "arm.cpp",
],
+ "@bazel_tools//platforms:aarch64": [
+ "arm64.cpp",
+ ],
+ "@bazel_tools//platforms:x86_32": [
+ "x86.cpp",
+ ],
+ "@bazel_tools//platforms:x86_64": [
+ "x86_64.cpp",
+ ],
+ "//conditions:default": [],
}),
)`,
},
diff --git a/bp2build/constants.go b/bp2build/constants.go
index 23bca83..70f320e 100644
--- a/bp2build/constants.go
+++ b/bp2build/constants.go
@@ -18,7 +18,7 @@
// When both a BUILD and BUILD.bazel file are exist in the same package, the BUILD.bazel file will
// be preferred for use within a Bazel build.
- // The file name used for automatically generated files. Files with this name are ignored by git.
+ // The file name used for automatically generated files.
GeneratedBuildFileName = "BUILD"
// The file name used for hand-crafted build targets.
HandcraftedBuildFileName = "BUILD.bazel"
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/bpf/bpf_test.go b/bpf/bpf_test.go
index 0bf15db..51fbc15 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -26,8 +26,7 @@
os.Exit(m.Run())
}
-var bpfFactory = android.NewFixtureFactory(
- nil,
+var prepareForBpfTest = android.GroupFixturePreparers(
cc.PrepareForTestWithCcDefaultModules,
android.FixtureMergeMockFs(
map[string][]byte{
@@ -53,7 +52,7 @@
}
`
- bpfFactory.RunTestWithBp(t, bp)
+ prepareForBpfTest.RunTestWithBp(t, bp)
// We only verify the above BP configuration is processed successfully since the data property
// value is not available for testing from this package.
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 cb845a4..f074597 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -363,7 +363,7 @@
// List of APEXes that this module has private access to for testing purpose. The module
// can depend on libraries that are not exported by the APEXes and use private symbols
// from the exported libraries.
- Test_for []string
+ Test_for []string `android:"arch_variant"`
}
type VendorProperties struct {
@@ -1846,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)
}
@@ -2637,14 +2631,31 @@
// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
// always link to non-stub variant
useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
- // Another exception: if this module is bundled with an APEX, then
- // it is linked with the non-stub variant of a module in the APEX
- // as if this is part of the APEX.
- testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
- for _, apexContents := range testFor.ApexContents {
- if apexContents.DirectlyInApex(depName) {
+ if useStubs {
+ // Another exception: if this module is a test for an APEX, then
+ // it is linked with the non-stub variant of a module in the APEX
+ // as if this is part of the APEX.
+ testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+ for _, apexContents := range testFor.ApexContents {
+ if apexContents.DirectlyInApex(depName) {
+ useStubs = false
+ break
+ }
+ }
+ }
+ if useStubs {
+ // Yet another exception: If this module and the dependency are
+ // available to the same APEXes then skip stubs between their
+ // platform variants. This complements the test_for case above,
+ // which avoids the stubs on a direct APEX library dependency, by
+ // avoiding stubs for indirect test dependencies as well.
+ //
+ // TODO(b/183882457): This doesn't work if the two libraries have
+ // only partially overlapping apex_available. For that test_for
+ // modules would need to be split into APEX variants and resolved
+ // separately for each APEX they have access to.
+ if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
useStubs = false
- break
}
}
} else {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index bb188ae..76e75da 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -27,33 +26,11 @@
"android/soong/android"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_cc_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-var ccFixtureFactory = android.NewFixtureFactory(
- &buildDir,
+var prepareForCcTest = android.GroupFixturePreparers(
PrepareForTestWithCcIncludeVndk,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.DeviceVndkVersion = StringPtr("current")
@@ -62,62 +39,62 @@
}),
)
-// testCcWithConfig runs tests using the ccFixtureFactory
+// testCcWithConfig runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
t.Helper()
- result := ccFixtureFactory.RunTestWithConfig(t, config)
+ result := prepareForCcTest.RunTestWithConfig(t, config)
return result.TestContext
}
-// testCc runs tests using the ccFixtureFactory
+// testCc runs tests using the prepareForCcTest
//
-// Do not add any new usages of this, instead use the ccFixtureFactory directly as it makes it much
+// Do not add any new usages of this, instead use the prepareForCcTest directly as it makes it much
// easier to customize the test behavior.
//
// If it is necessary to customize the behavior of an existing test that uses this then please first
-// convert the test to using ccFixtureFactory first and then in a following change add the
+// convert the test to using prepareForCcTest first and then in a following change add the
// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
// that it did not change the test behavior unexpectedly.
//
// deprecated
func testCc(t *testing.T, bp string) *android.TestContext {
t.Helper()
- result := ccFixtureFactory.RunTestWithBp(t, bp)
+ result := prepareForCcTest.RunTestWithBp(t, bp)
return result.TestContext
}
-// testCcNoVndk runs tests using the ccFixtureFactory
+// testCcNoVndk runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testCcNoVndk(t *testing.T, bp string) *android.TestContext {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
return testCcWithConfig(t, config)
}
-// testCcNoProductVndk runs tests using the ccFixtureFactory
+// testCcNoProductVndk runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testCcNoProductVndk(t *testing.T, bp string) *android.TestContext {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
return testCcWithConfig(t, config)
}
-// testCcErrorWithConfig runs tests using the ccFixtureFactory
+// testCcErrorWithConfig runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
@@ -125,33 +102,33 @@
func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
t.Helper()
- ccFixtureFactory.Extend().
+ prepareForCcTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
RunTestWithConfig(t, config)
}
-// testCcError runs tests using the ccFixtureFactory
+// testCcError runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testCcError(t *testing.T, pattern string, bp string) {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
testCcErrorWithConfig(t, pattern, config)
return
}
-// testCcErrorProductVndk runs tests using the ccFixtureFactory
+// testCcErrorProductVndk runs tests using the prepareForCcTest
//
// See testCc for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testCcErrorProductVndk(t *testing.T, pattern string, bp string) {
t.Helper()
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -166,6 +143,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()
@@ -180,7 +166,10 @@
},
}`
- result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ PrepareForTestOnFuchsia,
+ ).RunTestWithBp(t, bp)
rt := false
fb := false
@@ -216,7 +205,10 @@
},
}`
- result := ccFixtureFactory.Extend(PrepareForTestOnFuchsia).RunTestWithBp(t, bp)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ PrepareForTestOnFuchsia,
+ ).RunTestWithBp(t, bp)
ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
var objs []string
for _, o := range ld.Inputs {
@@ -292,13 +284,9 @@
func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
t.Helper()
- mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
- if !ok {
- t.Errorf("%q must have output\n", moduleName)
- return
- }
- outputFiles, err := mod.OutputFiles("")
- if err != nil || len(outputFiles) != 1 {
+ mod := ctx.ModuleForTests(moduleName, variant)
+ outputFiles := mod.OutputFiles(t, "")
+ if len(outputFiles) != 1 {
t.Errorf("%q must have single output\n", moduleName)
return
}
@@ -324,14 +312,17 @@
}
func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+ t.Helper()
checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
}
func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+ t.Helper()
checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
}
func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+ t.Helper()
checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
}
@@ -462,7 +453,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -483,7 +474,7 @@
// Check VNDK snapshot output.
snapshotDir := "vndk-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
vndkLibPath := filepath.Join(snapshotVariantPath, fmt.Sprintf("arch-%s-%s",
"arm64", "armv8-a"))
@@ -584,7 +575,7 @@
name: "llndk.libraries.txt",
insert_vndk_version: true,
}`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -634,7 +625,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -661,7 +652,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -712,7 +703,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -1337,7 +1328,7 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -1782,7 +1773,7 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -2109,7 +2100,7 @@
}
`
- ctx := ccFixtureFactory.RunTestWithBp(t, bp).TestContext
+ ctx := prepareForCcTest.RunTestWithBp(t, bp).TestContext
checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
@@ -2337,7 +2328,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
// native:vndk
@@ -2543,7 +2534,7 @@
func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
for _, moduleName := range moduleNames {
module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
- output := module.outputFile.Path()
+ output := module.outputFile.Path().RelativeToTop()
paths = append(paths, output)
}
return paths
@@ -2574,7 +2565,8 @@
variant := "android_arm64_armv8-a_static"
moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
- actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+ actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+ TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
if !reflect.DeepEqual(actual, expected) {
@@ -2608,7 +2600,8 @@
variant := "android_arm64_armv8-a_static"
moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
- actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+ actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+ TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
if !reflect.DeepEqual(actual, expected) {
@@ -3129,7 +3122,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
config.TestProductVariables.VndkUseCoreVariant = BoolPtr(true)
@@ -3447,7 +3440,8 @@
}
`
- result := ccFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
android.PrepareForTestWithVariables,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -3474,7 +3468,8 @@
}
`
- result := ccFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
android.PrepareForTestWithAllowMissingDependencies,
).RunTestWithBp(t, bp)
@@ -3526,7 +3521,7 @@
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
ctx := testCcWithConfig(t, config)
hostBin := ctx.ModuleForTests("bin", config.BuildOSTarget.String()).Description("install")
@@ -3817,7 +3812,10 @@
func TestSanitizeMemtagHeap(t *testing.T) {
variant := "android_arm64_armv8-a"
- result := ccFixtureFactory.Extend(prepareForTestWithMemtagHeap).RunTest(t)
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
+ prepareForTestWithMemtagHeap,
+ ).RunTest(t)
ctx := result.TestContext
checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
@@ -3872,7 +3870,8 @@
func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
variant := "android_arm64_armv8-a"
- result := ccFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
prepareForTestWithMemtagHeap,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.SanitizeDevice = []string{"memtag_heap"}
@@ -3932,7 +3931,8 @@
func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
variant := "android_arm64_armv8-a"
- result := ccFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForCcTest,
prepareForTestWithMemtagHeap,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.SanitizeDevice = []string{"memtag_heap"}
diff --git a/cc/compiler.go b/cc/compiler.go
index 2729e61..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -256,6 +256,10 @@
return []interface{}{&compiler.Properties, &compiler.Proto}
}
+func (compiler *baseCompiler) includeBuildDirectory() bool {
+ return proptools.BoolDefault(compiler.Properties.Include_build_directory, true)
+}
+
func (compiler *baseCompiler) compilerInit(ctx BaseModuleContext) {}
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
@@ -332,8 +336,7 @@
flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
}
- if compiler.Properties.Include_build_directory == nil ||
- *compiler.Properties.Include_build_directory {
+ if compiler.includeBuildDirectory() {
flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+modulePath)
flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+modulePath)
}
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 cb7d17d..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),
@@ -144,8 +145,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r412851"
- ClangDefaultShortVersion = "12.0.3"
+ ClangDefaultVersion = "clang-r416183"
+ ClangDefaultShortVersion = "12.0.4"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
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/gen_test.go b/cc/gen_test.go
index 41ef95c..40a5716 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -36,8 +36,10 @@
aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
- if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
- t.Errorf("missing aidl includes in global flags")
+ expected := "-I" + filepath.Dir(aidl.Output.String())
+ actual := android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)
+ if !inList(expected, actual) {
+ t.Errorf("missing aidl includes in global flags, expected %q, actual %q", expected, actual)
}
})
@@ -61,7 +63,7 @@
aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
- if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
+ if !inList("-I"+filepath.Dir(aidl.Output.String()), android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)) {
t.Errorf("missing aidl includes in global flags")
}
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index fa0c6f2..45b343b 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -52,7 +52,7 @@
},
}
`
- config := android.TestArchConfig(buildDir, nil, bp, fs)
+ config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
ctx := testGenruleContext(config)
diff --git a/cc/library.go b/cc/library.go
index 3bef629..091acfe 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"
)
@@ -203,6 +204,8 @@
func init() {
RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
}
func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -504,12 +507,11 @@
return
}
isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
- j := 0
- for i, header := range glob {
+ for _, header := range glob {
if isLibcxx {
// Glob all files under this special directory, because of C++ headers with no
// extension.
- if !strings.HasSuffix(header, "/") {
+ if strings.HasSuffix(header, "/") {
continue
}
} else {
@@ -525,12 +527,8 @@
continue
}
}
- if i != j {
- glob[j] = glob[i]
- }
- j++
+ ret = append(ret, android.PathForSource(ctx, header))
}
- glob = glob[:j]
}
// Collect generated headers
@@ -2047,3 +2045,135 @@
return outputFile
}
+
+func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelListAttribute {
+ 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 := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs))
+ return headerLibsLabels
+}
+
+func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelListAttribute, bazel.LabelListAttribute) {
+ 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 bazel.MakeLabelListAttribute(includeDirsLabels), bazel.MakeLabelListAttribute(headersLabels)
+}
+
+type bazelCcLibraryStaticAttributes struct {
+ Copts []string
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
+ Linkstatic bool
+ Includes bazel.LabelListAttribute
+ Hdrs bazel.LabelListAttribute
+}
+
+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 := bazel.MakeLabelListAttribute(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.Value)
+
+ headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
+ depsLabels.Append(headerLibsLabels.Value)
+
+ attrs := &bazelCcLibraryStaticAttributes{
+ Copts: copts,
+ Srcs: srcsLabels,
+ Deps: bazel.MakeLabelListAttribute(depsLabels),
+ Linkstatic: true,
+ Includes: bazel.MakeLabelListAttribute(includesLabels),
+ Hdrs: 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..8286848 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -62,9 +62,9 @@
}
type bazelCcLibraryHeadersAttributes struct {
- Hdrs bazel.LabelList
- Includes bazel.LabelList
- Deps bazel.LabelList
+ Hdrs bazel.LabelListAttribute
+ Includes bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
}
type bazelCcLibraryHeaders struct {
@@ -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/library_test.go b/cc/library_test.go
index 49838b4..7975275 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -199,7 +199,7 @@
},
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
ctx := testCcWithConfig(t, config)
@@ -222,7 +222,7 @@
},
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.Platform_version_active_codenames = []string{"R"}
testCcErrorWithConfig(t, `"libfoo" .*: versions: not sorted`, config)
}
diff --git a/cc/linkable.go b/cc/linkable.go
index 13b732b..6aa238b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -158,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/linker.go b/cc/linker.go
index 6d0d416..21281d2 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -599,21 +599,20 @@
_ = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
blueprint.RuleParams{
- Command: "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
- CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
+ Command: "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
+ CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
},
- "crossCompile")
+ "clangBin")
)
func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
- crossCompile := gccCmd(flags.toolchain, "")
ctx.Build(pctx, android.BuildParams{
Rule: genSortedBssSymbols,
Description: "generate bss symbol order " + symbolOrderingFile.Base(),
Output: symbolOrderingFile,
Input: in,
Args: map[string]string{
- "crossCompile": crossCompile,
+ "clangBin": "${config.ClangBin}",
},
})
return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
diff --git a/cc/object.go b/cc/object.go
index f9e6d2d..ea8d7d3 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -53,8 +53,17 @@
}
func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
- // TODO(b/181794963): restore mixed builds once cc_object incompatibility resolved
- return false
+ bazelCtx := ctx.Config().BazelContext
+ objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+ if ok {
+ if len(objPaths) != 1 {
+ ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
+ return false
+ }
+
+ handler.module.outputFile = android.OptionalPathForPath(android.PathForBazelOut(ctx, objPaths[0]))
+ }
+ return ok
}
type ObjectLinkerProperties struct {
@@ -103,9 +112,10 @@
// For bp2build conversion.
type bazelObjectAttributes struct {
- Srcs bazel.LabelList
- Deps bazel.LabelList
+ Srcs bazel.LabelListAttribute
+ Deps bazel.LabelListAttribute
Copts bazel.StringListAttribute
+ Asflags []string
Local_include_dirs []string
}
@@ -131,7 +141,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
}
@@ -147,37 +157,63 @@
// Set arch-specific configurable attributes
var copts bazel.StringListAttribute
- var srcs []string
- var excludeSrcs []string
+ var srcs bazel.LabelListAttribute
var localIncludeDirs []string
+ var asFlags []string
for _, props := range m.compiler.compilerProps() {
if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
copts.Value = baseCompilerProps.Cflags
- srcs = baseCompilerProps.Srcs
- excludeSrcs = baseCompilerProps.Exclude_srcs
+ srcs = bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleSrcExcludes(
+ ctx,
+ baseCompilerProps.Srcs,
+ baseCompilerProps.Exclude_srcs))
localIncludeDirs = baseCompilerProps.Local_include_dirs
break
}
}
- var deps bazel.LabelList
+ if c, ok := m.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
+ localIncludeDirs = append(localIncludeDirs, ".")
+ }
+
+ var deps bazel.LabelListAttribute
for _, props := range m.linker.linkerProps() {
if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
- deps = android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs)
+ deps = bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
}
}
+ productVariableProps := android.ProductVariableProperties(ctx)
+ if props, exists := productVariableProps["Asflags"]; exists {
+ // TODO(b/183595873): consider deduplicating handling of product variable properties
+ for _, prop := range props {
+ flags, ok := prop.Property.([]string)
+ if !ok {
+ ctx.ModuleErrorf("Could not convert product variable asflag property")
+ return
+ }
+ // TODO(b/183595873) handle other product variable usages -- as selects?
+ if newFlags, subbed := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable); subbed {
+ asFlags = append(asFlags, newFlags...)
+ }
+ }
+ }
+ // TODO(b/183595872) warn/error if we're not handling product variables
+
for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) {
if cProps, ok := p.(*BaseCompilerProperties); ok {
+ srcs.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcExcludes(ctx, cProps.Srcs, cProps.Exclude_srcs))
copts.SetValueForArch(arch.Name, cProps.Cflags)
}
}
- copts.SetValueForArch("default", []string{})
attrs := &bazelObjectAttributes{
- Srcs: android.BazelLabelForModuleSrcExcludes(ctx, srcs, excludeSrcs),
+ Srcs: srcs,
Deps: deps,
Copts: copts,
+ Asflags: asFlags,
Local_include_dirs: localIncludeDirs,
}
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/prebuilt_test.go b/cc/prebuilt_test.go
index 20274b2..fa6dd87 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,7 +15,6 @@
package cc
import (
- "path/filepath"
"testing"
"android/soong/android"
@@ -23,14 +22,17 @@
"github.com/google/blueprint"
)
-var prebuiltFixtureFactory = ccFixtureFactory.Extend(
+var prepareForPrebuiltTest = android.GroupFixturePreparers(
+ prepareForCcTest,
android.PrepareForTestWithAndroidMk,
)
func testPrebuilt(t *testing.T, bp string, fs android.MockFS, handlers ...android.FixturePreparer) *android.TestContext {
- result := prebuiltFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForPrebuiltTest,
fs.AddToFixture(),
- ).Extend(handlers...).RunTestWithBp(t, bp)
+ android.GroupFixturePreparers(handlers...),
+ ).RunTestWithBp(t, bp)
return result.TestContext
}
@@ -302,8 +304,7 @@
})
fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
- assertString(t, fooRule.Output.String(),
- filepath.Join(buildDir, ".intermediates/foo/linux_glibc_x86_64/foo"))
+ assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo")
assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
var libfooDep android.Path
@@ -313,8 +314,7 @@
break
}
}
- assertString(t, libfooDep.String(),
- filepath.Join(buildDir, ".intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so"))
+ assertString(t, libfooDep.String(), "out/soong/.intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so")
}
func TestPrebuiltLibrarySanitized(t *testing.T) {
diff --git a/cc/proto_test.go b/cc/proto_test.go
index f8bbd26..b9c89c7 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -61,7 +61,7 @@
t.Errorf("expected %q in %q", w, cmd)
}
- foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+ foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().RelativeToTop().String()
if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index cd09e6e..e1ac9f0 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1129,6 +1129,9 @@
Bool(c.sanitize.Properties.Sanitize.Undefined) ||
Bool(c.sanitize.Properties.Sanitize.All_undefined) {
runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+ if c.staticBinary() {
+ runtimeLibrary += ".static"
+ }
}
if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 5a3c181..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -66,6 +66,7 @@
} else {
toFile = m.outputFile.Path()
}
+ toFile = toFile.RelativeToTop()
rule := from.Description("link")
for _, dep := range rule.Implicits {
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/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 0833277..8d13ceb 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -86,7 +86,7 @@
symbol_file: "",
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -94,7 +94,7 @@
// Check Vendor snapshot output.
snapshotDir := "vendor-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var jsonFiles []string
@@ -212,7 +212,7 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
config.TestProductVariables.DirectedVendorSnapshot = true
@@ -224,7 +224,7 @@
// Check Vendor snapshot output.
snapshotDir := "vendor-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var includeJsonFiles []string
@@ -516,7 +516,7 @@
"vndk/libvndk.so": nil,
}
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := CreateTestContext(config)
@@ -628,7 +628,7 @@
},
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -707,7 +707,7 @@
"device/vendor.cpp": nil,
}
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := CreateTestContext(config)
@@ -730,7 +730,7 @@
// Verify the content of the vendor snapshot.
snapshotDir := "vendor-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var includeJsonFiles []string
@@ -799,7 +799,7 @@
"device/vendor.cpp": nil,
}
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := CreateTestContext(config)
@@ -873,7 +873,7 @@
recovery_available: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -881,7 +881,7 @@
// Check Recovery snapshot output.
snapshotDir := "recovery-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
var jsonFiles []string
@@ -991,7 +991,7 @@
"device/recovery.cpp": nil,
}
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+ config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS)
config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := CreateTestContext(config)
@@ -1014,7 +1014,7 @@
// Verify the content of the recovery snapshot.
snapshotDir := "recovery-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
var includeJsonFiles []string
@@ -1091,7 +1091,7 @@
nocrt: true,
}
`
- config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
@@ -1104,7 +1104,7 @@
// Check recovery snapshot output.
snapshotDir := "recovery-snapshot"
- snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
var includeJsonFiles []string
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/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index d341b8c..d9116b0 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -144,6 +144,7 @@
var hostAndDeviceModuleNames = HostAndDeviceModuleNames{}
var sdkVersion string
+var defaultMinSdkVersion string
var useVersion string
var staticDeps bool
var jetifier bool
@@ -286,6 +287,10 @@
return sdkVersion
}
+func (p Pom) DefaultMinSdkVersion() string {
+ return defaultMinSdkVersion
+}
+
func (p Pom) Jetifier() bool {
return jetifier
}
@@ -457,7 +462,7 @@
min_sdk_version: "{{.MinSdkVersion}}",
manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
{{- else if not .IsHostOnly}}
- min_sdk_version: "24",
+ min_sdk_version: "{{.DefaultMinSdkVersion}}",
{{- end}}
{{- end}}
static_libs: [
@@ -598,6 +603,8 @@
This may be specified multiple times to declare these dependencies.
-sdk-version <version>
Sets sdk_version: "<version>" for all modules.
+ -default-min-sdk-version
+ The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest.xml
-use-version <version>
If the maven directory contains multiple versions of artifacts and their pom files,
-use-version can be used to only write Android.bp files for a specific version of those artifacts.
@@ -622,6 +629,7 @@
flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.")
flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version")
+ flag.StringVar(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24")
flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
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..7bd0868 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) {
@@ -382,6 +387,14 @@
}
defer in.Close()
+ // Remove the target before copying. In most cases the file won't exist, but if there are
+ // duplicate copy rules for a file and the source file was read-only the second copy could
+ // fail.
+ err = os.Remove(to)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+
out, err := os.Create(to)
if err != nil {
return err
@@ -409,6 +422,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 11d3620..e2fc78c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -17,11 +17,14 @@
import (
"flag"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strings"
+ "time"
"android/soong/shared"
+
"github.com/google/blueprint/bootstrap"
"android/soong/android"
@@ -95,11 +98,15 @@
android.InitSandbox(topDir)
android.InitEnvironment(shared.JoinPath(topDir, outDir, "soong.environment.available"))
+ usedVariablesFile := shared.JoinPath(outDir, "soong.environment.used")
// The top-level Blueprints file is passed as the first argument.
srcDir := filepath.Dir(flag.Arg(0))
var ctx *android.Context
configuration := newConfig(srcDir)
- extraNinjaDeps := []string{configuration.ProductVariablesFileName}
+ extraNinjaDeps := []string{
+ configuration.ProductVariablesFileName,
+ shared.JoinPath(outDir, "soong.environment.used"),
+ }
if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
configuration.SetAllowMissingDependencies()
@@ -115,15 +122,12 @@
extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
}
- if bazelConversionRequested(configuration) {
+ bazelConversionRequested := bazelConversionRequested(configuration)
+ if bazelConversionRequested {
// Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
// before everything else.
- runBp2Build(srcDir, configuration)
- // Short-circuit and return.
- return
- }
-
- if configuration.BazelContext.BazelEnabled() {
+ runBp2Build(srcDir, configuration, extraNinjaDeps)
+ } else if configuration.BazelContext.BazelEnabled() {
// Bazel-enabled mode. Soong runs in two passes.
// First pass: Analyze the build tree, but only store all bazel commands
// needed to correctly evaluate the tree in the second pass.
@@ -151,7 +155,7 @@
}
// Convert the Soong module graph into Bazel BUILD files.
- if bazelQueryViewDir != "" {
+ if !bazelConversionRequested && bazelQueryViewDir != "" {
// Run the code-generation phase to convert BazelTargetModules to BUILD files.
codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
absoluteQueryViewDir := shared.JoinPath(topDir, bazelQueryViewDir)
@@ -161,7 +165,7 @@
}
}
- if docFile != "" {
+ if !bazelConversionRequested && docFile != "" {
if err := writeDocs(ctx, configuration, docFile); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
@@ -170,7 +174,7 @@
// TODO(ccross): make this a command line argument. Requires plumbing through blueprint
// to affect the command line of the primary builder.
- if shouldPrepareBuildActions(configuration) {
+ if !bazelConversionRequested && shouldPrepareBuildActions(configuration) {
metricsFile := filepath.Join(bootstrap.CmdlineBuildDir(), "soong_build_metrics.pb")
err := android.WriteMetrics(configuration, metricsFile)
if err != nil {
@@ -178,12 +182,43 @@
os.Exit(1)
}
}
+
+ if docFile == "" {
+ // Let's not overwrite the used variables file when generating
+ // documentation
+ writeUsedVariablesFile(shared.JoinPath(topDir, usedVariablesFile), configuration)
+ }
+}
+
+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\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\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)
+ }
}
// Run Soong in the bp2build mode. This creates a standalone context that registers
// an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files.
-func runBp2Build(srcDir string, configuration android.Config) {
+func runBp2Build(srcDir string, configuration android.Config, extraNinjaDeps []string) {
// Register an alternate set of singletons and mutators for bazel
// conversion for Bazel conversion.
bp2buildCtx := android.NewContext(configuration)
@@ -198,11 +233,13 @@
// configurations or variables, since those will generate different BUILD
// files based on how the user has configured their tree.
bp2buildCtx.SetModuleListFile(bootstrap.CmdlineModuleListFile())
- extraNinjaDeps, err := bp2buildCtx.ListModulePaths(srcDir)
+ modulePaths, err := bp2buildCtx.ListModulePaths(srcDir)
if err != nil {
panic(err)
}
+ extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
+
// Run the loading and analysis pipeline to prepare the graph of regular
// Modules parsed from Android.bp files, and the BazelTargetModules mapped
// from the regular Modules.
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/dexpreopt.go b/dexpreopt/dexpreopt.go
index 4999bc7..81a63b0 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -259,35 +259,53 @@
Implicits(clcHost).
Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
- } else if module.EnforceUsesLibraries {
- // Generate command that saves target SDK version in a shell variable.
- manifestOrApk := module.ManifestPath
- if manifestOrApk == nil {
- // No manifest to extract targetSdkVersion from, hope that dexjar is an APK.
+ } else {
+ // There are three categories of Java modules handled here:
+ //
+ // - Modules that have passed verify_uses_libraries check. They are AOT-compiled and
+ // expected to be loaded on device without CLC mismatch errors.
+ //
+ // - Modules that have failed the check in relaxed mode, so it didn't cause a build error.
+ // They are dexpreopted with "verify" filter and not AOT-compiled.
+ // TODO(b/132357300): ensure that CLC mismatch errors are ignored with "verify" filter.
+ //
+ // - Modules that didn't run the check. They are AOT-compiled, but it's unknown if they
+ // will have CLC mismatch errors on device (the check is disabled by default).
+ //
+ // TODO(b/132357300): enable the check by default and eliminate the last category, so that
+ // no time/space is wasted on AOT-compiling modules that will fail CLC check on device.
+
+ var manifestOrApk android.Path
+ if module.ManifestPath != nil {
+ // Ok, there is an XML manifest.
+ manifestOrApk = module.ManifestPath
+ } else if filepath.Ext(base) == ".apk" {
+ // Ok, there is is an APK with the manifest inside.
manifestOrApk = module.DexPath
}
- rule.Command().Text(`target_sdk_version="$(`).
- Tool(globalSoong.ManifestCheck).
- Flag("--extract-target-sdk-version").
- Input(manifestOrApk).
- FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt")).
- Text(`)"`)
+
+ // Generate command that saves target SDK version in a shell variable.
+ if manifestOrApk == nil {
+ // There is neither an XML manifest nor APK => nowhere to extract targetSdkVersion from.
+ // Set the latest ("any") version: then construct_context will not add any compatibility
+ // libraries (if this is incorrect, there will be a CLC mismatch and dexopt on device).
+ rule.Command().Textf(`target_sdk_version=%d`, AnySdkVersion)
+ } else {
+ rule.Command().Text(`target_sdk_version="$(`).
+ Tool(globalSoong.ManifestCheck).
+ Flag("--extract-target-sdk-version").
+ Input(manifestOrApk).
+ FlagWithInput("--aapt ", globalSoong.Aapt).
+ Text(`)"`)
+ }
// Generate command that saves host and target class loader context in shell variables.
clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
rule.Command().
- Text("if ! test -s ").Input(module.EnforceUsesLibrariesStatusFile).
- Text(` ; then eval "$(`).Tool(globalSoong.ConstructContext).
+ Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`).
Text(clc).Implicits(paths).
- Text(`)" ; fi`)
-
- } else {
- // Other libraries or APKs for which the exact <uses-library> list is unknown.
- // We assume the class loader context is empty.
- rule.Command().
- Text(`class_loader_context_arg=--class-loader-context=PCL[]`).
- Text(`stored_class_loader_context_arg=""`)
+ Text(`)"`)
}
// Devices that do not have a product partition use a symlink from /product to /system/product.
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 8e90295..c0ba5ca 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -15,39 +15,78 @@
package dexpreopt
import (
+ "fmt"
+
"android/soong/android"
)
-type dummyToolBinary struct {
+type fakeToolBinary struct {
android.ModuleBase
}
-func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+func (m *fakeToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-func (m *dummyToolBinary) HostToolPath() android.OptionalPath {
+func (m *fakeToolBinary) HostToolPath() android.OptionalPath {
return android.OptionalPathForPath(android.PathForTesting("dex2oat"))
}
-func dummyToolBinaryFactory() android.Module {
- module := &dummyToolBinary{}
+func fakeToolBinaryFactory() android.Module {
+ module := &fakeToolBinary{}
android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
return module
}
func RegisterToolModulesForTest(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
+ ctx.RegisterModuleType("fake_tool_binary", fakeToolBinaryFactory)
}
func BpToolModulesForTest() string {
return `
- dummy_tool_binary {
+ fake_tool_binary {
name: "dex2oatd",
}
`
}
-// Prepares a test fixture by enabling dexpreopt.
-var PrepareForTestWithDexpreopt = FixtureModifyGlobalConfig(func(*GlobalConfig) {})
+func CompatLibDefinitionsForTest() string {
+ bp := ""
+
+ // For class loader context and <uses-library> tests.
+ dexpreoptModules := []string{"android.test.runner"}
+ dexpreoptModules = append(dexpreoptModules, CompatUsesLibs...)
+ dexpreoptModules = append(dexpreoptModules, OptionalCompatUsesLibs...)
+
+ for _, extra := range dexpreoptModules {
+ bp += fmt.Sprintf(`
+ java_library {
+ name: "%s",
+ srcs: ["a.java"],
+ sdk_version: "none",
+ system_modules: "stable-core-platform-api-stubs-system-modules",
+ compile_dex: true,
+ installable: true,
+ }
+ `, extra)
+ }
+
+ return bp
+}
+
+var PrepareForTestWithDexpreoptCompatLibs = android.GroupFixturePreparers(
+ android.FixtureAddFile("defaults/dexpreopt/compat/a.java", nil),
+ android.FixtureAddTextFile("defaults/dexpreopt/compat/Android.bp", CompatLibDefinitionsForTest()),
+)
+
+var PrepareForTestWithFakeDex2oatd = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(RegisterToolModulesForTest),
+ android.FixtureAddTextFile("defaults/dexpreopt/Android.bp", BpToolModulesForTest()),
+)
+
+// Prepares a test fixture by enabling dexpreopt, registering the fake_tool_binary module type and
+// using that to define the `dex2oatd` module.
+var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
+ FixtureModifyGlobalConfig(func(*GlobalConfig) {}),
+)
// FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
// configuration.
@@ -78,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/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index f800c48..9c3db3b 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -26,8 +26,7 @@
os.Exit(m.Run())
}
-var prebuiltEtcFixtureFactory = android.NewFixtureFactory(
- nil,
+var prepareForPrebuiltEtcTest = android.GroupFixturePreparers(
android.PrepareForTestWithArchMutator,
PrepareForTestWithPrebuiltEtc,
android.FixtureMergeMockFs(android.MockFS{
@@ -38,7 +37,7 @@
)
func TestPrebuiltEtcVariants(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -72,7 +71,7 @@
}
func TestPrebuiltEtcOutputPath(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -85,7 +84,7 @@
}
func TestPrebuiltEtcGlob(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "my_foo",
src: "foo.*",
@@ -105,7 +104,7 @@
}
func TestPrebuiltEtcAndroidMk(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo",
src: "foo.conf",
@@ -139,7 +138,7 @@
}
func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc {
name: "foo.conf",
src: "foo.conf",
@@ -153,7 +152,7 @@
}
func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) {
- prebuiltEtcFixtureFactory.
+ prepareForPrebuiltEtcTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("relative_install_path is set. Cannot set sub_dir")).
RunTestWithBp(t, `
prebuilt_etc {
@@ -166,7 +165,7 @@
}
func TestPrebuiltEtcHost(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_etc_host {
name: "foo.conf",
src: "foo.conf",
@@ -181,7 +180,7 @@
}
func TestPrebuiltUserShareInstallDirPath(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_usr_share {
name: "foo.conf",
src: "foo.conf",
@@ -195,7 +194,7 @@
}
func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_usr_share_host {
name: "foo.conf",
src: "foo.conf",
@@ -210,7 +209,7 @@
}
func TestPrebuiltFontInstallDirPath(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, `
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
prebuilt_font {
name: "foo.conf",
src: "foo.conf",
@@ -249,7 +248,7 @@
}}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
})
@@ -283,7 +282,7 @@
}}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
- result := prebuiltEtcFixtureFactory.RunTestWithBp(t, tt.config)
+ result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config)
p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath)
})
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 8974eba..b2bd6bd 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -91,7 +91,7 @@
var dependencyTag = struct {
blueprint.BaseDependencyTag
- android.InstallAlwaysNeededDependencyTag
+ android.PackagingItemAlwaysDepTag
}{}
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 20d9622..dbbc1d8 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -43,6 +43,10 @@
// Total size of the logical partition
Size *string
+ // List of partitions for default group. Default group has no size limit and automatically
+ // minimized when creating an image.
+ Default_group []partitionProperties
+
// List of groups. A group defines a fixed sized region. It can host one or more logical
// partitions and their total size is limited by the size of the group they are in.
Groups []groupProperties
@@ -52,7 +56,7 @@
}
type groupProperties struct {
- // Name of the partition group
+ // Name of the partition group. Can't be "default"; use default_group instead.
Name *string
// Size of the partition group
@@ -92,8 +96,9 @@
// Sparse the filesystem images and calculate their sizes
sparseImages := make(map[string]android.OutputPath)
sparseImageSizes := make(map[string]android.OutputPath)
- for _, group := range l.properties.Groups {
- for _, part := range group.Partitions {
+
+ sparsePartitions := func(partitions []partitionProperties) {
+ for _, part := range partitions {
sparseImg, sizeTxt := sparseFilesystem(ctx, part, builder)
pName := proptools.String(part.Name)
sparseImages[pName] = sparseImg
@@ -101,6 +106,12 @@
}
}
+ for _, group := range l.properties.Groups {
+ sparsePartitions(group.Partitions)
+ }
+
+ sparsePartitions(l.properties.Default_group)
+
cmd := builder.Command().BuiltTool("lpmake")
size := proptools.String(l.properties.Size)
@@ -123,10 +134,32 @@
groupNames := make(map[string]bool)
partitionNames := make(map[string]bool)
+ addPartitionsToGroup := func(partitions []partitionProperties, gName string) {
+ for _, part := range partitions {
+ pName := proptools.String(part.Name)
+ if pName == "" {
+ ctx.PropertyErrorf("groups.partitions.name", "must be set")
+ }
+ if _, ok := partitionNames[pName]; ok {
+ ctx.PropertyErrorf("groups.partitions.name", "already exists")
+ } else {
+ partitionNames[pName] = true
+ }
+ // Get size of the partition by reading the -size.txt file
+ pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
+ cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
+ cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
+ }
+ }
+
+ addPartitionsToGroup(l.properties.Default_group, "default")
+
for _, group := range l.properties.Groups {
gName := proptools.String(group.Name)
if gName == "" {
ctx.PropertyErrorf("groups.name", "must be set")
+ } else if gName == "default" {
+ ctx.PropertyErrorf("groups.name", `can't use "default" as a group name. Use default_group instead`)
}
if _, ok := groupNames[gName]; ok {
ctx.PropertyErrorf("group.name", "already exists")
@@ -142,21 +175,7 @@
}
cmd.FlagWithArg("--group=", gName+":"+gSize)
- for _, part := range group.Partitions {
- pName := proptools.String(part.Name)
- if pName == "" {
- ctx.PropertyErrorf("groups.partitions.name", "must be set")
- }
- if _, ok := partitionNames[pName]; ok {
- ctx.PropertyErrorf("groups.partitions.name", "already exists")
- } else {
- partitionNames[pName] = true
- }
- // Get size of the partition by reading the -size.txt file
- pSize := fmt.Sprintf("$(cat %s)", sparseImageSizes[pName])
- cmd.FlagWithArg("--partition=", fmt.Sprintf("%s:readonly:%s:%s", pName, pSize, gName))
- cmd.FlagWithInput("--image="+pName+"=", sparseImages[pName])
- }
+ addPartitionsToGroup(group.Partitions, gName)
}
l.output = android.PathForModuleOut(ctx, l.installFileName()).OutputPath
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..5d438ea 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)
}
@@ -800,9 +798,9 @@
}
type bazelGenruleAttributes struct {
- Srcs bazel.LabelList
+ Srcs bazel.LabelListAttribute
Outs []string
- Tools bazel.LabelList
+ Tools bazel.LabelListAttribute
Cmd string
}
@@ -820,7 +818,7 @@
func GenruleBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
@@ -830,15 +828,16 @@
}
// Bazel only has the "tools" attribute.
- tools := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
- tool_files := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
- tools.Append(tool_files)
+ tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+ tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+ tools_prop.Append(tool_files_prop)
- srcs := android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)
+ tools := bazel.MakeLabelListAttribute(tools_prop)
+ srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs))
var allReplacements bazel.LabelList
- allReplacements.Append(tools)
- allReplacements.Append(srcs)
+ allReplacements.Append(tools.Value)
+ allReplacements.Append(srcs.Value)
// Replace in and out variables with $< and $@
var cmd string
@@ -846,9 +845,9 @@
cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
- if len(tools.Includes) > 0 {
- cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Includes[0].Label), -1)
- cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Includes[0].Label), -1)
+ if len(tools.Value.Includes) > 0 {
+ cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
+ cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
}
for _, l := range allReplacements.Includes {
bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 199a7df..2ee456d 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -28,8 +28,7 @@
os.Exit(m.Run())
}
-var genruleFixtureFactory = android.NewFixtureFactory(
- nil,
+var prepareForGenRuleTest = android.GroupFixturePreparers(
android.PrepareForTestWithArchMutator,
android.PrepareForTestWithDefaults,
@@ -37,6 +36,7 @@
PrepareForTestWithGenRuleBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
ctx.RegisterModuleType("tool", toolFactory)
+ ctx.RegisterModuleType("output", outputProducerFactory)
}),
android.FixtureMergeMockFs(android.MockFS{
"tool": nil,
@@ -447,7 +447,8 @@
expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
}
- result := genruleFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
}),
@@ -523,7 +524,7 @@
},
}
- result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
+ result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
@@ -605,7 +606,7 @@
expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
}
- result := genruleFixtureFactory.
+ result := prepareForGenRuleTest.
ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
RunTestWithBp(t, testGenruleBp()+bp)
@@ -642,7 +643,7 @@
}
`
- result := genruleFixtureFactory.RunTestWithBp(t, testGenruleBp()+bp)
+ result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
gen := result.Module("gen", "").(*Module)
@@ -653,6 +654,35 @@
android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
}
+func TestGenruleAllowMissingDependencies(t *testing.T) {
+ bp := `
+ output {
+ name: "disabled",
+ enabled: false,
+ }
+
+ genrule {
+ name: "gen",
+ srcs: [
+ ":disabled",
+ ],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ }
+ `
+ result := prepareForGenRuleTest.Extend(
+ android.FixtureModifyConfigAndContext(
+ func(config android.Config, ctx *android.TestContext) {
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+ ctx.SetAllowMissingDependencies(true)
+ })).RunTestWithBp(t, bp)
+
+ gen := result.ModuleForTests("gen", "").Output("out")
+ if gen.Rule != android.ErrorRule {
+ t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
+ }
+}
+
func TestGenruleWithBazel(t *testing.T) {
bp := `
genrule {
@@ -662,11 +692,12 @@
}
`
- result := genruleFixtureFactory.Extend(android.FixtureModifyConfig(func(config android.Config) {
- config.BazelContext = android.MockBazelContext{
- AllFiles: map[string][]string{
- "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
- })).RunTestWithBp(t, testGenruleBp()+bp)
+ result := android.GroupFixturePreparers(
+ prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ AllFiles: map[string][]string{
+ "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
+ })).RunTestWithBp(t, testGenruleBp()+bp)
gen := result.Module("foo", "").(*Module)
@@ -696,3 +727,24 @@
}
var _ android.HostToolProvider = (*testTool)(nil)
+
+type testOutputProducer struct {
+ android.ModuleBase
+ outputFile android.Path
+}
+
+func outputProducerFactory() android.Module {
+ module := &testOutputProducer{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
+ return android.Paths{t.outputFile}, nil
+}
+
+var _ android.OutputFileProducer = (*testOutputProducer)(nil)
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/aar.go b/java/aar.go
index 554ea67..67b9ef0 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -40,15 +40,14 @@
func init() {
RegisterAARBuildComponents(android.InitRegistrationContext)
-
- android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
- })
}
func RegisterAARBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("android_library_import", AARImportFactory)
ctx.RegisterModuleType("android_library", AndroidLibraryFactory)
+ ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
+ })
}
//
diff --git a/java/android_resources.go b/java/android_resources.go
index 4d420cf..6864ebb 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -22,8 +22,11 @@
)
func init() {
- android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+ registerOverlayBuildComponents(android.InitRegistrationContext)
+}
+func registerOverlayBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
}
var androidResourceIgnoreFilenames = []string{
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 3477956..5eaa77b 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -135,7 +135,8 @@
}
func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo-shared_library", "foo-no_shared_library"),
).RunTestWithBp(t, `
@@ -171,7 +172,7 @@
}
func TestImportSoongDexJar(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
java_import {
name: "my-java-import",
jars: ["a.jar"],
@@ -180,14 +181,10 @@
}
`)
- mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
- expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+ mod := result.Module("my-java-import", "android_common")
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
+ expectedSoongDexJar := "out/soong/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
- if len(actualSoongDexJar) != 1 {
- t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
- } else if actualSoongDexJar[0] != expectedSoongDexJar {
- t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_SOONG_DEX_JAR", result.Config, []string{expectedSoongDexJar}, actualSoongDexJar)
}
diff --git a/java/app.go b/java/app.go
index 1b6e0e3..b849b98 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1294,7 +1294,8 @@
// check is not necessary, and although it is good to have, it is difficult to maintain on
// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
// various unrelated reasons, such as a failure to get manifest from an APK).
- if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
+ global := dexpreopt.GetGlobalConfig(ctx)
+ if global.DisablePreopt || global.OnlyPreoptBootImageAndSystemServer {
return inputFile
}
diff --git a/java/app_import_test.go b/java/app_import_test.go
index cae41d0..147ae45 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -252,14 +252,15 @@
jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AAPTPreferredConfig = test.aaptPreferredConfig
+ variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ }),
+ ).RunTestWithBp(t, bp)
- run(t, ctx, config)
-
- variant := ctx.ModuleForTests("foo", "android_common")
+ variant := result.ModuleForTests("foo", "android_common")
jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
if len(matches) != 2 {
@@ -456,12 +457,9 @@
t.Errorf("prebuilt framework-res is not preprocessed")
}
- expectedInstallPath := buildDir + "/target/product/test_device/system/framework/framework-res.apk"
+ expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
- if a.dexpreopter.installPath.String() != expectedInstallPath {
- t.Errorf("prebuilt framework-res installed to incorrect location, actual: %s, expected: %s", a.dexpreopter.installPath, expectedInstallPath)
-
- }
+ android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
diff --git a/java/app_set_test.go b/java/app_set_test.go
index ab55758..adaf71b 100644
--- a/java/app_set_test.go
+++ b/java/app_set_test.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"reflect"
"testing"
@@ -96,20 +97,24 @@
}
for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
- config.Targets[android.Android] = test.targets
- ctx := testContext(config)
- run(t, ctx, config)
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ variables.Platform_sdk_version = &test.sdkVersion
+ }),
+ android.FixtureModifyConfig(func(config android.Config) {
+ config.Targets[android.Android] = test.targets
+ }),
+ ).RunTestWithBp(t, bp)
+
module := ctx.ModuleForTests("foo", "android_common")
const packedSplitApks = "foo.zip"
params := module.Output(packedSplitApks)
for k, v := range test.expected {
- if actual := params.Args[k]; actual != v {
- t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
- test.name, k, actual, v)
- }
+ t.Run(test.name, func(t *testing.T) {
+ android.AssertStringEquals(t, fmt.Sprintf("arg value for `%s`", k), v, params.Args[k])
+ })
}
}
}
diff --git a/java/app_test.go b/java/app_test.go
index 7168a96..a99ac62 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -26,25 +26,18 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/dexpreopt"
+ "android/soong/genrule"
)
-// testAppConfig is a legacy way of creating a test Config for testing java app modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testAppConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- return testConfig(env, bp, fs)
-}
-
-// testApp runs tests using the javaFixtureFactory
+// testApp runs tests using the prepareForJavaTest
//
// See testJava for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testApp(t *testing.T, bp string) *android.TestContext {
t.Helper()
- result := javaFixtureFactory.RunTestWithBp(t, bp)
+ result := prepareForJavaTest.RunTestWithBp(t, bp)
return result.TestContext
}
@@ -63,7 +56,8 @@
for _, moduleType := range []string{"android_app", "android_library"} {
t.Run(moduleType, func(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
android.FixtureModifyMockFS(func(fs android.MockFS) {
for _, file := range resourceFiles {
fs[file] = nil
@@ -118,9 +112,9 @@
foo := ctx.ModuleForTests("foo", "android_common")
expectedOutputs := []string{
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo.apk"),
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v4.apk"),
- filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v7_hdpi.apk"),
+ "out/soong/.intermediates/foo/android_common/foo.apk",
+ "out/soong/.intermediates/foo/android_common/foo_v4.apk",
+ "out/soong/.intermediates/foo/android_common/foo_v7_hdpi.apk",
}
for _, expectedOutput := range expectedOutputs {
foo.Output(expectedOutput)
@@ -130,9 +124,7 @@
if err != nil {
t.Fatal(err)
}
- if g, w := outputFiles.Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
- t.Errorf(`want OutputFiles("") = %q, got %q`, w, g)
- }
+ android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
}
func TestPlatformAPIs(t *testing.T) {
@@ -374,8 +366,8 @@
if test.expectedError != "" {
errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
}
- javaFixtureFactory.
- Extend(FixtureWithPrebuiltApis(map[string][]string{
+ android.GroupFixturePreparers(
+ prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{
"29": {"foo"},
})).
ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp)
@@ -446,7 +438,7 @@
"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o": nil,
}
- ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+ ctx, _ := testJavaWithFS(t, bp, fs)
inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
var crtbeginFound, crtendFound bool
@@ -547,7 +539,7 @@
},
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"res/res/values/strings.xml": nil,
}
@@ -561,11 +553,13 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testConfig(nil, fmt.Sprintf(bp, testCase.prop), fs)
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))
- module := ctx.ModuleForTests("foo", "android_common")
+ module := result.ModuleForTests("foo", "android_common")
resourceList := module.MaybeOutput("aapt2/res.list")
var resources []string
@@ -575,10 +569,7 @@
}
}
- if !reflect.DeepEqual(resources, testCase.resources) {
- t.Errorf("expected resource files %q, got %q",
- testCase.resources, resources)
- }
+ android.AssertDeepEquals(t, "resource files", testCase.resources, resources)
})
}
}
@@ -624,9 +615,9 @@
name: "foo",
// lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively.
assetPackages: []string{
- buildDir + "/.intermediates/foo/android_common/aapt2/package-res.apk",
- buildDir + "/.intermediates/lib1/android_common/assets.zip",
- buildDir + "/.intermediates/lib3/android_common/assets.zip",
+ "out/soong/.intermediates/foo/android_common/aapt2/package-res.apk",
+ "out/soong/.intermediates/lib1/android_common/assets.zip",
+ "out/soong/.intermediates/lib3/android_common/assets.zip",
},
},
{
@@ -639,8 +630,8 @@
{
name: "lib3",
assetPackages: []string{
- buildDir + "/.intermediates/lib3/android_common/aapt2/package-res.apk",
- buildDir + "/.intermediates/lib4/android_common/assets.zip",
+ "out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk",
+ "out/soong/.intermediates/lib4/android_common/assets.zip",
},
},
{
@@ -661,24 +652,18 @@
} else {
aapt2link = m.Output("package-res.apk")
}
+ aapt2link = aapt2link
aapt2Flags := aapt2link.Args["flags"]
if test.assetFlag != "" {
- if !strings.Contains(aapt2Flags, test.assetFlag) {
- t.Errorf("Can't find asset flag %q in aapt2 link flags %q", test.assetFlag, aapt2Flags)
- }
+ android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
} else {
- if strings.Contains(aapt2Flags, " -A ") {
- t.Errorf("aapt2 link flags %q contain unexpected asset flag", aapt2Flags)
- }
+ android.AssertStringDoesNotContain(t, "aapt2 link flags", aapt2Flags, " -A ")
}
// Check asset merge rule.
if len(test.assetPackages) > 0 {
mergeAssets := m.Output("package-res.apk")
- if !reflect.DeepEqual(test.assetPackages, mergeAssets.Inputs.Strings()) {
- t.Errorf("Unexpected mergeAssets inputs: %v, expected: %v",
- mergeAssets.Inputs.Strings(), test.assetPackages)
- }
+ android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs)
}
})
}
@@ -750,9 +735,9 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
"device/vendor/blah/overlay/foo/res/values/strings.xml",
@@ -763,7 +748,7 @@
"device/vendor/blah/overlay/bar/res/values/strings.xml",
},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
"device/vendor/blah/overlay/lib/res/values/strings.xml",
},
@@ -785,9 +770,9 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
},
@@ -796,7 +781,7 @@
"device/vendor/blah/overlay/bar/res/values/strings.xml",
},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
},
},
@@ -827,15 +812,15 @@
},
overlayFiles: map[string][]string{
"foo": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
- buildDir + "/.intermediates/lib/android_common/package-res.apk",
- buildDir + "/.intermediates/lib3/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib/android_common/package-res.apk",
+ "out/soong/.intermediates/lib3/android_common/package-res.apk",
"foo/res/res/values/strings.xml",
"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
},
"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
"lib": {
- buildDir + "/.intermediates/lib2/android_common/package-res.apk",
+ "out/soong/.intermediates/lib2/android_common/package-res.apk",
"lib/res/res/values/strings.xml",
},
},
@@ -862,7 +847,7 @@
"product/vendor/blah/overlay",
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"foo/res/res/values/strings.xml": nil,
"bar/res/res/values/strings.xml": nil,
"lib/res/res/values/strings.xml": nil,
@@ -913,18 +898,21 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
- if testCase.enforceRROExcludedOverlays != nil {
- config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.DeviceResourceOverlays = deviceResourceOverlays
+ variables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ variables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ if testCase.enforceRROExcludedOverlays != nil {
+ variables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
+ }
+ }),
+ ).RunTestWithBp(t, bp)
resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
for _, o := range list {
@@ -942,14 +930,14 @@
}
getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
- module := ctx.ModuleForTests(moduleName, "android_common")
+ module := result.ModuleForTests(moduleName, "android_common")
resourceList := module.MaybeOutput("aapt2/res.list")
if resourceList.Rule != nil {
- resourceFiles = resourceListToFiles(module, resourceList.Inputs.Strings())
+ resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
}
overlayList := module.MaybeOutput("aapt2/overlay.list")
if overlayList.Rule != nil {
- overlayFiles = resourceListToFiles(module, overlayList.Inputs.Strings())
+ overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs))
}
for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
@@ -961,7 +949,7 @@
} else {
t.Fatalf("Unexpected overlayType %d", d.overlayType)
}
- rroDirs = append(rroDirs, prefix+d.path.String())
+ rroDirs = append(rroDirs, prefix+android.PathRelativeToTop(d.path))
}
return resourceFiles, overlayFiles, rroDirs
@@ -1077,7 +1065,8 @@
%s
}`, moduleType, test.sdkVersion, platformApiProp)
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.Platform_sdk_version = &test.platformSdkInt
variables.Platform_sdk_codename = &test.platformSdkCodename
@@ -1145,7 +1134,8 @@
vendor: true,
}`, moduleType, sdkKind, test.sdkVersion)
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.Platform_sdk_version = &test.platformSdkInt
variables.Platform_sdk_codename = &test.platformSdkCodename
@@ -1269,13 +1259,19 @@
}
`
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ errorHandler := android.FixtureExpectsNoErrors
if enforce {
- testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
- } else {
- testJavaWithConfig(t, config)
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
}
+
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ }),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
}
@@ -1609,25 +1605,23 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- if test.certificateOverride != "" {
- config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
- }
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.certificateOverride != "" {
+ variables.CertificateOverrides = []string{test.certificateOverride}
+ }
+ }),
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
signapk := foo.Output("foo.apk")
signCertificateFlags := signapk.Args["certificates"]
- if test.expectedCertificate != signCertificateFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedCertificate, signCertificateFlags)
- }
+ android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags)
signFlags := signapk.Args["flags"]
- if test.expectedLineage != signFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedLineage, signFlags)
- }
+ android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags)
})
}
}
@@ -1677,17 +1671,15 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
signapk := foo.Output("foo.apk")
signFlags := signapk.Args["flags"]
- if test.expected != signFlags {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
- }
+ android.AssertStringEquals(t, "signing flags", test.expected, signFlags)
})
}
}
@@ -1710,8 +1702,8 @@
`,
packageNameOverride: "",
expected: []string{
- buildDir + "/.intermediates/foo/android_common/foo.apk",
- buildDir + "/target/product/test_device/system/app/foo/foo.apk",
+ "out/soong/.intermediates/foo/android_common/foo.apk",
+ "out/soong/target/product/test_device/system/app/foo/foo.apk",
},
},
{
@@ -1726,27 +1718,31 @@
packageNameOverride: "foo:bar",
expected: []string{
// The package apk should be still be the original name for test dependencies.
- buildDir + "/.intermediates/foo/android_common/bar.apk",
- buildDir + "/target/product/test_device/system/app/bar/bar.apk",
+ "out/soong/.intermediates/foo/android_common/bar.apk",
+ "out/soong/target/product/test_device/system/app/bar/bar.apk",
},
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
- config := testAppConfig(nil, test.bp, nil)
- if test.packageNameOverride != "" {
- config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
- }
- ctx := testContext(config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ if test.packageNameOverride != "" {
+ variables.PackageNameOverrides = []string{test.packageNameOverride}
+ }
+ }),
+ ).RunTestWithBp(t, test.bp)
- run(t, ctx, config)
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
+
+ outSoongDir := result.Config.BuildDir()
outputs := foo.AllOutputs()
outputMap := make(map[string]bool)
for _, o := range outputs {
- outputMap[o] = true
+ outputMap[android.StringPathRelativeToTop(outSoongDir, o)] = true
}
for _, e := range test.expected {
if _, exist := outputMap[e]; !exist {
@@ -1771,13 +1767,15 @@
sdk_version: "current",
}
`
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
+ }),
+ ).RunTestWithBp(t, bp)
- bar := ctx.ModuleForTests("bar", "android_common")
+ bar := result.ModuleForTests("bar", "android_common")
res := bar.Output("package-res.apk")
aapt2Flags := res.Args["flags"]
e := "--rename-instrumentation-target-package org.dandroid.bp"
@@ -1787,7 +1785,8 @@
}
func TestOverrideAndroidApp(t *testing.T) {
- ctx, _ := testJava(t, `
+ result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+ t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -1862,7 +1861,7 @@
name: "foo",
moduleName: "foo",
variantName: "android_common",
- apkPath: "/target/product/test_device/system/app/foo/foo.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux"},
@@ -1874,7 +1873,7 @@
name: "foo",
moduleName: "bar",
variantName: "android_common_bar",
- apkPath: "/target/product/test_device/system/app/bar/bar.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk",
certFlag: "cert/new_cert.x509.pem cert/new_cert.pk8",
lineageFlag: "--lineage lineage.bin",
overrides: []string{"qux", "foo"},
@@ -1886,7 +1885,7 @@
name: "foo",
moduleName: "baz",
variantName: "android_common_baz",
- apkPath: "/target/product/test_device/system/app/baz/baz.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz/baz.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo"},
@@ -1898,7 +1897,7 @@
name: "foo",
moduleName: "baz_no_rename_resources",
variantName: "android_common_baz_no_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo"},
@@ -1910,7 +1909,7 @@
name: "foo_no_rename_resources",
moduleName: "baz_base_no_rename_resources",
variantName: "android_common_baz_base_no_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo_no_rename_resources"},
@@ -1922,7 +1921,7 @@
name: "foo_no_rename_resources",
moduleName: "baz_override_base_rename_resources",
variantName: "android_common_baz_override_base_rename_resources",
- apkPath: "/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
+ apkPath: "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
lineageFlag: "",
overrides: []string{"qux", "foo_no_rename_resources"},
@@ -1932,48 +1931,27 @@
},
}
for _, expected := range expectedVariants {
- variant := ctx.ModuleForTests(expected.name, expected.variantName)
+ variant := result.ModuleForTests(expected.name, expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output(expected.apkPath)
// Check the certificate paths
signapk := variant.Output(expected.moduleName + ".apk")
certFlag := signapk.Args["certificates"]
- if expected.certFlag != certFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.certFlag, certFlag)
- }
+ android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag)
// Check the lineage flags
lineageFlag := signapk.Args["flags"]
- if expected.lineageFlag != lineageFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.lineageFlag, lineageFlag)
- }
+ android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidApp)
- if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
- t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
- expected.overrides, mod.appProperties.Overrides)
- }
+ android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides)
// Test Overridable property: Logging_parent
logging_parent := mod.aapt.LoggingParent
- if expected.logging_parent != logging_parent {
- t.Errorf("Incorrect overrides property value for logging parent, expected: %q, got: %q",
- expected.logging_parent, logging_parent)
- }
+ android.AssertStringEquals(t, "overrides property value for logging parent", expected.logging_parent, logging_parent)
// Check the package renaming flag, if exists.
res := variant.Output("package-res.apk")
@@ -2016,14 +1994,14 @@
// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
- fooTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+ fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
if !strings.Contains(javac.Args["classpath"], fooTurbine) {
t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
}
// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
- barTurbine := filepath.Join(buildDir, ".intermediates", "foo", "android_common_bar", "turbine-combined", "foo.jar")
+ barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
if !strings.Contains(javac.Args["classpath"], barTurbine) {
t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
}
@@ -2089,18 +2067,7 @@
variant := ctx.ModuleForTests("foo_test", expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output("out/soong" + expected.apkPath)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidTest)
@@ -2111,7 +2078,7 @@
// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
javac := variant.Rule("javac")
- turbine := filepath.Join(buildDir, ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
+ turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
if !strings.Contains(javac.Args["classpath"], turbine) {
t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
}
@@ -2167,7 +2134,7 @@
moduleName: "bar_test",
variantName: "android_common",
expectedFlags: []string{
- "--manifest " + buildDir + "/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
+ "--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.bar.test",
},
},
@@ -2175,8 +2142,7 @@
moduleName: "foo_test",
variantName: "android_common_baz_test",
expectedFlags: []string{
- "--manifest " + buildDir +
- "/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
+ "--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
"--package-name com.android.baz.test",
"--test-file-name baz_test.apk",
},
@@ -2369,7 +2335,8 @@
}
`
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -2456,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
@@ -2533,7 +2560,17 @@
}
func TestEmbedNotice(t *testing.T) {
- ctx, _ := testJavaWithFS(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcDefaultModules,
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+ android.MockFS{
+ "APP_NOTICE": nil,
+ "GENRULE_NOTICE": nil,
+ "LIB_NOTICE": nil,
+ "TOOL_NOTICE": nil,
+ }.AddToFixture(),
+ ).RunTestWithBp(t, `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -2589,15 +2626,10 @@
srcs: ["b.java"],
notice: "TOOL_NOTICE",
}
- `, map[string][]byte{
- "APP_NOTICE": nil,
- "GENRULE_NOTICE": nil,
- "LIB_NOTICE": nil,
- "TOOL_NOTICE": nil,
- })
+ `)
// foo has NOTICE files to process, and embed_notices is true.
- foo := ctx.ModuleForTests("foo", "android_common")
+ foo := result.ModuleForTests("foo", "android_common")
// verify merge notices rule.
mergeNotices := foo.Rule("mergeNoticesRule")
noticeInputs := mergeNotices.Inputs.Strings()
@@ -2617,25 +2649,21 @@
// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
res := foo.Output("package-res.apk")
aapt2Flags := res.Args["flags"]
- e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
- if !strings.Contains(aapt2Flags, e) {
- t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
- }
+ e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
+ android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
// bar has NOTICE files to process, but embed_notices is not set.
- bar := ctx.ModuleForTests("bar", "android_common")
+ bar := result.ModuleForTests("bar", "android_common")
res = bar.Output("package-res.apk")
aapt2Flags = res.Args["flags"]
- e = "-A " + buildDir + "/.intermediates/bar/android_common/NOTICE"
- if strings.Contains(aapt2Flags, e) {
- t.Errorf("bar shouldn't have the asset dir flag for NOTICE: %q", e)
- }
+ e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
+ android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
// baz's embed_notice is true, but it doesn't have any NOTICE files.
- baz := ctx.ModuleForTests("baz", "android_common")
+ baz := result.ModuleForTests("baz", "android_common")
res = baz.Output("package-res.apk")
aapt2Flags = res.Args["flags"]
- e = "-A " + buildDir + "/.intermediates/baz/android_common/NOTICE"
+ e = "-A out/soong/.intermediates/baz/android_common/NOTICE"
if strings.Contains(aapt2Flags, e) {
t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e)
}
@@ -2718,7 +2746,8 @@
test := func(t *testing.T, bp string, want bool, unbundled bool) {
t.Helper()
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithPrebuiltsOfCurrentApi,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
if unbundled {
diff --git a/java/boot_image.go b/java/boot_image.go
index 25a4f17..0c47976 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -20,6 +20,7 @@
"android/soong/android"
"android/soong/dexpreopt"
+ "github.com/google/blueprint/proptools"
"github.com/google/blueprint"
)
@@ -27,24 +28,62 @@
func init() {
RegisterBootImageBuildComponents(android.InitRegistrationContext)
+ // TODO(b/177892522): Remove after has been replaced by bootclasspath_fragments
android.RegisterSdkMemberType(&bootImageMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "boot_images",
SupportsSdk: true,
},
})
+
+ android.RegisterSdkMemberType(&bootImageMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "bootclasspath_fragments",
+ SupportsSdk: true,
+ },
+ })
}
func RegisterBootImageBuildComponents(ctx android.RegistrationContext) {
+ // TODO(b/177892522): Remove after has been replaced by bootclasspath_fragment
ctx.RegisterModuleType("boot_image", bootImageFactory)
ctx.RegisterModuleType("prebuilt_boot_image", prebuiltBootImageFactory)
+
+ ctx.RegisterModuleType("bootclasspath_fragment", bootImageFactory)
+ ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootImageFactory)
+}
+
+type bootImageContentDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+// Avoid having to make boot image content visible to the boot image.
+//
+// This is a temporary workaround to make it easier to migrate to boot image modules with proper
+// dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (b bootImageContentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The tag used for the dependency between the boot image module and its contents.
+var bootImageContentDepTag = bootImageContentDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = bootImageContentDepTag
+
+func IsbootImageContentDepTag(tag blueprint.DependencyTag) bool {
+ return tag == bootImageContentDepTag
}
type bootImageProperties struct {
// The name of the image this represents.
//
- // Must be one of "art" or "boot".
- Image_name string
+ // If specified then it must be one of "art" or "boot".
+ Image_name *string
+
+ // The contents of this boot image, could be either java_library, java_sdk_library, or boot_image.
+ //
+ // The order of this list matters as it is the order that is used in the bootclasspath.
+ Contents []string
}
type BootImageModule struct {
@@ -60,9 +99,59 @@
android.InitApexModule(m)
android.InitSdkAwareModule(m)
android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+ // Perform some consistency checking to ensure that the configuration is correct.
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ bootImageConsistencyCheck(ctx, m)
+ })
return m
}
+func bootImageConsistencyCheck(ctx android.EarlyModuleContext, m *BootImageModule) {
+ contents := m.properties.Contents
+ if m.properties.Image_name == nil && len(contents) == 0 {
+ ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+ }
+ if m.properties.Image_name != nil && len(contents) != 0 {
+ ctx.ModuleErrorf(`both of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+ }
+ imageName := proptools.String(m.properties.Image_name)
+ if imageName == "art" {
+ // Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
+ // too early in the Soong processing for that to work.
+ global := dexpreopt.GetGlobalConfig(ctx)
+ modules := global.ArtApexJars
+
+ // Make sure that the apex specified in the configuration is consistent and is one for which
+ // this boot image is available.
+ jars := []string{}
+ commonApex := ""
+ for i := 0; i < modules.Len(); i++ {
+ apex := modules.Apex(i)
+ jar := modules.Jar(i)
+ if apex == "platform" {
+ ctx.ModuleErrorf("ArtApexJars is invalid as it requests a platform variant of %q", jar)
+ continue
+ }
+ if !m.AvailableFor(apex) {
+ ctx.ModuleErrorf("incompatible with ArtApexJars which expects this to be in apex %q but this is only in apexes %q",
+ apex, m.ApexAvailable())
+ continue
+ }
+ if commonApex == "" {
+ commonApex = apex
+ } else if commonApex != apex {
+ ctx.ModuleErrorf("ArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex %q and %q",
+ commonApex, apex)
+ }
+ jars = append(jars, jar)
+ }
+
+ // Store the jars in the Contents property so that they can be used to add dependencies.
+ m.properties.Contents = jars
+ }
+}
+
var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{})
type BootImageInfo struct {
@@ -96,9 +185,9 @@
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 tag == bootImageContentDepTag {
+ // Boot image contents are automatically added to apex.
+ return true
}
if android.IsMetaDependencyTag(tag) {
// Cross-cutting metadata dependencies are metadata.
@@ -112,6 +201,8 @@
}
func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), bootImageContentDepTag, b.properties.Contents...)
+
if SkipDexpreoptBootJars(ctx) {
return
}
@@ -131,14 +222,8 @@
// GenerateSingletonBuildActions method as it cannot create it for itself.
dexpreopt.GetGlobalSoongConfig(ctx)
- // Get a map of the image configs that are supported.
- imageConfigs := genBootImageConfigs(ctx)
-
- // Retrieve the config for this image.
- imageName := b.properties.Image_name
- imageConfig := imageConfigs[imageName]
+ imageConfig := b.getImageConfig(ctx)
if imageConfig == nil {
- ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
return
}
@@ -149,6 +234,25 @@
ctx.SetProvider(BootImageInfoProvider, info)
}
+func (b *BootImageModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+ // Get a map of the image configs that are supported.
+ imageConfigs := genBootImageConfigs(ctx)
+
+ // Retrieve the config for this image.
+ imageNamePtr := b.properties.Image_name
+ if imageNamePtr == nil {
+ return nil
+ }
+
+ imageName := *imageNamePtr
+ imageConfig := imageConfigs[imageName]
+ if imageConfig == nil {
+ ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+ return nil
+ }
+ return imageConfig
+}
+
type bootImageMemberType struct {
android.SdkMemberTypeBase
}
@@ -163,7 +267,11 @@
}
func (b *bootImageMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
- return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+ if b.PropertyName == "boot_images" {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+ } else {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_bootclasspath_fragment")
+ }
}
func (b *bootImageMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
@@ -173,7 +281,7 @@
type bootImageSdkMemberProperties struct {
android.SdkMemberPropertiesBase
- Image_name string
+ Image_name *string
}
func (b *bootImageSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -183,8 +291,8 @@
}
func (b *bootImageSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
- if b.Image_name != "" {
- propertySet.AddProperty("image_name", b.Image_name)
+ if b.Image_name != nil {
+ propertySet.AddProperty("image_name", *b.Image_name)
}
}
@@ -217,5 +325,10 @@
android.InitApexModule(m)
android.InitSdkAwareModule(m)
android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+ // Perform some consistency checking to ensure that the configuration is correct.
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ bootImageConsistencyCheck(ctx, &m.BootImageModule)
+ })
return m
}
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
index 65e590d..e1866de 100644
--- a/java/boot_image_test.go
+++ b/java/boot_image_test.go
@@ -16,25 +16,112 @@
import (
"testing"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
)
// Contains some simple tests for boot_image logic, additional tests can be found in
// apex/boot_image_test.go as the ART boot image requires modules from the ART apex.
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
func TestUnknownBootImage(t *testing.T) {
- testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
- boot_image {
- name: "unknown-boot-image",
- image_name: "unknown",
- }
-`)
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
+}
+
+func TestUnknownBootclasspathFragmentImageName(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ bootclasspath_fragment {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
}
func TestUnknownPrebuiltBootImage(t *testing.T) {
- testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
- prebuilt_boot_image {
- name: "unknown-boot-image",
- image_name: "unknown",
- }
-`)
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+ RunTestWithBp(t, `
+ prebuilt_boot_image {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+ `)
+}
+
+func TestBootImageInconsistentArtConfiguration_Platform(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\QArtApexJars is invalid as it requests a platform variant of "foo"\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "art",
+ apex_available: [
+ "apex",
+ ],
+ }
+ `)
+}
+
+func TestBootImageInconsistentArtConfiguration_ApexMixture(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForTestWithBootImage,
+ dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\QArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex "apex1" and "apex2"\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "art",
+ apex_available: [
+ "apex1",
+ "apex2",
+ ],
+ }
+ `)
+}
+
+func TestBootImageWithoutImageNameOrContents(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qneither of the "image_name" and "contents" properties\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ }
+ `)
+}
+
+func TestBootImageWithImageNameAndContents(t *testing.T) {
+ prepareForTestWithBootImage.
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+ `\Qboth of the "image_name" and "contents" properties\E`)).
+ RunTestWithBp(t, `
+ boot_image {
+ name: "boot-image",
+ image_name: "boot",
+ contents: ["other"],
+ }
+ `)
}
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 e94b20c..7137f33 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"path/filepath"
"sort"
"strings"
@@ -213,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",
@@ -438,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)
}
@@ -449,46 +452,61 @@
// be needed there too.
//
// TODO(b/177892522): Avoid having to perform this type of check or if necessary dedup it.
-func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
+func getBootJar(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
+ module android.Module, fromWhere string) (int, android.Path, *android.ApexInfo) {
+
name := ctx.ModuleName(module)
// Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
name = android.RemoveOptionalPrebuiltPrefix(name)
// Ignore any module that is not listed in the boot image configuration.
- index := image.modules.IndexOfJar(name)
+ index := bootjars.IndexOfJar(name)
if index == -1 {
- return -1, nil
+ return -1, nil, nil
}
// It is an error if a module configured in the boot image does not support accessing the dex jar.
// This is safe because every module that has the same name has to have the same module type.
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
if !hasJar {
- ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
- return -1, nil
+ ctx.Errorf("module %q %sdoes not support accessing dex jar", module, fromWhere)
+ return -1, nil, nil
}
// It is also an error if the module is not an ApexModule.
if _, ok := module.(android.ApexModule); !ok {
- ctx.Errorf("module %q configured in boot image %q does not support being added to an apex", module, image.name)
- return -1, nil
+ ctx.Errorf("module %q %sdoes not support being added to an apex", module, fromWhere)
+ return -1, nil, nil
}
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
// Now match the apex part of the boot image configuration.
- requiredApex := image.modules.Apex(index)
+ requiredApex := bootjars.Apex(index)
if requiredApex == "platform" {
if len(apexInfo.InApexes) != 0 {
// A platform variant is required but this is for an apex so ignore it.
- return -1, nil
+ return -1, nil, nil
}
} else if !apexInfo.InApexByBaseName(requiredApex) {
// An apex variant for a specific apex is required but this is the wrong apex.
+ return -1, nil, nil
+ }
+
+ return index, jar.DexJarBuildPath(), &apexInfo
+}
+
+// Inspect this module to see if it contains a bootclasspath dex jar from a given boot image.
+func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
+ fromImage := fmt.Sprintf("configured in boot image %q ", image.name)
+ index, jarPath, apexInfo := getBootJar(ctx, image.modules, module, fromImage)
+ if index == -1 {
return -1, nil
}
+ name := ctx.ModuleName(module)
+
// Check that this module satisfies any boot image specific constraints.
fromUpdatableApex := apexInfo.Updatable
@@ -525,39 +543,40 @@
panic("unknown boot image: " + image.name)
}
- return index, jar.DexJarBuildPath()
+ return index, jarPath
}
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
-func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
- // Collect dex jar paths for the boot image modules.
+// Generate commands that will copy boot jars to predefined paths in the global config.
+func findAndCopyBootJars(ctx android.SingletonContext, bootjars android.ConfiguredJarList,
+ jarPathsPredefined android.WritablePaths,
+ getBootJar func(module android.Module) (int, android.Path)) []string {
+
// This logic is tested in the apex package to avoid import cycle apex <-> java.
- bootDexJars := make(android.Paths, image.modules.Len())
+ jarPaths := make(android.Paths, bootjars.Len())
ctx.VisitAllModules(func(module android.Module) {
if !isActiveModule(module) {
return
}
-
- if i, j := getBootImageJar(ctx, image, module); i != -1 {
- if existing := bootDexJars[i]; existing != nil {
- ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
- image.modules.Apex(i), image.modules.Jar(i), existing, j)
+ if i, j := getBootJar(module); i != -1 {
+ if existing := jarPaths[i]; existing != nil {
+ ctx.Errorf("Multiple dex jars found for %s:%s - %q and %q",
+ bootjars.Apex(i), bootjars.Jar(i), existing, j)
return
}
-
- bootDexJars[i] = j
+ jarPaths[i] = j
}
})
var missingDeps []string
// Ensure all modules were converted to paths
- for i := range bootDexJars {
- if bootDexJars[i] == nil {
- m := image.modules.Jar(i)
+ for i := range jarPaths {
+ if jarPaths[i] == nil {
+ m := bootjars.Jar(i)
if ctx.Config().AllowMissingDependencies() {
missingDeps = append(missingDeps, m)
- bootDexJars[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex", image.modules.Apex(i))
+ jarPaths[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex",
+ bootjars.Apex(i))
} else {
ctx.Errorf("failed to find a dex jar path for module '%s'"+
", note that some jars may be filtered out by module constraints", m)
@@ -569,14 +588,24 @@
// time, before the boot images are built (these paths are used in dexpreopt rule generation for
// Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
// paths.
- for i := range bootDexJars {
+ for i := range jarPaths {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
- Input: bootDexJars[i],
- Output: image.dexPaths[i],
+ Input: jarPaths[i],
+ Output: jarPathsPredefined[i],
})
}
+ return missingDeps
+}
+
+// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
+ getBootJarFunc := func(module android.Module) (int, android.Path) {
+ return getBootImageJar(ctx, image, module)
+ }
+ missingDeps := findAndCopyBootJars(ctx, image.modules, image.dexPaths, getBootJarFunc)
+
profile := bootImageProfileRule(ctx, image, missingDeps)
bootFrameworkProfileRule(ctx, image, missingDeps)
updatableBcpPackagesRule(ctx, image, missingDeps)
@@ -603,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 {
@@ -970,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_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index aadf6ad..d78651d 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -43,22 +43,22 @@
}
`
- result := javaFixtureFactory.
- Extend(
- PrepareForTestWithJavaSdkLibraryFiles,
- FixtureWithLastReleaseApis("foo"),
- dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz"),
- ).RunTestWithBp(t, bp)
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar", "platform:baz"),
+ ).RunTestWithBp(t, bp)
dexpreoptBootJars := result.SingletonForTests("dex_bootjars")
rule := dexpreoptBootJars.Output(ruleFile)
for i := range expectedInputs {
- expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
+ expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i])
}
for i := range expectedOutputs {
- expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+ expectedOutputs[i] = filepath.Join("out/soong/test_device", expectedOutputs[i])
}
inputs := rule.Implicits.Strings()
@@ -69,9 +69,9 @@
sort.Strings(outputs)
sort.Strings(expectedOutputs)
- android.AssertDeepEquals(t, "inputs", expectedInputs, inputs)
+ android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
- android.AssertDeepEquals(t, "outputs", expectedOutputs, outputs)
+ android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
}
func TestDexpreoptBootJars(t *testing.T) {
@@ -107,7 +107,7 @@
func TestDexpreoptBootZip(t *testing.T) {
ruleFile := "boot.zip"
- ctx := android.PathContextForTesting(testConfig(nil, "", nil))
+ ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
expectedInputs := []string{}
for _, target := range ctx.Config().Targets[android.Android] {
for _, ext := range []string{".art", ".oat", ".vdex"} {
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..f7595b1 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
//
@@ -315,11 +226,8 @@
srcJars android.Paths
srcFiles android.Paths
sourcepaths android.Paths
- argFiles android.Paths
implicits android.Paths
- args []string
-
docZip android.WritablePath
stubsSrcJar android.WritablePath
}
@@ -569,15 +477,20 @@
j.sourcepaths = android.PathsForModuleSrc(ctx, []string{"."})
}
- j.argFiles = android.PathsForModuleSrc(ctx, j.properties.Arg_files)
+ return deps
+}
+
+func (j *Javadoc) expandArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
+ var argFiles android.Paths
argFilesMap := map[string]string{}
argFileLabels := []string{}
for _, label := range j.properties.Arg_files {
var paths = android.PathsForModuleSrc(ctx, []string{label})
if _, exists := argFilesMap[label]; !exists {
- argFilesMap[label] = strings.Join(paths.Strings(), " ")
+ argFilesMap[label] = strings.Join(cmd.PathsForInputs(paths), " ")
argFileLabels = append(argFileLabels, label)
+ argFiles = append(argFiles, paths...)
} else {
ctx.ModuleErrorf("multiple arg_files for %q, %q and %q",
label, argFilesMap[label], paths)
@@ -597,7 +510,7 @@
}
for _, flag := range flags {
- args, err := android.Expand(flag, func(name string) (string, error) {
+ expanded, err := android.Expand(flag, func(name string) (string, error) {
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
if paths, ok := argFilesMap[label]; ok {
@@ -615,10 +528,10 @@
if err != nil {
ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
}
- j.args = append(j.args, args)
+ cmd.Flag(expanded)
}
- return deps
+ cmd.Implicits(argFiles)
}
func (j *Javadoc) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -652,6 +565,8 @@
Flag("-XDignore.symbol.file").
Flag("-Xdoclint:none")
+ j.expandArgs(ctx, cmd)
+
rule.Command().
BuiltTool("soong_zip").
Flag("-write_if_changed").
@@ -910,7 +825,7 @@
deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
}
- cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
+ d.expandArgs(ctx, cmd)
if d.properties.Compat_config != nil {
compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
@@ -947,669 +862,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 +917,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 +941,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..8d1f591
--- /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")
+ 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..d7a0668
--- /dev/null
+++ b/java/droidstubs.go
@@ -0,0 +1,903 @@
+// 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 :=
+ strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
+ 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 dep.Base() == filename {
+ cmd.Implicit(dep)
+ }
+ if filename != "android.jar" && dep.Base() == "android.jar" {
+ // Metalava implicitly searches these patterns:
+ // prebuilts/tools/common/api-versions/android-%/android.jar
+ // prebuilts/sdk/%/public/android.jar
+ // Add android.jar files from the api_levels_annotations_dirs directories to try
+ // to satisfy these patterns. If Metalava can't find a match for an API level
+ // between 1 and 28 in at least one pattern it will fail.
+ 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.BoolDefault(d.Javadoc.properties.Sandbox, true)
+ 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)
+
+ d.expandArgs(ctx, cmd)
+
+ 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..f8125fb
--- /dev/null
+++ b/java/droidstubs_test.go
@@ -0,0 +1,174 @@
+// 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,
+ sandbox: false,
+ }
+
+ 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",
+ sandbox: false,
+ }
+ `,
+ 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, `
+ genrule {
+ name: "foo",
+ out: ["foo.txt"],
+ cmd: "touch $(out)",
+ }
+
+ droidstubs {
+ name: "bar-stubs",
+ srcs: ["bar-doc/a.java"],
+ sandbox: true,
+
+ args: "--reference $(location :foo)",
+ arg_files: [":foo"],
+ }
+ `,
+ 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)
+ }
+
+ manifest := android.RuleBuilderSboxProtoForTests(t, m.Output("metalava.sbox.textproto"))
+ if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) {
+ t.Errorf("Expected command to contain %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/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index f17d436..dc4e8aa 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -35,7 +35,8 @@
})
}
-var hiddenApiFixtureFactory = javaFixtureFactory.Extend(PrepareForTestWithHiddenApiBuildComponents)
+var hiddenApiFixtureFactory = android.GroupFixturePreparers(
+ prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
func TestHiddenAPISingleton(t *testing.T) {
result := hiddenApiFixtureFactory.Extend(
@@ -50,7 +51,7 @@
hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+ want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
}
@@ -145,7 +146,7 @@
hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+ want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
}
@@ -169,10 +170,10 @@
hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+ fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
- prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+ prebuiltJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/dex/foo.jar"
android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
}
@@ -196,10 +197,10 @@
hiddenAPI := result.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
+ prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
- fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+ fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
android.AssertStringDoesNotContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
}
@@ -260,7 +261,7 @@
}
func generateDexedPath(subDir, dex, module string) string {
- return fmt.Sprintf("%s/.intermediates/%s/android_common/%s/%s.jar", buildDir, subDir, dex, module)
+ return fmt.Sprintf("out/soong/.intermediates/%s/android_common/%s/%s.jar", subDir, dex, module)
}
func generateDexPath(moduleDir string, module string) string {
@@ -297,8 +298,8 @@
`)
expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
- expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
- expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+ expectedCpOutput := "out/soong/hiddenapi/hiddenapi-flags.csv"
+ expectedFlagsCsv := "out/soong/hiddenapi/hiddenapi-flags.csv"
foo := result.ModuleForTests("foo", "android_common")
@@ -309,9 +310,9 @@
encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
- android.AssertStringEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput.String())
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
- android.AssertStringEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput.String())
+ android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule output", expectedCpOutput, actualCpOutput)
android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv)
}
diff --git a/java/java.go b/java/java.go
index 9786947..70ad879 100644
--- a/java/java.go
+++ b/java/java.go
@@ -33,12 +33,12 @@
)
func init() {
- RegisterJavaBuildComponents(android.InitRegistrationContext)
+ registerJavaBuildComponents(android.InitRegistrationContext)
RegisterJavaSdkMemberTypes()
}
-func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
+func registerJavaBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("java_defaults", DefaultsFactory)
ctx.RegisterModuleType("java_library", LibraryFactory)
diff --git a/java/java_test.go b/java/java_test.go
index 99a96e1..fdf7579 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -34,107 +33,31 @@
"android/soong/python"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_java_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
-var emptyFixtureFactory = android.NewFixtureFactory(&buildDir)
-
-// Factory to use to create fixtures for tests in this package.
-var javaFixtureFactory = emptyFixtureFactory.Extend(
+// Legacy preparer used for running tests within the java package.
+//
+// This includes everything that was needed to run any test in the java package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testJava...() methods should avoid using this and instead use the
+// various preparers directly, using android.GroupFixturePreparers(...) to group them when
+// necessary.
+//
+// deprecated
+var prepareForJavaTest = android.GroupFixturePreparers(
genrule.PrepareForTestWithGenRuleBuildComponents,
// Get the CC build components but not default modules.
cc.PrepareForTestWithCcBuildComponents,
// Include all the default java modules.
PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
python.PrepareForTestWithPythonBuildComponents,
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("java_plugin", PluginFactory)
-
- ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
}),
- dexpreopt.PrepareForTestWithDexpreopt,
+ PrepareForTestWithDexpreopt,
)
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
-}
-
-// testConfig is a legacy way of creating a test Config for testing java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
- return TestConfig(buildDir, env, bp, fs)
-}
-
-// testContext is a legacy way of creating a TestContext for testing java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testContext(config android.Config) *android.TestContext {
-
- ctx := android.NewTestArchContext(config)
- RegisterRequiredBuildComponentsForTest(ctx)
- ctx.RegisterModuleType("java_plugin", PluginFactory)
- ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.PreArchMutators(android.RegisterComponentsMutator)
-
- ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
- ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
-
- android.RegisterPrebuiltMutators(ctx)
-
- genrule.RegisterGenruleBuildComponents(ctx)
-
- // Register module types and mutators from cc needed for JNI testing
- cc.RegisterRequiredBuildComponentsForTest(ctx)
-
- ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("propagate_rro_enforcement", propagateRROEnforcementMutator).Parallel()
- })
-
- return ctx
-}
-
-// run is a legacy way of running tests of java modules.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func run(t *testing.T, ctx *android.TestContext, config android.Config) {
- t.Helper()
-
- pathCtx := android.PathContextForTesting(config)
- dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
-
- ctx.Register()
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ os.Exit(m.Run())
}
// testJavaError is a legacy way of running tests of java modules that expect errors.
@@ -144,102 +67,42 @@
// deprecated
func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
t.Helper()
- result := javaFixtureFactory.
- Extend(dexpreopt.PrepareForTestWithDexpreopt).
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, dexpreopt.PrepareForTestByEnablingDexpreopt).
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
RunTestWithBp(t, bp)
return result.TestContext, result.Config
}
-// testJavaErrorWithConfig is a legacy way of running tests of java modules that expect errors.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
- t.Helper()
- // This must be done on the supplied config and not as part of the fixture because any changes to
- // the fixture's config will be ignored when RunTestWithConfig replaces it.
- pathCtx := android.PathContextForTesting(config)
- dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
- result := javaFixtureFactory.
- ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
- RunTestWithConfig(t, config)
- return result.TestContext, result.Config
-}
-
-// runWithErrors is a legacy way of running tests of java modules that expect errors.
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func runWithErrors(t *testing.T, ctx *android.TestContext, config android.Config, pattern string) {
- ctx.Register()
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
- _, errs = ctx.PrepareBuildActions(config)
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
-
- t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
- return
-}
-
-// testJavaWithFS runs tests using the javaFixtureFactory
+// testJavaWithFS runs tests using the prepareForJavaTest
//
// See testJava for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testJavaWithFS(t *testing.T, bp string, fs android.MockFS) (*android.TestContext, android.Config) {
t.Helper()
- result := javaFixtureFactory.Extend(fs.AddToFixture()).RunTestWithBp(t, bp)
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, fs.AddToFixture()).RunTestWithBp(t, bp)
return result.TestContext, result.Config
}
-// testJava runs tests using the javaFixtureFactory
+// testJava runs tests using the prepareForJavaTest
//
-// Do not add any new usages of this, instead use the javaFixtureFactory directly as it makes it
+// Do not add any new usages of this, instead use the prepareForJavaTest directly as it makes it
// much easier to customize the test behavior.
//
// If it is necessary to customize the behavior of an existing test that uses this then please first
-// convert the test to using javaFixtureFactory first and then in a following change add the
+// convert the test to using prepareForJavaTest first and then in a following change add the
// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
// that it did not change the test behavior unexpectedly.
//
// deprecated
func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
t.Helper()
- result := javaFixtureFactory.RunTestWithBp(t, bp)
+ result := prepareForJavaTest.RunTestWithBp(t, bp)
return result.TestContext, result.Config
}
-// testJavaWithConfig runs tests using the javaFixtureFactory
-//
-// See testJava for an explanation as to how to stop using this deprecated method.
-//
-// deprecated
-func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
- t.Helper()
- result := javaFixtureFactory.RunTestWithConfig(t, config)
- return result.TestContext, result.Config
-}
-
-func moduleToPath(name string) string {
- switch {
- case name == `""`:
- return name
- case strings.HasSuffix(name, ".jar"):
- return name
- default:
- return filepath.Join(buildDir, ".intermediates", name, "android_common", "turbine-combined", name+".jar")
- }
-}
-
// defaultModuleToPath constructs a path to the turbine generate jar for a default test module that
// is defined in PrepareForIntegrationTestWithJava
func defaultModuleToPath(name string) string {
@@ -249,10 +112,19 @@
case strings.HasSuffix(name, ".jar"):
return name
default:
- return filepath.Join(buildDir, ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
+ return filepath.Join("out", "soong", ".intermediates", defaultJavaDir, name, "android_common", "turbine-combined", name+".jar")
}
}
+// 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 {
@@ -369,8 +241,8 @@
}
baz := ctx.ModuleForTests("baz", "android_common").Rule("javac").Output.String()
- barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
- bazTurbine := filepath.Join(buildDir, ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
+ barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ bazTurbine := filepath.Join("out", "soong", ".intermediates", "baz", "android_common", "turbine-combined", "baz.jar")
android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barTurbine)
@@ -512,13 +384,19 @@
}
`
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ errorHandler := android.FixtureExpectsNoErrors
if enforce {
- testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", config)
- } else {
- testJavaWithConfig(t, config)
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
}
+
+ android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+ }),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, bp)
}
}
@@ -596,13 +474,16 @@
srcs: ["b.java"],
}
`
- config := testConfig(nil, bp, nil)
- config.TestProductVariables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
- ctx, _ := testJavaWithConfig(t, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.MinimizeJavaDebugInfo = proptools.BoolPtr(true)
+ }),
+ ).RunTestWithBp(t, bp)
// first, check that the -g flag is added to target modules
- targetLibrary := ctx.ModuleForTests("target_library", "android_common")
+ targetLibrary := result.ModuleForTests("target_library", "android_common")
targetJavaFlags := targetLibrary.Module().VariablesForTests()["javacFlags"]
if !strings.Contains(targetJavaFlags, "-g:source,lines") {
t.Errorf("target library javac flags %v should contain "+
@@ -611,7 +492,7 @@
// check that -g is not overridden for host modules
buildOS := android.BuildOs.String()
- hostBinary := ctx.ModuleForTests("host_binary", buildOS+"_common")
+ hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
if strings.Contains(hostJavaFlags, "-g:source,lines") {
t.Errorf("java_binary_host javac flags %v should not have "+
@@ -699,11 +580,9 @@
t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
}
- bazDexJar := bazModule.Module().(*Import).DexJarBuildPath().String()
- expectedDexJar := buildDir + "/.intermediates/baz/android_common/dex/baz.jar"
- if bazDexJar != expectedDexJar {
- t.Errorf("baz dex jar build path expected %q, got %q", expectedDexJar, bazDexJar)
- }
+ bazDexJar := bazModule.Module().(*Import).DexJarBuildPath()
+ expectedDexJar := "out/soong/.intermediates/baz/android_common/dex/baz.jar"
+ android.AssertPathRelativeToTopEquals(t, "baz dex jar build path", expectedDexJar, bazDexJar)
ctx.ModuleForTests("qux", "android_common").Rule("Cp")
}
@@ -732,7 +611,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) {
@@ -744,7 +623,7 @@
}
func TestJavaSdkLibraryImport(t *testing.T) {
- result := javaFixtureFactory.RunTestWithBp(t, `
+ result := prepareForJavaTest.RunTestWithBp(t, `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -798,7 +677,8 @@
}
func TestJavaSdkLibraryImport_WithSource(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("sdklib"),
).RunTestWithBp(t, `
@@ -840,7 +720,8 @@
}
func TestJavaSdkLibraryImport_Preferred(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("sdklib"),
).RunTestWithBp(t, `
@@ -942,12 +823,17 @@
}
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)
}
- javaFixtureFactory.ExtendWithErrorHandler(errorHandler).RunTest(t, createPreparer(info))
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ createPreparer(info),
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTest(t)
})
}
@@ -1085,7 +971,7 @@
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- barTurbine := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ barTurbine := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
if !strings.Contains(javac.Args["classpath"], barTurbine) {
t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
}
@@ -1331,33 +1217,22 @@
}
func TestJavaLintRequiresCustomLintFileToExist(t *testing.T) {
- config := testConfig(
- 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) {
@@ -1425,8 +1300,8 @@
}
func TestTurbine(t *testing.T) {
- result := javaFixtureFactory.
- Extend(FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{"14": {"foo"}})).
RunTestWithBp(t, `
java_library {
name: "foo",
@@ -1455,13 +1330,13 @@
barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
- android.AssertArrayString(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs.Strings())
+ android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
- fooHeaderJar := filepath.Join(buildDir, ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
- barTurbineJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine", "bar.jar")
+ fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
+ barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar)
android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
- android.AssertArrayString(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs.Strings())
+ android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs)
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar")
}
@@ -1474,7 +1349,7 @@
}
`)
- barHeaderJar := filepath.Join(buildDir, ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
+ barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
for i := 0; i < 3; i++ {
barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
@@ -1483,246 +1358,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")
- 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 " + buildDir + "/.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 {
@@ -1804,7 +1439,7 @@
}
func TestJavaLibrary(t *testing.T) {
- config := testConfig(nil, "", map[string][]byte{
+ testJavaWithFS(t, "", map[string][]byte{
"libcore/Android.bp": []byte(`
java_library {
name: "core",
@@ -1816,14 +1451,12 @@
name: "core-jar",
srcs: [":core{.jar}"],
}
-`),
+ `),
})
- ctx := testContext(config)
- run(t, ctx, config)
}
func TestJavaImport(t *testing.T) {
- config := testConfig(nil, "", map[string][]byte{
+ testJavaWithFS(t, "", map[string][]byte{
"libcore/Android.bp": []byte(`
java_import {
name: "core",
@@ -1834,14 +1467,13 @@
name: "core-jar",
srcs: [":core{.jar}"],
}
-`),
+ `),
})
- ctx := testContext(config)
- run(t, ctx, config)
}
func TestJavaSdkLibrary(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithPrebuiltApis(map[string][]string{
"28": {"foo"},
@@ -1966,7 +1598,8 @@
}
func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("sdklib"),
).RunTestWithBp(t, `
@@ -2002,7 +1635,8 @@
}
func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2024,13 +1658,14 @@
// The bar library should depend on the stubs jar.
barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
- if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
- javaFixtureFactory.Extend(
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2051,11 +1686,11 @@
}
func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
- javaFixtureFactory.
- Extend(
- PrepareForTestWithJavaSdkLibraryFiles,
- FixtureWithLastReleaseApis("foo"),
- ).
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("foo"),
+ ).
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`"foo" does not provide api scope system`)).
RunTestWithBp(t, `
java_sdk_library {
@@ -2075,7 +1710,8 @@
}
func TestJavaSdkLibrary_Deps(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("sdklib"),
).RunTestWithBp(t, `
@@ -2100,7 +1736,7 @@
}
func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
- javaFixtureFactory.RunTestWithBp(t, `
+ prepareForJavaTest.RunTestWithBp(t, `
java_sdk_library_import {
name: "foo",
public: {
@@ -2133,7 +1769,7 @@
`
t.Run("stubs.source", func(t *testing.T) {
- javaFixtureFactory.
+ prepareForJavaTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`stubs.source not available for api scope public`)).
RunTestWithBp(t, bp+`
java_library {
@@ -2148,7 +1784,7 @@
})
t.Run("api.txt", func(t *testing.T) {
- javaFixtureFactory.
+ prepareForJavaTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`api.txt not available for api scope public`)).
RunTestWithBp(t, bp+`
java_library {
@@ -2162,7 +1798,7 @@
})
t.Run("removed-api.txt", func(t *testing.T) {
- javaFixtureFactory.
+ prepareForJavaTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`removed-api.txt not available for api scope public`)).
RunTestWithBp(t, bp+`
java_library {
@@ -2177,7 +1813,7 @@
}
func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
- javaFixtureFactory.
+ prepareForJavaTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": enabled api scope "system" depends on disabled scope "public"`)).
RunTestWithBp(t, `
java_sdk_library {
@@ -2197,7 +1833,8 @@
}
func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
- javaFixtureFactory.Extend(
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2214,7 +1851,8 @@
}
func TestJavaSdkLibrary_ModuleLib(t *testing.T) {
- javaFixtureFactory.Extend(
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2233,7 +1871,8 @@
}
func TestJavaSdkLibrary_SystemServer(t *testing.T) {
- javaFixtureFactory.Extend(
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2252,7 +1891,7 @@
}
func TestJavaSdkLibrary_MissingScope(t *testing.T) {
- javaFixtureFactory.
+ prepareForJavaTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`requires api scope module-lib from foo but it only has \[\] available`)).
RunTestWithBp(t, `
java_sdk_library {
@@ -2273,7 +1912,8 @@
}
func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
- javaFixtureFactory.Extend(
+ android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2296,7 +1936,8 @@
}
func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
- result := javaFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
PrepareForTestWithJavaSdkLibraryFiles,
FixtureWithLastReleaseApis("foo"),
).RunTestWithBp(t, `
@@ -2320,7 +1961,7 @@
`)
// The baz library should depend on the system stubs jar.
bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
- if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+ if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
t.Errorf("expected %q, found %#q", expected, actual)
}
}
@@ -2388,7 +2029,7 @@
// TODO(jungjw): Consider making this more robust by ignoring path order.
func checkPatchModuleFlag(t *testing.T, ctx *android.TestContext, moduleName string, expected string) {
- variables := ctx.ModuleForTests(moduleName, "android_common").Module().VariablesForTests()
+ variables := ctx.ModuleForTests(moduleName, "android_common").VariablesForTestsRelativeToTop()
flags := strings.Split(variables["javacFlags"], " ")
got := ""
for _, flag := range flags {
@@ -2398,7 +2039,7 @@
break
}
}
- if expected != got {
+ if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) {
t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
}
}
@@ -2468,10 +2109,10 @@
ctx, _ := testJava(t, bp)
checkPatchModuleFlag(t, ctx, "foo", "")
- expected := "java.base=.:" + buildDir
+ expected := "java.base=.:out/soong"
checkPatchModuleFlag(t, ctx, "bar", expected)
expected = "java.base=" + strings.Join([]string{
- ".", buildDir, "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
+ ".", "out/soong", "dir", "dir2", "nested", defaultModuleToPath("ext"), defaultModuleToPath("framework")}, ":")
checkPatchModuleFlag(t, ctx, "baz", expected)
})
}
@@ -2590,11 +2231,9 @@
test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
- expected := []string{buildDir + "/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
+ expected := []string{"out/soong/.intermediates/bin/" + buildOS + "_x86_64_PY3/bin:bin"}
actual := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
- }
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_COMPATIBILITY_SUPPORT_FILES", ctx.Config(), expected, actual)
}
func TestDefaultInstallable(t *testing.T) {
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 77ef294..1c146a1 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -176,19 +176,22 @@
env := map[string]string{
"RUN_ERROR_PRONE": "true",
}
- config := testConfig(env, bp, nil)
- ctx, _ := testJavaWithConfig(t, config)
+
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(env),
+ ).RunTestWithBp(t, bp)
buildOS := android.BuildOs.String()
- kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
//kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
- errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+ javac := result.ModuleForTests("foo", "android_common").Description("javac")
+ errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
- bar := ctx.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
- baz := ctx.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
- myCheck := ctx.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+ bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
+ baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
+ myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
// Test that the errorprone plugins are not passed to kapt
expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
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 3c43a8e..c3d13ae 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -15,12 +15,23 @@
package java
import (
+ "path/filepath"
+
"android/soong/android"
+ "github.com/google/blueprint"
+
"fmt"
)
func init() {
registerPlatformCompatConfigBuildComponents(android.InitRegistrationContext)
+
+ android.RegisterSdkMemberType(&compatConfigMemberType{
+ SdkMemberTypeBase: android.SdkMemberTypeBase{
+ PropertyName: "compat_configs",
+ SupportsSdk: true,
+ },
+ })
}
func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
@@ -42,6 +53,7 @@
type platformCompatConfig struct {
android.ModuleBase
+ android.SdkBase
properties platformCompatConfigProperties
installDirPath android.InstallPath
@@ -113,10 +125,54 @@
func PlatformCompatConfigFactory() android.Module {
module := &platformCompatConfig{}
module.AddProperties(&module.properties)
+ android.InitSdkAwareModule(module)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
+type compatConfigMemberType struct {
+ android.SdkMemberTypeBase
+}
+
+func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+ mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
+ _, ok := module.(*platformCompatConfig)
+ return ok
+}
+
+func (b *compatConfigMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+ return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_platform_compat_config")
+}
+
+func (b *compatConfigMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+ return &compatConfigSdkMemberProperties{}
+}
+
+type compatConfigSdkMemberProperties struct {
+ android.SdkMemberPropertiesBase
+
+ Metadata android.Path
+}
+
+func (b *compatConfigSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+ module := variant.(*platformCompatConfig)
+ b.Metadata = module.metadataFile
+}
+
+func (b *compatConfigSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+ builder := ctx.SnapshotBuilder()
+ if b.Metadata != nil {
+ snapshotRelativePath := filepath.Join("compat_configs", ctx.Name(), b.Metadata.Base())
+ builder.CopyToSnapshot(b.Metadata, snapshotRelativePath)
+ propertySet.AddProperty("metadata", snapshotRelativePath)
+ }
+}
+
+var _ android.SdkMemberType = (*compatConfigMemberType)(nil)
+
// A prebuilt version of the platform compat config module.
type prebuiltCompatConfigModule struct {
android.ModuleBase
@@ -165,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/platform_compat_config_test.go b/java/platform_compat_config_test.go
index 0c5d001..80d991c 100644
--- a/java/platform_compat_config_test.go
+++ b/java/platform_compat_config_test.go
@@ -21,7 +21,7 @@
)
func TestPlatformCompatConfig(t *testing.T) {
- result := emptyFixtureFactory.RunTest(t,
+ result := android.GroupFixturePreparers(
PrepareForTestWithPlatformCompatConfig,
android.FixtureWithRootAndroidBp(`
platform_compat_config {
@@ -34,20 +34,11 @@
name: "myconfig3",
}
`),
- )
+ ).RunTest(t)
- checkMergedCompatConfigInputs(t, result, "myconfig",
+ CheckMergedCompatConfigInputs(t, result, "myconfig",
"out/soong/.intermediates/myconfig1/myconfig1_meta.xml",
"out/soong/.intermediates/myconfig2/myconfig2_meta.xml",
"out/soong/.intermediates/myconfig3/myconfig3_meta.xml",
)
}
-
-// Check that the merged file create by platform_compat_config_singleton has the correct inputs.
-func checkMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
- sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
- allOutputs := sourceGlobalCompatConfig.AllOutputs()
- android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
- output := sourceGlobalCompatConfig.Output(allOutputs[0])
- android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
-}
diff --git a/java/plugin.go b/java/plugin.go
index 947c286..297ac2c 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -17,7 +17,11 @@
import "android/soong/android"
func init() {
- android.RegisterModuleType("java_plugin", PluginFactory)
+ registerJavaPluginBuildComponents(android.InitRegistrationContext)
+}
+
+func registerJavaPluginBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_plugin", PluginFactory)
}
// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
diff --git a/java/rro_test.go b/java/rro_test.go
index 061d9d3..bad60bc 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -24,7 +24,7 @@
)
func TestRuntimeResourceOverlay(t *testing.T) {
- fs := map[string][]byte{
+ fs := android.MockFS{
"baz/res/res/values/strings.xml": nil,
"bar/res/res/values/strings.xml": nil,
}
@@ -57,12 +57,15 @@
sdk_version: "current",
resource_dirs: ["baz/res"],
}
- `
- config := testAppConfig(nil, bp, fs)
- ctx := testContext(config)
- run(t, ctx, config)
+ `
- m := ctx.ModuleForTests("foo", "android_common")
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, bp)
+
+ m := result.ModuleForTests("foo", "android_common")
// Check AAPT2 link flags.
aapt2Flags := m.Output("package-res.apk").Args["flags"]
@@ -73,14 +76,14 @@
}
// Check overlay.list output for static_libs dependency.
- overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
- staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+ overlayList := android.PathsRelativeToTop(m.Output("aapt2/overlay.list").Inputs)
+ staticLibPackage := "out/soong/.intermediates/bar/android_common/package-res.apk"
if !inList(staticLibPackage, overlayList) {
t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
}
// Check AAPT2 link flags for resource_libs dependency.
- resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+ resourceLibFlag := "-I " + "out/soong/.intermediates/baz/android_common/package-res.apk"
if !strings.Contains(aapt2Flags, resourceLibFlag) {
t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
}
@@ -97,7 +100,7 @@
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
- androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
+ androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
if !reflect.DeepEqual(path, expectedPath) {
@@ -106,19 +109,15 @@
// Check device location.
path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay")}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
// A themed module has a different device location
- m = ctx.ModuleForTests("foo_themed", "android_common")
- androidMkEntries = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0]
+ m = result.ModuleForTests("foo_themed", "android_common")
+ androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/faza")}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
expectedOverrides := []string{"foo"}
@@ -128,7 +127,7 @@
}
func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
- ctx, _ := testJava(t, `
+ ctx, config := testJava(t, `
java_defaults {
name: "rro_defaults",
theme: "default_theme",
@@ -161,10 +160,8 @@
// Check device location.
path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath := []string{shared.JoinPath(buildDir, "../target/product/test_device/product/overlay/default_theme")}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
- }
+ expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
//
// RRO module without defaults
@@ -180,10 +177,8 @@
// Check device location.
path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{shared.JoinPath(buildDir, "../target/product/test_device/system/overlay")}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
+ android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
}
func TestOverrideRuntimeResourceOverlay(t *testing.T) {
@@ -214,7 +209,7 @@
}{
{
variantName: "android_common",
- apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
+ apkPath: "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
overrides: nil,
targetVariant: "android_common",
packageFlag: "",
@@ -222,7 +217,7 @@
},
{
variantName: "android_common_bar_overlay",
- apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
+ apkPath: "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
overrides: []string{"foo_overlay"},
targetVariant: "android_common_bar",
packageFlag: "com.android.bar.overlay",
@@ -233,18 +228,7 @@
variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
// Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
+ variant.Output(expected.apkPath)
// Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*RuntimeResourceOverlay)
@@ -298,7 +282,7 @@
"product/vendor/blah/overlay",
}
- fs := map[string][]byte{
+ fs := android.MockFS{
"lib2/res/values/strings.xml": nil,
"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
}
@@ -334,19 +318,22 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
+ result := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ PrepareForTestWithOverlayBuildComponents,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ variables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ }),
+ ).RunTestWithBp(t, bp)
modules := []string{"foo", "bar"}
for _, moduleName := range modules {
- module := ctx.ModuleForTests(moduleName, "android_common")
- mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0]
+ module := result.ModuleForTests(moduleName, "android_common")
+ mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 37875a4..e1ec41b 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -16,7 +16,6 @@
import (
"path/filepath"
- "reflect"
"strings"
"testing"
@@ -98,7 +97,7 @@
bootclasspath: []string{"android_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -107,7 +106,7 @@
bootclasspath: []string{"android_system_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_system_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -135,7 +134,7 @@
bootclasspath: []string{"android_test_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_test_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
{
@@ -222,7 +221,7 @@
bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_module_lib_stubs_current"},
- aidl: "-p" + buildDir + "/framework_non_updatable.aidl",
+ aidl: "-pout/soong/framework_non_updatable.aidl",
},
{
name: "system_server_current",
@@ -230,7 +229,7 @@
bootclasspath: []string{"android_system_server_stubs_current", "core-lambda-stubs"},
system: "core-current-stubs-system-modules",
java9classpath: []string{"android_system_server_stubs_current"},
- aidl: "-p" + buildDir + "/framework.aidl",
+ aidl: "-pout/soong/framework.aidl",
},
}
@@ -303,12 +302,12 @@
} else {
dir = defaultJavaDir
}
- system = "--system=" + filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system")
+ system = "--system=" + filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system")
// The module-relative parts of these paths are hardcoded in system_modules.go:
systemDeps = []string{
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
- filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "release"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
+ filepath.Join("out", "soong", ".intermediates", dir, testcase.system, "android_common", "system", "release"),
}
}
@@ -319,7 +318,7 @@
aidl := foo.MaybeRule("aidl")
if aidl.Rule != nil {
- deps = append(deps, aidl.Output.String())
+ deps = append(deps, android.PathRelativeToTop(aidl.Output))
}
got := javac.Args["bootClasspath"]
@@ -347,12 +346,11 @@
t.Errorf("classpath expected %q != got %q", expected, got)
}
- if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
- t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
- }
+ android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits)
}
- fixtureFactory := javaFixtureFactory.Extend(
+ fixtureFactory := android.GroupFixturePreparers(
+ prepareForJavaTest,
FixtureWithPrebuiltApis(map[string][]string{
"29": {},
"30": {},
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 3d9f398..7b5a386 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -20,12 +20,12 @@
"android/soong/android"
)
-func getModuleHeaderJarsAsNormalizedPaths(result *android.TestResult, moduleNames ...string) []string {
+func getModuleHeaderJarsAsRelativeToTopPaths(result *android.TestResult, moduleNames ...string) []string {
paths := []string{}
for _, moduleName := range moduleNames {
module := result.Module(moduleName, "android_common")
info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
- paths = append(paths, result.NormalizePathsForTesting(info.HeaderJars)...)
+ paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
}
return paths
}
@@ -50,15 +50,15 @@
`)
func TestJavaSystemModules(t *testing.T) {
- result := javaFixtureFactory.RunTest(t, addSourceSystemModules)
+ result := android.GroupFixturePreparers(prepareForJavaTest, addSourceSystemModules).RunTest(t)
// check the existence of the source module
sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
// The expected paths are the header jars from the source input modules.
- expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
- android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+ expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
}
var addPrebuiltSystemModules = android.FixtureAddTextFile("prebuilts/Android.bp", `
@@ -77,36 +77,37 @@
`)
func TestJavaSystemModulesImport(t *testing.T) {
- result := javaFixtureFactory.RunTest(t, addPrebuiltSystemModules)
+ result := android.GroupFixturePreparers(prepareForJavaTest, addPrebuiltSystemModules).RunTest(t)
// check the existence of the renamed prebuilt module
prebuiltSystemModules := result.ModuleForTests("system-modules", "android_common")
prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
// The expected paths are the header jars from the renamed prebuilt input modules.
- expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
- android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+ expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
}
func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
- result := javaFixtureFactory.RunTest(t,
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
addSourceSystemModules,
addPrebuiltSystemModules,
- )
+ ).RunTest(t)
// check the existence of the source module
sourceSystemModules := result.ModuleForTests("system-modules", "android_common")
sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
// The expected paths are the header jars from the source input modules.
- expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
- android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+ expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+ android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
// check the existence of the renamed prebuilt module
prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
// The expected paths are the header jars from the renamed prebuilt input modules.
- expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
- android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+ expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
+ android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
}
diff --git a/java/testing.go b/java/testing.go
index 4b8b849..1113af7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -43,17 +43,46 @@
// Make sure that mutators and module types, e.g. prebuilt mutators available.
android.PrepareForTestWithAndroidBuildComponents,
// Make java build components available to the test.
- android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+ 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()),
+ android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
+ // Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
+ dexpreopt.PrepareForTestWithDexpreoptCompatLibs,
+ dexpreopt.PrepareForTestWithFakeDex2oatd,
)
+// Provides everything needed by dexpreopt.
+var PrepareForTestWithDexpreopt = android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+var PrepareForTestWithOverlayBuildComponents = android.FixtureRegisterWithContext(registerOverlayBuildComponents)
+
// Prepare a fixture to use all java module types, mutators and singletons fully.
//
// This should only be used by tests that want to run with as much of the build enabled as possible.
@@ -131,28 +160,6 @@
)
}
-func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) android.Config {
- bp += GatherRequiredDepsForTest()
-
- mockFS := android.MockFS{}
-
- cc.GatherRequiredFilesForTest(mockFS)
-
- for k, v := range fs {
- mockFS[k] = v
- }
-
- if env == nil {
- env = make(map[string]string)
- }
- if env["ANDROID_JAVA8_HOME"] == "" {
- env["ANDROID_JAVA8_HOME"] = "jdk8"
- }
- config := android.TestArchConfig(buildDir, env, bp, mockFS)
-
- return config
-}
-
func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
fs := make(map[string][]byte)
for _, level := range apiLevels {
@@ -171,11 +178,13 @@
return fs
}
-// Register build components provided by this package that are needed by tests.
+// registerRequiredBuildComponentsForTest registers the build components used by
+// PrepareForTestWithJavaDefaultModules.
//
-// In particular this must register all the components that are used in the `Android.bp` snippet
-// returned by GatherRequiredDepsForTest()
-func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
RegisterAARBuildComponents(ctx)
RegisterAppBuildComponents(ctx)
RegisterAppImportBuildComponents(ctx)
@@ -184,21 +193,21 @@
RegisterDexpreoptBootJarsComponents(ctx)
RegisterDocsBuildComponents(ctx)
RegisterGenRuleBuildComponents(ctx)
- RegisterJavaBuildComponents(ctx)
+ registerJavaBuildComponents(ctx)
RegisterPrebuiltApisBuildComponents(ctx)
RegisterRuntimeResourceOverlayBuildComponents(ctx)
RegisterSdkLibraryBuildComponents(ctx)
RegisterStubsBuildComponents(ctx)
RegisterSystemModulesBuildComponents(ctx)
-
- // Make sure that any tool related module types needed by dexpreopt have been registered.
- dexpreopt.RegisterToolModulesForTest(ctx)
}
-// Gather the module definitions needed by tests that depend upon code from this package.
+// gatherRequiredDepsForTest gathers the module definitions used by
+// PrepareForTestWithJavaDefaultModules.
//
-// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
-func GatherRequiredDepsForTest() string {
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func gatherRequiredDepsForTest() string {
var bp string
extraModules := []string{
@@ -230,24 +239,6 @@
`, extra)
}
- // For class loader context and <uses-library> tests.
- dexpreoptModules := []string{"android.test.runner"}
- dexpreoptModules = append(dexpreoptModules, dexpreopt.CompatUsesLibs...)
- dexpreoptModules = append(dexpreoptModules, dexpreopt.OptionalCompatUsesLibs...)
-
- for _, extra := range dexpreoptModules {
- bp += fmt.Sprintf(`
- java_library {
- name: "%s",
- srcs: ["a.java"],
- sdk_version: "none",
- system_modules: "stable-core-platform-api-stubs-system-modules",
- compile_dex: true,
- installable: true,
- }
- `, extra)
- }
-
bp += `
java_library {
name: "framework",
@@ -284,9 +275,6 @@
`, extra)
}
- // Make sure that any tools needed for dexpreopting are defined.
- bp += dexpreopt.BpToolModulesForTest()
-
// Make sure that the dex_bootjars singleton module is instantiated for the tests.
bp += `
dex_bootjars {
@@ -319,3 +307,12 @@
t.Errorf("Expected hiddenapi rule inputs:\n%s\nactual inputs:\n%s", expected, actual)
}
}
+
+// Check that the merged file create by platform_compat_config_singleton has the correct inputs.
+func CheckMergedCompatConfigInputs(t *testing.T, result *android.TestResult, message string, expectedPaths ...string) {
+ sourceGlobalCompatConfig := result.SingletonForTests("platform_compat_config_singleton")
+ allOutputs := sourceGlobalCompatConfig.AllOutputs()
+ android.AssertIntEquals(t, message+": output len", 1, len(allOutputs))
+ output := sourceGlobalCompatConfig.Output(allOutputs[0])
+ android.AssertPathsRelativeToTopEquals(t, message+": inputs", expectedPaths, output.Implicits)
+}
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index 14ac021..5bcca04 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -27,8 +27,12 @@
)
func init() {
- android.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
pctx.Import("android/soong/cc/config")
+ registerKernelBuildComponents(android.InitRegistrationContext)
+}
+
+func registerKernelBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
}
type prebuiltKernelModules struct {
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index 433548b..90b9886 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -15,73 +15,29 @@
package kernel
import (
- "io/ioutil"
"os"
- "reflect"
- "strings"
"testing"
"android/soong/android"
"android/soong/cc"
)
-func testKernelModules(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
- bp = bp + `
- cc_binary_host {
- name: "depmod",
- srcs: ["depmod.cpp"],
- stl: "none",
- static_executable: true,
- system_shared_libs: [],
- }
- `
- bp = bp + cc.GatherRequiredDepsForTest(android.Android)
-
- fs["depmod.cpp"] = nil
- cc.GatherRequiredFilesForTest(fs)
-
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
-
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx, config
-}
-
-func ensureListContains(t *testing.T, result []string, expected string) {
- t.Helper()
- if !android.InList(expected, result) {
- t.Errorf("%q is not found in %v", expected, result)
- }
-}
-
-func ensureContains(t *testing.T, result string, expected string) {
- t.Helper()
- if !strings.Contains(result, expected) {
- t.Errorf("%q is not found in %q", expected, result)
- }
-}
-
func TestKernelModulesFilelist(t *testing.T) {
- ctx, _ := testKernelModules(t, `
+ ctx := android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ android.FixtureRegisterWithContext(registerKernelBuildComponents),
+ android.MockFS{
+ "depmod.cpp": nil,
+ "mod1.ko": nil,
+ "mod2.ko": nil,
+ }.AddToFixture(),
+ ).RunTestWithBp(t, `
prebuilt_kernel_modules {
name: "foo",
srcs: ["*.ko"],
kernel_version: "5.10",
}
- `,
- map[string][]byte{
- "mod1.ko": nil,
- "mod2.ko": nil,
- })
+ `)
expected := []string{
"lib/modules/5.10/mod1.ko",
@@ -98,32 +54,9 @@
}
actual = android.SortedUniqueStrings(actual)
expected = android.SortedUniqueStrings(expected)
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected)
- }
-}
-
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_kernel_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
+ android.AssertDeepEquals(t, "foo packaging specs", expected, actual)
}
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index ff548e5..da80a47 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -27,7 +27,11 @@
func init() {
pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config")
- android.RegisterModuleType("linker_config", linkerConfigFactory)
+ registerLinkerConfigBuildComponent(android.InitRegistrationContext)
+}
+
+func registerLinkerConfigBuildComponent(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("linker_config", linkerConfigFactory)
}
type linkerConfigProperties struct {
diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 8eed4b5..939e4bb 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -15,65 +15,29 @@
package linkerconfig
import (
- "android/soong/android"
- "io/ioutil"
"os"
"reflect"
"testing"
+
+ "android/soong/android"
)
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_etc_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
+ os.Exit(m.Run())
}
-func testContext(t *testing.T, bp string) *android.TestContext {
- t.Helper()
-
- fs := map[string][]byte{
- "linker.config.json": nil,
- }
-
- config := android.TestArchConfig(buildDir, nil, bp, fs)
-
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("linker_config", linkerConfigFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- return ctx
-}
+var prepareForLinkerConfigTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ android.FixtureRegisterWithContext(registerLinkerConfigBuildComponent),
+ android.FixtureAddFile("linker.config.json", nil),
+)
func TestBaseLinkerConfig(t *testing.T) {
- ctx := testContext(t, `
- linker_config {
- name: "linker-config-base",
- src: "linker.config.json",
- }
+ result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+ linker_config {
+ name: "linker-config-base",
+ src: "linker.config.json",
+ }
`)
expected := map[string][]string{
@@ -82,13 +46,13 @@
"LOCAL_INSTALLED_MODULE_STEM": {"linker.config.pb"},
}
- p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+ p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
if p.outputFilePath.Base() != "linker.config.pb" {
t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base())
}
- entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
for k, expectedValue := range expected {
if value, ok := entries.EntryMap[k]; ok {
if !reflect.DeepEqual(value, expectedValue) {
@@ -105,18 +69,18 @@
}
func TestUninstallableLinkerConfig(t *testing.T) {
- ctx := testContext(t, `
- linker_config {
- name: "linker-config-base",
- src: "linker.config.json",
- installable: false,
- }
+ result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+ linker_config {
+ name: "linker-config-base",
+ src: "linker.config.json",
+ installable: false,
+ }
`)
expected := []string{"true"}
- p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
- entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
+ p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+ entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
if !reflect.DeepEqual(value, expected) {
t.Errorf("LOCAL_UNINSTALLABLE_MODULE is expected to be true but %s", value)
diff --git a/python/binary.go b/python/binary.go
index 6061ad4..e955492 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -36,8 +36,8 @@
type bazelPythonBinaryAttributes struct {
Main string
- Srcs bazel.LabelList
- Data bazel.LabelList
+ Srcs bazel.LabelListAttribute
+ Data bazel.LabelListAttribute
Python_version string
}
@@ -61,7 +61,7 @@
func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*Module)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
@@ -97,10 +97,13 @@
// do nothing, since python_version defaults to PY3.
}
+ srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+
attrs := &bazelPythonBinaryAttributes{
Main: main,
- Srcs: android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs),
- Data: android.BazelLabelForModuleSrc(ctx, m.properties.Data),
+ Srcs: bazel.MakeLabelListAttribute(srcs),
+ Data: bazel.MakeLabelListAttribute(data),
Python_version: python_version,
}
diff --git a/python/python_test.go b/python/python_test.go
index 6263c8a..f57f504 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -335,13 +335,12 @@
}
t.Run(d.desc, func(t *testing.T) {
- result := emptyFixtureFactory.
- ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
- RunTest(t,
- android.PrepareForTestWithDefaults,
- PrepareForTestWithPythonBuildComponents,
- d.mockFiles.AddToFixture(),
- )
+ result := android.GroupFixturePreparers(
+ android.PrepareForTestWithDefaults,
+ PrepareForTestWithPythonBuildComponents,
+ d.mockFiles.AddToFixture(),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(errorPatterns)).
+ RunTest(t)
if len(result.Errs) > 0 {
return
@@ -376,8 +375,6 @@
android.AssertPathsRelativeToTopEquals(t, "depsSrcsZips", expectedDepsSrcsZips, base.depsSrcsZips)
}
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
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/binary.go b/rust/binary.go
index df48916..dfe8744 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -87,7 +87,7 @@
deps = binary.baseCompiler.compilerDeps(ctx, deps)
if ctx.toolchain().Bionic() {
- deps = bionicDeps(deps, Bool(binary.Properties.Static_executable))
+ deps = bionicDeps(ctx, deps, Bool(binary.Properties.Static_executable))
if Bool(binary.Properties.Static_executable) {
deps.CrtBegin = "crtbegin_static"
} else {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index db69e23..bcc26b8 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -260,7 +260,7 @@
func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
if ctx.toolchain().Bionic() {
- deps = bionicDeps(deps, false)
+ deps = bionicDeps(ctx, deps, false)
}
deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index e24f666..e90564f 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -66,7 +66,7 @@
for _, tc := range clippyLintTests {
t.Run("path="+tc.modulePath, func(t *testing.T) {
- config := android.TestArchConfig(buildDir, nil, bp, fs)
+ config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
ctx := CreateTestContext(config)
ctx.Register()
_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/compiler.go b/rust/compiler.go
index 98ad7ad..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) {
@@ -281,7 +294,7 @@
return deps
}
-func bionicDeps(deps Deps, static bool) Deps {
+func bionicDeps(ctx DepsContext, deps Deps, static bool) Deps {
bionicLibs := []string{}
bionicLibs = append(bionicLibs, "liblog")
bionicLibs = append(bionicLibs, "libc")
@@ -294,9 +307,9 @@
deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
}
- //TODO(b/141331117) libstd requires libgcc on Android
- deps.StaticLibs = append(deps.StaticLibs, "libgcc")
-
+ if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
+ deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
+ }
return deps
}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 2b40727..3ed086f 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -153,7 +153,7 @@
for _, tc := range lintTests {
t.Run("path="+tc.modulePath, func(t *testing.T) {
- config := android.TestArchConfig(buildDir, nil, bp, fs)
+ config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
ctx := CreateTestContext(config)
ctx.Register()
_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
diff --git a/rust/config/global.go b/rust/config/global.go
index 12f4972..9208ddb 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -53,6 +53,7 @@
deviceGlobalRustFlags = []string{
"-C panic=abort",
+ "-Z link-native-libraries=no",
}
deviceGlobalLinkFlags = []string{
@@ -62,7 +63,7 @@
// Override cc's --no-undefined-version to allow rustc's generated alloc functions
"-Wl,--undefined-version",
- "-Bdynamic",
+ "-Wl,-Bdynamic",
"-nostdlib",
"-Wl,--pack-dyn-relocs=android+relr",
"-Wl,--use-android-relr-tags",
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index 9525c38..a769f12 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -112,6 +112,10 @@
return ""
}
+func BuiltinsRuntimeLibrary(t Toolchain) string {
+ return LibclangRuntimeLibrary(t, "builtins")
+}
+
func LibFuzzerRuntimeLibrary(t Toolchain) string {
return LibclangRuntimeLibrary(t, "fuzzer")
}
diff --git a/rust/library.go b/rust/library.go
index 7ff13ec..71fe1f5 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -404,7 +404,7 @@
deps = library.baseCompiler.compilerDeps(ctx, deps)
if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
- deps = bionicDeps(deps, false)
+ deps = bionicDeps(ctx, deps, false)
deps.CrtBegin = "crtbegin_so"
deps.CrtEnd = "crtend_so"
}
diff --git a/rust/project_json.go b/rust/project_json.go
index 8d3d250..c28bc7b 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -49,7 +49,7 @@
RootModule string `json:"root_module"`
Edition string `json:"edition,omitempty"`
Deps []rustProjectDep `json:"deps"`
- Cfgs []string `json:"cfgs"`
+ Cfg []string `json:"cfg"`
Env map[string]string `json:"env"`
}
@@ -230,7 +230,7 @@
RootModule: rootModule,
Edition: comp.edition(),
Deps: make([]rustProjectDep, 0),
- Cfgs: make([]string, 0),
+ Cfg: make([]string, 0),
Env: make(map[string]string),
}
@@ -238,6 +238,10 @@
crate.Env["OUT_DIR"] = comp.CargoOutDir().String()
}
+ for _, feature := range comp.Properties.Features {
+ crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
+ }
+
deps := make(map[string]int)
singleton.mergeDependencies(ctx, rModule, &crate, deps)
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 289bcb8..7af4635 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -18,6 +18,7 @@
"encoding/json"
"io/ioutil"
"path/filepath"
+ "sort"
"strings"
"testing"
@@ -27,15 +28,14 @@
// testProjectJson run the generation of rust-project.json. It returns the raw
// content of the generated file.
func testProjectJson(t *testing.T, bp string) []byte {
- tctx := newTestRustCtx(t, bp)
- tctx.env = map[string]string{"SOONG_GEN_RUST_PROJECT": "1"}
- tctx.generateConfig()
- tctx.parse(t)
+ result := prepareForRustTest.
+ Extend(android.FixtureMergeEnv(map[string]string{"SOONG_GEN_RUST_PROJECT": "1"})).
+ RunTestWithBp(t, bp)
// The JSON file is generated via WriteFileToOutputDir. Therefore, it
// won't appear in the Output of the TestingSingleton. Manually verify
// it exists.
- content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName))
+ content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
if err != nil {
t.Errorf("rust-project.json has not been generated")
}
@@ -116,6 +116,41 @@
validateJsonCrates(t, jsonContent)
}
+func TestProjectJsonFeature(t *testing.T) {
+ bp := `
+ rust_library {
+ name: "liba",
+ srcs: ["a/src/lib.rs"],
+ crate_name: "a",
+ features: ["f1", "f2"]
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ cfgs, ok := crate["cfg"].([]interface{})
+ if !ok {
+ t.Fatalf("Unexpected type for cfgs: %v", crate)
+ }
+ expectedCfgs := []string{"feature=\"f1\"", "feature=\"f2\""}
+ foundCfgs := []string{}
+ for _, cfg := range cfgs {
+ cfg, ok := cfg.(string)
+ if !ok {
+ t.Fatalf("Unexpected type for cfg: %v", cfg)
+ }
+ foundCfgs = append(foundCfgs, cfg)
+ }
+ sort.Strings(foundCfgs)
+ for i, foundCfg := range foundCfgs {
+ if foundCfg != expectedCfgs[i] {
+ t.Errorf("Incorrect features: got %v; want %v", foundCfg, expectedCfgs[i])
+ }
+ }
+ }
+}
+
func TestProjectJsonBinary(t *testing.T) {
bp := `
rust_binary {
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 1ac66f3..f0f5ec0 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -101,7 +101,7 @@
}
// Check that we're including the exported directory from libprotobuf-cpp-full
- if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
+ if w := "-I" + rustDefaultsDir + "libprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
}
diff --git a/rust/rust.go b/rust/rust.go
index 17ff625..f0d0e36 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -273,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
}
@@ -755,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
}
@@ -915,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())
}
}
@@ -1103,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 a0ed534..418bd93 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -15,7 +15,6 @@
package rust
import (
- "io/ioutil"
"os"
"runtime"
"strings"
@@ -24,72 +23,94 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/cc"
+ "android/soong/genrule"
)
-var (
- buildDir string
-)
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_rust_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
+ os.Exit(m.Run())
+}
- return m.Run()
- }
+var prepareForRustTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithPrebuilts,
- os.Exit(run())
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+
+ PrepareForIntegrationTestWithRust,
+)
+
+var rustMockedFiles = android.MockFS{
+ "foo.rs": nil,
+ "foo.c": nil,
+ "src/bar.rs": nil,
+ "src/any.h": nil,
+ "proto.proto": nil,
+ "proto/buf.proto": nil,
+ "buf.proto": nil,
+ "foo.proto": nil,
+ "liby.so": nil,
+ "libz.so": nil,
+ "data.txt": nil,
}
// testRust returns a TestContext in which a basic environment has been setup.
-// This environment contains a few mocked files. See testRustCtx.useMockedFs
-// for the list of these files.
+// This environment contains a few mocked files. See rustMockedFiles for the list of these files.
func testRust(t *testing.T, bp string) *android.TestContext {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- return tctx.parse(t)
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ ).
+ RunTestWithBp(t, bp)
+ return result.TestContext
}
func testRustVndk(t *testing.T, bp string) *android.TestContext {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- tctx.setVndk(t)
- return tctx.parse(t)
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr("current")
+ variables.ProductVndkVersion = StringPtr("current")
+ variables.Platform_vndk_version = StringPtr("VER")
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustCov returns a TestContext in which a basic environment has been
// setup. This environment explicitly enables coverage.
func testRustCov(t *testing.T, bp string) *android.TestContext {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- tctx.enableCoverage(t)
- return tctx.parse(t)
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.ClangCoverage = proptools.BoolPtr(true)
+ variables.Native_coverage = proptools.BoolPtr(true)
+ variables.NativeCoveragePaths = []string{"*"}
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustError ensures that at least one error was raised and its value
// matches the pattern provided. The error can be either in the parsing of the
// Blueprint or when generating the build actions.
func testRustError(t *testing.T, pattern string, bp string) {
- tctx := newTestRustCtx(t, bp)
- tctx.useMockedFs()
- tctx.generateConfig()
- tctx.parseError(t, pattern)
+ skipTestIfOsNotSupported(t)
+ android.GroupFixturePreparers(
+ prepareForRustTest,
+ rustMockedFiles.AddToFixture(),
+ ).
+ ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
+ RunTestWithBp(t, bp)
}
// testRustCtx is used to build a particular test environment. Unless your
@@ -102,99 +123,11 @@
config *android.Config
}
-// newTestRustCtx returns a new testRustCtx for the Blueprint definition argument.
-func newTestRustCtx(t *testing.T, bp string) *testRustCtx {
+func skipTestIfOsNotSupported(t *testing.T) {
// TODO (b/140435149)
if runtime.GOOS != "linux" {
t.Skip("Rust Soong tests can only be run on Linux hosts currently")
}
- return &testRustCtx{bp: bp}
-}
-
-// useMockedFs setup a default mocked filesystem for the test environment.
-func (tctx *testRustCtx) useMockedFs() {
- tctx.fs = map[string][]byte{
- "foo.rs": nil,
- "foo.c": nil,
- "src/bar.rs": nil,
- "src/any.h": nil,
- "proto.proto": nil,
- "proto/buf.proto": nil,
- "buf.proto": nil,
- "foo.proto": nil,
- "liby.so": nil,
- "libz.so": nil,
- "data.txt": nil,
- }
-}
-
-// generateConfig creates the android.Config based on the bp, fs and env
-// attributes of the testRustCtx.
-func (tctx *testRustCtx) generateConfig() {
- tctx.bp = tctx.bp + GatherRequiredDepsForTest()
- tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
- cc.GatherRequiredFilesForTest(tctx.fs)
- config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
- tctx.config = &config
-}
-
-// enableCoverage configures the test to enable coverage.
-func (tctx *testRustCtx) enableCoverage(t *testing.T) {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- tctx.config.TestProductVariables.ClangCoverage = proptools.BoolPtr(true)
- tctx.config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
- tctx.config.TestProductVariables.NativeCoveragePaths = []string{"*"}
-}
-
-func (tctx *testRustCtx) setVndk(t *testing.T) {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- tctx.config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- tctx.config.TestProductVariables.ProductVndkVersion = StringPtr("current")
- tctx.config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-}
-
-// parse validates the configuration and parses the Blueprint file. It returns
-// a TestContext which can be used to retrieve the generated modules via
-// ModuleForTests.
-func (tctx testRustCtx) parse(t *testing.T) *android.TestContext {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- ctx := CreateTestContext(*tctx.config)
- ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(*tctx.config)
- android.FailIfErrored(t, errs)
- return ctx
-}
-
-// parseError parses the Blueprint file and ensure that at least one error
-// matching the provided pattern is observed.
-func (tctx testRustCtx) parseError(t *testing.T, pattern string) {
- if tctx.config == nil {
- t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
- }
- ctx := CreateTestContext(*tctx.config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
-
- _, errs = ctx.PrepareBuildActions(*tctx.config)
- if len(errs) > 0 {
- android.FailIfNoMatchingErrors(t, pattern, errs)
- return
- }
-
- t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
}
// Test that we can extract the link path from a lib path.
@@ -216,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"],
@@ -231,6 +169,7 @@
srcs: ["foo.rs"],
crate_name: "rlib",
static_libs: ["libstatic"],
+ whole_static_libs: ["libwholestatic"],
}
rust_proc_macro {
name: "libpm",
@@ -271,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/scripts/OWNERS b/scripts/OWNERS
index 8198083..2b9c2de 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -3,3 +3,4 @@
per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
+per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 18174a4..30cb937 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -27,7 +27,6 @@
platform-mainline-test-exports
runtime-module-host-exports
runtime-module-sdk
- stats-log-api-gen-exports
statsd-module-sdk
statsd-module-sdk-for-art
tzdata-module-test-exports
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index 6f9edc4..f0658ba 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -66,9 +66,9 @@
if not args.sdk:
raise SystemExit('target sdk version is not set')
if not args.host_contexts:
- raise SystemExit('host context is not set')
+ args.host_contexts = []
if not args.target_contexts:
- raise SystemExit('target context is not set')
+ args.target_contexts = []
print(construct_contexts(args))
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
index f143161..0d3ed5a 100755
--- a/scripts/gen_ndk_usedby_apex.sh
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -33,7 +33,7 @@
do
if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
then
- echo "$line" | sed -r 's/.*UND (.*)@.*/\1/g' >> "$2"
+ echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "$2"
fi
done < "$1"
echo "" >> "$2"
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
index 244ed0d..a9b61a1 100755
--- a/scripts/gen_sorted_bss_symbols.sh
+++ b/scripts/gen_sorted_bss_symbols.sh
@@ -18,11 +18,11 @@
# their sizes.
# Inputs:
# Environment:
-# CROSS_COMPILE: prefix added to nm tools
+# CLANG_BIN: path to the clang bin directory
# Arguments:
# $1: Input ELF file
# $2: Output symbol ordering file
set -o pipefail
-${CROSS_COMPILE}nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
+${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 1343f35..907f239 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -289,7 +289,12 @@
f.write("%s\n" % errmsg)
if args.extract_target_sdk_version:
- print(extract_target_sdk_version(manifest, is_apk))
+ try:
+ print(extract_target_sdk_version(manifest, is_apk))
+ except:
+ # Failed; don't crash, return "any" SDK version. This will result in
+ # dexpreopt not adding any compatibility libraries.
+ print(10000)
if args.output:
# XML output is supposed to be written only when this script is invoked
diff --git a/scripts/strip.sh b/scripts/strip.sh
index 5b7a6da..43e6cbf 100755
--- a/scripts/strip.sh
+++ b/scripts/strip.sh
@@ -87,7 +87,7 @@
comm -13 "${outfile}.dynsyms" "${outfile}.funcsyms" > "${outfile}.keep_symbols"
echo >> "${outfile}.keep_symbols" # Ensure that the keep_symbols file is not empty.
"${CROSS_COMPILE}objcopy" --rename-section .debug_frame=saved_debug_frame "${outfile}.debug" "${outfile}.mini_debuginfo"
- "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
+ "${CROSS_COMPILE}objcopy" -S --remove-section .gdb_index --remove-section .comment --remove-section .rustc --keep-symbols="${outfile}.keep_symbols" "${outfile}.mini_debuginfo"
"${CROSS_COMPILE}objcopy" --rename-section saved_debug_frame=.debug_frame "${outfile}.mini_debuginfo"
"${XZ}" --block-size=64k --threads=0 "${outfile}.mini_debuginfo"
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 6e49c6d..7b034e6 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -23,6 +23,7 @@
"boot_image_sdk_test.go",
"bp_test.go",
"cc_sdk_test.go",
+ "compat_config_sdk_test.go",
"exports_test.go",
"java_sdk_test.go",
"sdk_test.go",
diff --git a/sdk/boot_image_sdk_test.go b/sdk/boot_image_sdk_test.go
index 9805a6a..5a03e34 100644
--- a/sdk/boot_image_sdk_test.go
+++ b/sdk/boot_image_sdk_test.go
@@ -14,20 +14,27 @@
package sdk
-import "testing"
+import (
+ "testing"
+
+ "android/soong/android"
+)
func TestSnapshotWithBootImage(t *testing.T) {
- result := testSdkWithJava(t, `
- sdk {
- name: "mysdk",
- boot_images: ["mybootimage"],
- }
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ android.FixtureWithRootAndroidBp(`
+ sdk {
+ name: "mysdk",
+ boot_images: ["mybootimage"],
+ }
- boot_image {
- name: "mybootimage",
- image_name: "art",
- }
- `)
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ }
+ `),
+ ).RunTest(t)
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
@@ -60,3 +67,39 @@
`),
checkAllCopyRules(""))
}
+
+// Test that boot_image works with sdk.
+func TestBasicSdkWithBootImage(t *testing.T) {
+ android.GroupFixturePreparers(
+ prepareForSdkTestWithApex,
+ prepareForSdkTestWithJava,
+ android.FixtureWithRootAndroidBp(`
+ sdk {
+ name: "mysdk",
+ boot_images: ["mybootimage"],
+ }
+
+ boot_image {
+ name: "mybootimage",
+ image_name: "art",
+ apex_available: ["myapex"],
+ }
+
+ sdk_snapshot {
+ name: "mysdk@1",
+ boot_images: ["mybootimage_mysdk_1"],
+ }
+
+ prebuilt_boot_image {
+ name: "mybootimage_mysdk_1",
+ sdk_member_name: "mybootimage",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: [
+ "myapex",
+ ],
+ image_name: "art",
+ }
+ `),
+ ).RunTest(t)
+}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9db816d..b19fcc5 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),
)
}
@@ -808,7 +814,8 @@
}
func TestSnapshotWithSingleHostOsType(t *testing.T) {
- result := sdkFixtureFactory.Extend(
+ result := android.GroupFixturePreparers(
+ prepareForSdkTest,
ccTestFs.AddToFixture(),
cc.PrepareForTestOnLinuxBionic,
android.FixtureModifyConfig(func(config android.Config) {
@@ -1815,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`)),
)
}
@@ -2666,6 +2676,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.
@@ -2690,6 +2705,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
new file mode 100644
index 0000000..00073c2
--- /dev/null
+++ b/sdk/compat_config_sdk_test.go
@@ -0,0 +1,91 @@
+// 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 sdk
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+)
+
+func TestSnapshotWithCompatConfig(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForSdkTestWithJava,
+ java.PrepareForTestWithPlatformCompatConfig,
+ ).RunTestWithBp(t, `
+ sdk {
+ name: "mysdk",
+ compat_configs: ["myconfig"],
+ }
+
+ platform_compat_config {
+ name: "myconfig",
+ }
+ `)
+
+ CheckSnapshot(t, result, "mysdk", "",
+ checkVersionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+ name: "mysdk_myconfig@current",
+ sdk_member_name: "myconfig",
+ visibility: ["//visibility:public"],
+ metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+
+sdk_snapshot {
+ name: "mysdk@current",
+ visibility: ["//visibility:public"],
+ compat_configs: ["mysdk_myconfig@current"],
+}
+`),
+ checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_platform_compat_config {
+ name: "myconfig",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ metadata: "compat_configs/myconfig/myconfig_meta.xml",
+}
+`),
+ 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 6ca8512..b60fb18 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -445,20 +445,26 @@
}
}
+// An interface that encapsulates all the functionality needed to manage the sdk dependencies.
+//
+// It is a mixture of apex and sdk module functionality.
+type sdkAndApexModule interface {
+ android.Module
+ android.DepIsInSameApex
+ android.RequiredSdks
+}
+
// Step 4: transitively ripple down the SDK requirements from the root modules like APEX to its
// descendants
func sdkDepsMutator(mctx android.TopDownMutatorContext) {
- if parent, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); ok {
+ if parent, ok := mctx.Module().(sdkAndApexModule); ok {
// Module types for Mainline modules (e.g. APEX) are expected to implement RequiredSdks()
// by reading its own properties like `uses_sdks`.
requiredSdks := parent.RequiredSdks()
if len(requiredSdks) > 0 {
mctx.VisitDirectDeps(func(m android.Module) {
// Only propagate required sdks from the apex onto its contents.
- if dep, ok := m.(android.SdkAware); ok && parent.DepIsInSameApex(mctx, dep) {
+ if dep, ok := m.(android.SdkAware); ok && android.IsDepInSameApex(mctx, parent, dep) {
dep.BuildWithSdks(requiredSdks)
}
})
@@ -480,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 {
@@ -497,10 +512,7 @@
// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(interface {
- android.DepIsInSameApex
- android.RequiredSdks
- }); ok {
+ if m, ok := mctx.Module().(sdkAndApexModule); ok {
requiredSdks := m.RequiredSdks()
if len(requiredSdks) == 0 {
return
@@ -519,9 +531,18 @@
return
}
- // If the dep is outside of the APEX, but is not in any of the
- // required SDKs, we know that the dep is a violation.
+ // If the dep is outside of the APEX, but is not in any of the required SDKs, we know that the
+ // dep is a violation.
if sa, ok := dep.(android.SdkAware); ok {
+ // It is not an error if a dependency that is excluded from the apex due to the tag is not
+ // in one of the required SDKs. That is because all of the existing tags that implement it
+ // do not depend on modules which can or should belong to an sdk_snapshot.
+ if _, ok := tag.(android.ExcludeFromApexContentsTag); ok {
+ // The tag defines a dependency that never requires the child module to be part of the
+ // same apex.
+ return
+ }
+
if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
mctx.ModuleErrorf("depends on %q (in SDK %q) that isn't part of the required SDKs: %v",
sa.Name(), sa.ContainingSdk(), requiredSdks)
diff --git a/sdk/testing.go b/sdk/testing.go
index 6df402c..9465e13 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -27,14 +27,9 @@
"android/soong/java"
)
-var sdkFixtureFactory = android.NewFixtureFactory(
- nil,
+// Prepare for running an sdk test with an apex.
+var prepareForSdkTestWithApex = android.GroupFixturePreparers(
apex.PrepareForTestWithApexBuildComponents,
- cc.PrepareForTestWithCcDefaultModules,
- genrule.PrepareForTestWithGenRuleBuildComponents,
- java.PrepareForTestWithJavaBuildComponents,
- PrepareForTestWithSdkBuildComponents,
-
android.FixtureAddTextFile("sdk/tests/Android.bp", `
apex_key {
name: "myapex.key",
@@ -49,16 +44,33 @@
`),
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,
}),
+)
+
+// Legacy preparer used for running tests within the sdk package.
+//
+// This includes everything that was needed to run any test in the sdk package prior to the
+// introduction of the test fixtures. Tests that are being converted to use fixtures directly
+// rather than through the testSdkError() and testSdkWithFs() methods should avoid using this and
+// instead should use the various preparers directly using android.GroupFixturePreparers(...) to
+// group them when necessary.
+//
+// deprecated
+var prepareForSdkTest = android.GroupFixturePreparers(
+ cc.PrepareForTestWithCcDefaultModules,
+ genrule.PrepareForTestWithGenRuleBuildComponents,
+ java.PrepareForTestWithJavaBuildComponents,
+ PrepareForTestWithSdkBuildComponents,
+
+ prepareForSdkTestWithApex,
cc.PrepareForTestOnWindows,
android.FixtureModifyConfig(func(config android.Config) {
@@ -68,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(
@@ -77,12 +95,15 @@
func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
t.Helper()
- return sdkFixtureFactory.RunTest(t, fs.AddToFixture(), android.FixtureWithRootAndroidBp(bp))
+ return android.GroupFixturePreparers(
+ prepareForSdkTest,
+ fs.AddToFixture(),
+ ).RunTestWithBp(t, bp)
}
func testSdkError(t *testing.T, pattern, bp string) {
t.Helper()
- sdkFixtureFactory.
+ prepareForSdkTest.
ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
RunTestWithBp(t, bp)
}
@@ -113,6 +134,7 @@
androidBpContents: sdk.GetAndroidBpContentsForTests(),
androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
androidVersionedBpContents: sdk.GetVersionedAndroidBpContentsForTests(),
+ snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
}
buildParams := sdk.BuildParamsForTests()
@@ -171,6 +193,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.
@@ -201,13 +241,60 @@
// Populate a mock filesystem with the files that would have been copied by
// the rules.
- fs := make(map[string][]byte)
+ fs := android.MockFS{}
for _, dest := range snapshotBuildInfo.snapshotContents {
- fs[dest] = nil
+ fs[filepath.Join(snapshotSubDir, dest)] = nil
+ }
+ fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
+
+ // The preparers from the original source fixture.
+ sourcePreparers := result.Preparer()
+
+ // 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)
+ }
}
- // Process the generated bp file to make sure it is valid.
- testSdkWithFs(t, snapshotBuildInfo.androidBpContents, fs)
+ t.Run("snapshot without source", func(t *testing.T) {
+ // Remove the source Android.bp file to make sure it works without.
+ removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
+ delete(fs, "Android.bp")
+ })
+
+ runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
+ })
+
+ 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)
+ })
}
type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
@@ -280,6 +367,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.
//
@@ -323,4 +450,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..6623381 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -485,7 +485,7 @@
}
type bazelShBinaryAttributes struct {
- Srcs bazel.LabelList
+ Srcs bazel.LabelListAttribute
// Bazel also supports the attributes below, but (so far) these are not required for Bionic
// deps
// data
@@ -521,11 +521,12 @@
func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
m, ok := ctx.Module().(*ShBinary)
- if !ok || !m.ConvertWithBp2build() {
+ if !ok || !m.ConvertWithBp2build(ctx) {
return
}
- srcs := android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src})
+ srcs := bazel.MakeLabelListAttribute(
+ android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
attrs := &bazelShBinaryAttributes{
Srcs: srcs,
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 5887b56..9e7e594 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -13,8 +13,7 @@
os.Exit(m.Run())
}
-var shFixtureFactory = android.NewFixtureFactory(
- nil,
+var prepareForShTest = android.GroupFixturePreparers(
cc.PrepareForTestWithCcBuildComponents,
PrepareForTestWithShBuildComponents,
android.FixtureMergeMockFs(android.MockFS{
@@ -24,19 +23,19 @@
}),
)
-// testShBinary runs tests using the shFixtureFactory
+// testShBinary runs tests using the prepareForShTest
//
-// Do not add any new usages of this, instead use the shFixtureFactory directly as it makes it much
+// Do not add any new usages of this, instead use the prepareForShTest directly as it makes it much
// easier to customize the test behavior.
//
// If it is necessary to customize the behavior of an existing test that uses this then please first
-// convert the test to using shFixtureFactory first and then in a following change add the
+// convert the test to using prepareForShTest first and then in a following change add the
// appropriate fixture preparers. Keeping the conversion change separate makes it easy to verify
// that it did not change the test behavior unexpectedly.
//
// deprecated
func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
- result := shFixtureFactory.RunTestWithBp(t, bp)
+ result := prepareForShTest.RunTestWithBp(t, bp)
return result.TestContext, result.Config
}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index cb1e362..e9d9051 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -30,8 +30,6 @@
os.Exit(m.Run())
}
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
func test(t *testing.T, bp string) *android.TestResult {
t.Helper()
@@ -124,7 +122,7 @@
"com/android2/OdmProperties.sysprop": nil,
}
- result := emptyFixtureFactory.RunTest(t,
+ result := android.GroupFixturePreparers(
cc.PrepareForTestWithCcDefaultModules,
java.PrepareForTestWithJavaDefaultModules,
PrepareForTestWithSyspropBuildComponents,
@@ -135,7 +133,7 @@
}),
mockFS.AddToFixture(),
android.FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
return result
}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 1fabd92..45ccd04 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -112,9 +112,15 @@
func stopRBE(ctx Context, config Config) {
cmd := Command(ctx, config, "stopRBE bootstrap", rbeCommand(ctx, config, bootstrapCmd), "-shutdown")
- if output, err := cmd.CombinedOutput(); err != nil {
+ output, err := cmd.CombinedOutput()
+ if err != nil {
ctx.Fatalf("rbe bootstrap with shutdown failed with: %v\n%s\n", err, output)
}
+
+ if len(output) > 0 {
+ fmt.Fprintln(ctx.Writer, "")
+ fmt.Fprintln(ctx.Writer, fmt.Sprintf("%s", output))
+ }
}
// DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
diff --git a/xml/xml_test.go b/xml/xml_test.go
index 83ae51c..a59a293 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -26,8 +26,6 @@
os.Exit(m.Run())
}
-var emptyFixtureFactory = android.NewFixtureFactory(nil)
-
func testXml(t *testing.T, bp string) *android.TestResult {
fs := android.MockFS{
"foo.xml": nil,
@@ -37,13 +35,13 @@
"baz.xml": nil,
}
- return emptyFixtureFactory.RunTest(t,
+ return android.GroupFixturePreparers(
android.PrepareForTestWithArchMutator,
etc.PrepareForTestWithPrebuiltEtc,
PreparerForTestWithXmlBuildComponents,
fs.AddToFixture(),
android.FixtureWithRootAndroidBp(bp),
- )
+ ).RunTest(t)
}
// Minimal test
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;"),