Merge changes from topic "package"
* changes:
add android_filesystem
Introduce PackagingBase
diff --git a/android/Android.bp b/android/Android.bp
index e7eff2e..26605a7 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -32,6 +32,7 @@
"mutator.go",
"namespace.go",
"neverallow.go",
+ "ninja_deps.go",
"notices.go",
"onceper.go",
"override_module.go",
@@ -74,6 +75,7 @@
"mutator_test.go",
"namespace_test.go",
"neverallow_test.go",
+ "ninja_deps_test.go",
"onceper_test.go",
"package_test.go",
"packaging_test.go",
diff --git a/android/arch.go b/android/arch.go
index 98ff07a..afb9c7f 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -757,7 +757,7 @@
for _, os := range OsTypeList {
for _, t := range mctx.Config().Targets[os] {
- if base.supportsTarget(t, mctx.Config()) {
+ if base.supportsTarget(t) {
moduleOSList = append(moduleOSList, os)
break
}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index c87a945..7d8d12f 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -192,9 +192,9 @@
func (context *bazelContext) issueBazelCommand(command string, labels []string,
extraFlags ...string) (string, error) {
- cmdFlags := []string{"--bazelrc=build/bazel/common.bazelrc",
- "--output_base=" + context.outputBase, command}
+ cmdFlags := []string{"--output_base=" + context.outputBase, command}
cmdFlags = append(cmdFlags, labels...)
+ cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
cmdFlags = append(cmdFlags, extraFlags...)
bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
@@ -211,6 +211,21 @@
}
}
+// Returns the string contents of a workspace file that should be output
+// adjacent to the main bzl file and build file.
+// This workspace file allows, via local_repository rule, sourcetree-level
+// BUILD targets to be referenced via @sourceroot.
+func (context *bazelContext) workspaceFileContents() []byte {
+ formatString := `
+# This file is generated by soong_build. Do not edit.
+local_repository(
+ name = "sourceroot",
+ path = "%s",
+)
+`
+ return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+}
+
func (context *bazelContext) mainBzlFileContents() []byte {
contents := `
# This file is generated by soong_build. Do not edit.
@@ -225,6 +240,18 @@
return []byte(contents)
}
+// Returns a "canonicalized" corresponding to the given sourcetree-level label.
+// This abstraction is required because a sourcetree label such as //foo/bar:baz
+// must be referenced via the local repository prefix, such as
+// @sourceroot//foo/bar:baz.
+func canonicalizeLabel(label string) string {
+ if strings.HasPrefix(label, "//") {
+ return "@sourceroot" + label
+ } else {
+ return "@sourceroot//" + label
+ }
+}
+
func (context *bazelContext) mainBuildFileContents() []byte {
formatString := `
# This file is generated by soong_build. Do not edit.
@@ -236,7 +263,7 @@
`
var buildRootDeps []string = nil
for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", val.label))
+ buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
}
buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
@@ -262,13 +289,19 @@
// TODO(cparsons): Sort by request type instead of assuming all requests
// are of GetAllFiles type.
for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", val.label))
+ buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
}
buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
return []byte(fmt.Sprintf(formatString, buildRootDepsString))
}
+// Returns a workspace-relative path containing build-related metadata required
+// for interfacing with Bazel. Example: out/soong/bazel.
+func (context *bazelContext) intermediatesDir() string {
+ return filepath.Join(context.buildDir, "bazel")
+}
+
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
func (context *bazelContext) InvokeBazel() error {
@@ -276,26 +309,38 @@
var cqueryOutput string
var err error
+
+ err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+ if err != nil {
+ return err
+ }
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.buildDir, "main.bzl")),
+ absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
context.mainBzlFileContents(), 0666)
if err != nil {
return err
}
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.buildDir, "BUILD.bazel")),
+ absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
context.mainBuildFileContents(), 0666)
if err != nil {
return err
}
- cquery_file_relpath := filepath.Join(context.buildDir, "buildroot.cquery")
+ cquery_file_relpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
err = ioutil.WriteFile(
absolutePath(cquery_file_relpath),
context.cqueryStarlarkFileContents(), 0666)
if err != nil {
return err
}
- buildroot_label := fmt.Sprintf("//%s:buildroot", context.buildDir)
+ workspace_file_relpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
+ err = ioutil.WriteFile(
+ absolutePath(workspace_file_relpath),
+ context.workspaceFileContents(), 0666)
+ if err != nil {
+ return err
+ }
+ buildroot_label := "//:buildroot"
cqueryOutput, err = context.issueBazelCommand("cquery",
[]string{fmt.Sprintf("deps(%s)", buildroot_label)},
"--output=starlark",
@@ -314,7 +359,7 @@
}
for val, _ := range context.requests {
- if cqueryResult, ok := cqueryResults[val.label]; ok {
+ if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
context.results[val] = string(cqueryResult)
} else {
return fmt.Errorf("missing result for bazel target %s", val.label)
diff --git a/android/config.go b/android/config.go
index e87a4ac..d833c5c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -126,6 +126,8 @@
// in tests when a path doesn't exist.
testAllowNonExistentPaths bool
+ ninjaFileDepsSet sync.Map
+
OncePer
}
diff --git a/android/makevars.go b/android/makevars.go
index 3ca7792..f784395 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -139,15 +139,24 @@
MakeVars(ctx MakeVarsContext)
}
-// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
-// MakeVarsProviders to run.
-func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
- singletonMakeVarsProviders = append(singletonMakeVarsProviders,
- makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
+var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
+
+// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
+// the list of MakeVarsProviders to run.
+func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
+ // Singletons are registered on the Context and may be different between different Contexts,
+ // for example when running multiple tests. Store the SingletonMakeVarsProviders in the
+ // Config so they are attached to the Context.
+ singletonMakeVarsProviders := config.Once(singletonMakeVarsProvidersKey, func() interface{} {
+ return &[]makeVarsProvider{}
+ }).(*[]makeVarsProvider)
+
+ *singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
+ makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
}
-// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
-func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
+// singletonMakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
+func singletonMakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
}
@@ -175,9 +184,6 @@
// Collection of makevars providers that are registered in init() methods.
var makeVarsInitProviders []makeVarsProvider
-// Collection of singleton makevars providers that are not registered as part of init() methods.
-var singletonMakeVarsProviders []makeVarsProvider
-
type makeVarsContext struct {
SingletonContext
config Config
@@ -224,7 +230,11 @@
var vars []makeVarsVariable
var dists []dist
var phonies []phony
- for _, provider := range append(makeVarsInitProviders) {
+
+ providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
+ providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
+
+ for _, provider := range providers {
mctx := &makeVarsContext{
SingletonContext: ctx,
pctx: provider.pctx,
@@ -237,25 +247,6 @@
dists = append(dists, mctx.dists...)
}
- for _, provider := range append(singletonMakeVarsProviders) {
- mctx := &makeVarsContext{
- SingletonContext: ctx,
- pctx: provider.pctx,
- }
-
- provider.call(mctx)
-
- vars = append(vars, mctx.vars...)
- phonies = append(phonies, mctx.phonies...)
- dists = append(dists, mctx.dists...)
- }
-
- // Clear singleton makevars providers after use. Since these are in-memory
- // singletons, this ensures state is reset if the build tree is processed
- // multiple times.
- // TODO(cparsons): Clean up makeVarsProviders to be part of the context.
- singletonMakeVarsProviders = nil
-
ctx.VisitAllModules(func(m Module) {
if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
mctx := &makeVarsContext{
diff --git a/android/module.go b/android/module.go
index 3cef3f5..eeb4fae 100644
--- a/android/module.go
+++ b/android/module.go
@@ -766,27 +766,32 @@
type HostOrDeviceSupported int
const (
- _ HostOrDeviceSupported = iota
+ hostSupported = 1 << iota
+ hostCrossSupported
+ deviceSupported
+ hostDefault
+ deviceDefault
// Host and HostCross are built by default. Device is not supported.
- HostSupported
+ HostSupported = hostSupported | hostCrossSupported | hostDefault
// Host is built by default. HostCross and Device are not supported.
- HostSupportedNoCross
+ HostSupportedNoCross = hostSupported | hostDefault
// Device is built by default. Host and HostCross are not supported.
- DeviceSupported
+ DeviceSupported = deviceSupported | deviceDefault
// Device is built by default. Host and HostCross are supported.
- HostAndDeviceSupported
+ HostAndDeviceSupported = hostSupported | hostCrossSupported | deviceSupported | deviceDefault
// Host, HostCross, and Device are built by default.
- HostAndDeviceDefault
+ HostAndDeviceDefault = hostSupported | hostCrossSupported | hostDefault |
+ deviceSupported | deviceDefault
// Nothing is supported. This is not exposed to the user, but used to mark a
// host only module as unsupported when the module type is not supported on
// the host OS. E.g. benchmarks are supported on Linux but not Darwin.
- NeitherHostNorDeviceSupported
+ NeitherHostNorDeviceSupported = 0
)
type moduleKind int
@@ -848,8 +853,7 @@
base.commonProperties.ArchSpecific = true
base.commonProperties.UseTargetVariants = true
- switch hod {
- case HostAndDeviceSupported, HostAndDeviceDefault:
+ if hod&hostSupported != 0 && hod&deviceSupported != 0 {
m.AddProperties(&base.hostAndDeviceProperties)
}
@@ -1100,43 +1104,54 @@
return m.commonProperties.CommonOSVariant
}
-func (m *ModuleBase) supportsTarget(target Target, config Config) bool {
- switch m.commonProperties.HostOrDeviceSupported {
- case HostSupported:
- return target.Os.Class == Host
- case HostSupportedNoCross:
- return target.Os.Class == Host && !target.HostCross
- case DeviceSupported:
- return target.Os.Class == Device
- case HostAndDeviceSupported, HostAndDeviceDefault:
- supported := false
- if Bool(m.hostAndDeviceProperties.Host_supported) ||
- (m.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
- m.hostAndDeviceProperties.Host_supported == nil) {
- supported = supported || target.Os.Class == Host
+// supportsTarget returns true if the given Target is supported by the current module.
+func (m *ModuleBase) supportsTarget(target Target) bool {
+ switch target.Os.Class {
+ case Host:
+ if target.HostCross {
+ return m.HostCrossSupported()
+ } else {
+ return m.HostSupported()
}
- if m.hostAndDeviceProperties.Device_supported == nil ||
- *m.hostAndDeviceProperties.Device_supported {
- supported = supported || target.Os.Class == Device
- }
- return supported
+ case Device:
+ return m.DeviceSupported()
default:
return false
}
}
+// DeviceSupported returns true if the current module is supported and enabled for device targets,
+// i.e. the factory method set the HostOrDeviceSupported value to include device support and
+// the device support is enabled by default or enabled by the device_supported property.
func (m *ModuleBase) DeviceSupported() bool {
- return m.commonProperties.HostOrDeviceSupported == DeviceSupported ||
- m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
- (m.hostAndDeviceProperties.Device_supported == nil ||
- *m.hostAndDeviceProperties.Device_supported)
+ hod := m.commonProperties.HostOrDeviceSupported
+ // deviceEnabled is true if the device_supported property is true or the HostOrDeviceSupported
+ // value has the deviceDefault bit set.
+ deviceEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Device_supported, hod&deviceDefault != 0)
+ return hod&deviceSupported != 0 && deviceEnabled
}
+// HostSupported returns true if the current module is supported and enabled for host targets,
+// i.e. the factory method set the HostOrDeviceSupported value to include host support and
+// the host support is enabled by default or enabled by the host_supported property.
func (m *ModuleBase) HostSupported() bool {
- return m.commonProperties.HostOrDeviceSupported == HostSupported ||
- m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
- (m.hostAndDeviceProperties.Host_supported != nil &&
- *m.hostAndDeviceProperties.Host_supported)
+ hod := m.commonProperties.HostOrDeviceSupported
+ // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported
+ // value has the hostDefault bit set.
+ hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0)
+ return hod&hostSupported != 0 && hostEnabled
+}
+
+// HostCrossSupported returns true if the current module is supported and enabled for host cross
+// targets, i.e. the factory method set the HostOrDeviceSupported value to include host cross
+// support and the host cross support is enabled by default or enabled by the
+// host_supported property.
+func (m *ModuleBase) HostCrossSupported() bool {
+ hod := m.commonProperties.HostOrDeviceSupported
+ // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported
+ // value has the hostDefault bit set.
+ hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0)
+ return hod&hostCrossSupported != 0 && hostEnabled
}
func (m *ModuleBase) Platform() bool {
diff --git a/android/ninja_deps.go b/android/ninja_deps.go
new file mode 100644
index 0000000..2f442d5
--- /dev/null
+++ b/android/ninja_deps.go
@@ -0,0 +1,43 @@
+// Copyright 2020 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 "sort"
+
+func (c *config) addNinjaFileDeps(deps ...string) {
+ for _, dep := range deps {
+ c.ninjaFileDepsSet.Store(dep, true)
+ }
+}
+
+func (c *config) ninjaFileDeps() []string {
+ var deps []string
+ c.ninjaFileDepsSet.Range(func(key, value interface{}) bool {
+ deps = append(deps, key.(string))
+ return true
+ })
+ sort.Strings(deps)
+ return deps
+}
+
+func ninjaDepsSingletonFactory() Singleton {
+ return &ninjaDepsSingleton{}
+}
+
+type ninjaDepsSingleton struct{}
+
+func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
+ ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...)
+}
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
new file mode 100644
index 0000000..d3775ed
--- /dev/null
+++ b/android/ninja_deps_test.go
@@ -0,0 +1,75 @@
+// Copyright 2020 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 init() {
+ // This variable uses ExistentPathForSource on a PackageVarContext, which is a PathContext
+ // that is not a PathGlobContext. That requires the deps to be stored in the Config.
+ pctx.VariableFunc("test_ninja_deps_variable", func(ctx PackageVarContext) string {
+ // Using ExistentPathForSource to look for a file that does not exist in a directory that
+ // does exist (test_ninja_deps) from a PackageVarContext adds a dependency from build.ninja
+ // to the directory.
+ if ExistentPathForSource(ctx, "test_ninja_deps/does_not_exist").Valid() {
+ return "true"
+ } else {
+ return "false"
+ }
+ })
+}
+
+func testNinjaDepsSingletonFactory() Singleton {
+ return testNinjaDepsSingleton{}
+}
+
+type testNinjaDepsSingleton struct{}
+
+func (testNinjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
+ // Reference the test_ninja_deps_variable in a build statement so Blueprint is forced to
+ // evaluate it.
+ ctx.Build(pctx, BuildParams{
+ Rule: Cp,
+ Input: PathForTesting("foo"),
+ Output: PathForOutput(ctx, "test_ninja_deps_out"),
+ Args: map[string]string{
+ "cpFlags": "${test_ninja_deps_variable}",
+ },
+ })
+}
+
+func TestNinjaDeps(t *testing.T) {
+ fs := map[string][]byte{
+ "test_ninja_deps/exists": nil,
+ }
+ config := TestConfig(buildDir, nil, "", fs)
+
+ ctx := NewTestContext(config)
+ ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
+ ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ ninjaDeps, errs := ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ // Verify that the ninja file has a dependency on the test_ninja_deps directory.
+ if g, w := ninjaDeps, "test_ninja_deps"; !InList(w, g) {
+ t.Errorf("expected %q in %q", w, g)
+ }
+}
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 0de356e..6d0fcb3 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -56,7 +56,7 @@
e.errors = append(e.errors, fmt.Errorf(format, args...))
}
func (e *configErrorWrapper) AddNinjaFileDeps(deps ...string) {
- e.pctx.AddNinjaFileDeps(deps...)
+ e.config.addNinjaFileDeps(deps...)
}
type PackageVarContext interface {
diff --git a/android/register.go b/android/register.go
index bd824c9..08e47b3 100644
--- a/android/register.go
+++ b/android/register.go
@@ -29,7 +29,7 @@
type singleton struct {
name string
- factory blueprint.SingletonFactory
+ factory SingletonFactory
}
var singletons []singleton
@@ -57,11 +57,11 @@
// SingletonFactoryAdaptor wraps a SingletonFactory into a blueprint.SingletonFactory by converting
// a Singleton into a blueprint.Singleton
-func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
+func SingletonFactoryAdaptor(ctx *Context, factory SingletonFactory) blueprint.SingletonFactory {
return func() blueprint.Singleton {
singleton := factory()
if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
- registerSingletonMakeVarsProvider(makevars)
+ registerSingletonMakeVarsProvider(ctx.config, makevars)
}
return &singletonAdaptor{Singleton: singleton}
}
@@ -72,11 +72,11 @@
}
func RegisterSingletonType(name string, factory SingletonFactory) {
- singletons = append(singletons, singleton{name, SingletonFactoryAdaptor(factory)})
+ singletons = append(singletons, singleton{name, factory})
}
func RegisterPreSingletonType(name string, factory SingletonFactory) {
- preSingletons = append(preSingletons, singleton{name, SingletonFactoryAdaptor(factory)})
+ preSingletons = append(preSingletons, singleton{name, factory})
}
type Context struct {
@@ -92,7 +92,7 @@
func (ctx *Context) Register() {
for _, t := range preSingletons {
- ctx.RegisterPreSingletonType(t.name, t.factory)
+ ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
for _, t := range moduleTypes {
@@ -100,21 +100,23 @@
}
for _, t := range singletons {
- ctx.RegisterSingletonType(t.name, t.factory)
+ ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
- ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(BazelSingleton))
+ ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton))
// Register phony just before makevars so it can write out its phony rules as Make rules
- ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(phonySingletonFactory))
+ ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory))
// Register makevars after other singletons so they can export values through makevars
- ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
+ ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc))
- // Register env last so that it can track all used environment variables
- ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
+ // Register env and ninjadeps last so that they can track all used environment variables and
+ // Ninja file dependencies stored in the config.
+ ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton))
+ ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory))
}
func ModuleTypeFactories() map[string]ModuleFactory {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 8dc9d6a..86418b2 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -15,7 +15,9 @@
package android
import (
+ "crypto/sha256"
"fmt"
+ "path/filepath"
"sort"
"strings"
@@ -25,6 +27,8 @@
"android/soong/shared"
)
+const sboxOutDir = "__SBOX_OUT_DIR__"
+
// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
// graph.
type RuleBuilder struct {
@@ -133,8 +137,8 @@
// race with any call to Build.
func (r *RuleBuilder) Command() *RuleBuilderCommand {
command := &RuleBuilderCommand{
- sbox: r.sbox,
- sboxOutDir: r.sboxOutDir,
+ sbox: r.sbox,
+ outDir: r.sboxOutDir,
}
r.commands = append(r.commands, command)
return command
@@ -163,7 +167,7 @@
}
// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
-// input paths, such as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or
+// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
func (r *RuleBuilder) Inputs() Paths {
@@ -362,7 +366,7 @@
return commands
}
-// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
+// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
// RuleBuilder.Command.
func (r *RuleBuilder) NinjaEscapedCommands() []string {
var commands []string
@@ -427,6 +431,7 @@
tools := r.Tools()
commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
+ inputs := r.Inputs()
if len(commands) == 0 {
return
@@ -440,7 +445,7 @@
if r.sbox {
sboxOutputs := make([]string, len(outputs))
for i, output := range outputs {
- sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
+ sboxOutputs[i] = filepath.Join(sboxOutDir, Rel(ctx, r.sboxOutDir.String(), output.String()))
}
commandString = proptools.ShellEscape(commandString)
@@ -458,10 +463,19 @@
sboxCmd.Flag("--depfile-out").Text(depFile.String())
}
+ // Add a hash of the list of input files to the xbox command line so that ninja reruns
+ // it when the list of input files changes.
+ sboxCmd.FlagWithArg("--input-hash ", hashSrcFiles(inputs))
+
sboxCmd.Flags(sboxOutputs)
commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
+ } else {
+ // If not using sbox the rule will run the command directly, put the hash of the
+ // list of input files in a comment at the end of the command line to ensure ninja
+ // reruns the rule when the list of input files changes.
+ commandString += " # hash of input list: " + hashSrcFiles(inputs)
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
@@ -499,7 +513,7 @@
Pool: pool,
}),
Inputs: rspFileInputs,
- Implicits: r.Inputs(),
+ Implicits: inputs,
Output: output,
ImplicitOutputs: implicitOutputs,
SymlinkOutputs: r.SymlinkOutputs(),
@@ -527,14 +541,16 @@
// spans [start,end) of the command that should not be ninja escaped
unescapedSpans [][2]int
- sbox bool
- sboxOutDir WritablePath
+ sbox bool
+ // outDir is the directory that will contain the output files of the rules. sbox will copy
+ // the output files from the sandbox directory to this directory when it finishes.
+ outDir WritablePath
}
func (c *RuleBuilderCommand) addInput(path Path) string {
if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
- return "__SBOX_OUT_DIR__/" + rel
+ if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ return filepath.Join(sboxOutDir, rel)
}
}
c.inputs = append(c.inputs, path)
@@ -543,8 +559,8 @@
func (c *RuleBuilderCommand) addImplicit(path Path) string {
if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
- return "__SBOX_OUT_DIR__/" + rel
+ if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ return filepath.Join(sboxOutDir, rel)
}
}
c.implicits = append(c.implicits, path)
@@ -555,15 +571,22 @@
c.orderOnlys = append(c.orderOnlys, path)
}
-func (c *RuleBuilderCommand) outputStr(path Path) string {
+func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
if c.sbox {
- // Errors will be handled in RuleBuilder.Build where we have a context to report them
- rel, _, _ := maybeRelErr(c.sboxOutDir.String(), path.String())
- return "__SBOX_OUT_DIR__/" + rel
+ return SboxPathForOutput(path, c.outDir)
}
return path.String()
}
+// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
+// and returns the corresponding path for the output in the sbox sandbox. This can be used
+// on the RuleBuilder command line to reference the output.
+func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ rel, _, _ := maybeRelErr(outDir.String(), path.String())
+ return filepath.Join(sboxOutDir, rel)
+}
+
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@@ -727,7 +750,7 @@
if !c.sbox {
panic("OutputDir only valid with Sbox")
}
- return c.Text("__SBOX_OUT_DIR__")
+ return c.Text(sboxOutDir)
}
// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
@@ -906,3 +929,12 @@
}
return s
}
+
+// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
+// or the sbox textproto manifest change even if the input files are not listed on the command line.
+func hashSrcFiles(srcFiles Paths) string {
+ h := sha256.New()
+ srcFileList := strings.Join(srcFiles.Strings(), "\n")
+ h.Write([]byte(srcFileList))
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index ca6359d..c1d5521 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -18,6 +18,7 @@
"fmt"
"path/filepath"
"reflect"
+ "regexp"
"strings"
"testing"
@@ -441,7 +442,7 @@
type testRuleBuilderModule struct {
ModuleBase
properties struct {
- Src string
+ Srcs []string
Restat bool
Sbox bool
@@ -449,7 +450,7 @@
}
func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- in := PathForSource(ctx, t.properties.Src)
+ in := PathsForSource(ctx, t.properties.Srcs)
out := PathForModuleOut(ctx, ctx.ModuleName())
outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
outDir := PathForModuleOut(ctx)
@@ -468,17 +469,17 @@
out := PathForOutput(ctx, "baz")
outDep := PathForOutput(ctx, "baz.d")
outDir := PathForOutput(ctx)
- testRuleBuilder_Build(ctx, in, out, outDep, outDir, true, false)
+ testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, true, false)
}
-func testRuleBuilder_Build(ctx BuilderContext, in Path, out, outDep, outDir WritablePath, restat, sbox bool) {
+func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir WritablePath, restat, sbox bool) {
rule := NewRuleBuilder()
if sbox {
rule.Sbox(outDir)
}
- rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out).ImplicitDepFile(outDep)
+ rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
if restat {
rule.Restat()
@@ -496,12 +497,12 @@
bp := `
rule_builder_test {
name: "foo",
- src: "bar",
+ srcs: ["bar"],
restat: true,
}
rule_builder_test {
name: "foo_sbox",
- src: "bar",
+ srcs: ["bar"],
sbox: true,
}
`
@@ -519,7 +520,10 @@
check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
t.Helper()
- if params.RuleParams.Command != wantCommand {
+ command := params.RuleParams.Command
+ re := regexp.MustCompile(" (# hash of input list:|--input-hash) [a-z0-9]*")
+ command = re.ReplaceAllLiteralString(command, "")
+ if command != wantCommand {
t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
}
@@ -651,3 +655,78 @@
})
}
}
+
+func TestRuleBuilderHashInputs(t *testing.T) {
+ // The basic idea here is to verify that the command (in the case of a
+ // non-sbox rule) or the sbox textproto manifest contain a hash of the
+ // inputs.
+
+ // By including a hash of the inputs, we cause the rule to re-run if
+ // the list of inputs changes because the command line or a dependency
+ // changes.
+
+ bp := `
+ rule_builder_test {
+ name: "hash0",
+ srcs: ["in1.txt", "in2.txt"],
+ }
+ rule_builder_test {
+ name: "hash0_sbox",
+ srcs: ["in1.txt", "in2.txt"],
+ sbox: true,
+ }
+ rule_builder_test {
+ name: "hash1",
+ srcs: ["in1.txt", "in2.txt", "in3.txt"],
+ }
+ rule_builder_test {
+ name: "hash1_sbox",
+ srcs: ["in1.txt", "in2.txt", "in3.txt"],
+ sbox: true,
+ }
+ `
+ testcases := []struct {
+ name string
+ expectedHash string
+ }{
+ {
+ name: "hash0",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
+ expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
+ },
+ {
+ name: "hash1",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
+ },
+ }
+
+ config := TestConfig(buildDir, nil, bp, nil)
+ ctx := NewTestContext(config)
+ ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ t.Run("sbox", func(t *testing.T) {
+ gen := ctx.ModuleForTests(test.name+"_sbox", "")
+ command := gen.Output(test.name + "_sbox").RuleParams.Command
+ if g, w := command, " --input-hash "+test.expectedHash; !strings.Contains(g, w) {
+ t.Errorf("Expected command line to end with %q, got %q", w, g)
+ }
+ })
+ t.Run("", func(t *testing.T) {
+ gen := ctx.ModuleForTests(test.name+"", "")
+ command := gen.Output(test.name).RuleParams.Command
+ if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
+ t.Errorf("Expected command line to end with %q, got %q", w, g)
+ }
+ })
+ })
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index d83cecc..1e2ae13 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -104,7 +104,7 @@
}
func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
- ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(factory))
+ ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
}
func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 739a965..4540a1f 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -214,6 +214,8 @@
"LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
"LOCAL_JETIFIER_ENABLED": "jetifier",
+
+ "LOCAL_IS_UNIT_TEST": "unit_test",
})
}
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index 86dabf9..e61241b 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -212,8 +212,21 @@
expression := SimpleMakeString("", pos)
switch d {
- case "endif", "endef", "else":
+ case "endif", "endef":
// Nothing
+ case "else":
+ p.ignoreSpaces()
+ if p.tok != '\n' {
+ d = p.scanner.TokenText()
+ p.accept(scanner.Ident)
+ if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
+ d = "el" + d
+ p.ignoreSpaces()
+ expression = p.parseExpression()
+ } else {
+ p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
+ }
+ }
case "define":
expression, endPos = p.parseDefine()
default:
diff --git a/bazel/bazelenv.sh b/bazel/bazelenv.sh
deleted file mode 100755
index fcf71f1..0000000
--- a/bazel/bazelenv.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash
-
-# Copyright 2020 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.
-
-# Helper script for setting environment variables required for Bazel/Soong
-# mixed builds prototype. For development use only.
-#
-# Usage:
-# export BAZEL_PATH=[some_bazel_path] && source bazelenv.sh
-#
-# If BAZEL_PATH is not set, `which bazel` will be used
-# to locate the appropriate bazel to use.
-
-
-# Function to find top of the source tree (if $TOP isn't set) by walking up the
-# tree.
-function gettop
-{
- local TOPFILE=build/soong/root.bp
- if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
- # The following circumlocution ensures we remove symlinks from TOP.
- (cd $TOP; PWD= /bin/pwd)
- else
- if [ -f $TOPFILE ] ; then
- # The following circumlocution (repeated below as well) ensures
- # that we record the true directory name and not one that is
- # faked up with symlink names.
- PWD= /bin/pwd
- else
- local HERE=$PWD
- T=
- while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
- \cd ..
- T=`PWD= /bin/pwd -P`
- done
- \cd $HERE
- if [ -f "$T/$TOPFILE" ]; then
- echo $T
- fi
- fi
- fi
-}
-
-BASE_DIR="$(mktemp -d)"
-
-if [ -z "$BAZEL_PATH" ] ; then
- export BAZEL_PATH="$(which bazel)"
-fi
-
-# TODO(cparsons): Use USE_BAZEL=1 instead once "mixed Soong/Bazel builds" are
-# production ready.
-export USE_BAZEL_ANALYSIS=1
-# TODO(cparsons): Retrieve this information in either envsetup.sh or
-# bazel.sh.
-export BAZEL_HOME="$BASE_DIR/bazelhome"
-export BAZEL_OUTPUT_BASE="$BASE_DIR/output"
-export BAZEL_WORKSPACE="$(gettop)"
-
-echo "USE_BAZEL_ANALYSIS=${USE_BAZEL_ANALYSIS}"
-echo "BAZEL_PATH=${BAZEL_PATH}"
-echo "BAZEL_HOME=${BAZEL_HOME}"
-echo "BAZEL_OUTPUT_BASE=${BAZEL_OUTPUT_BASE}"
-echo "BAZEL_WORKSPACE=${BAZEL_WORKSPACE}"
-
-mkdir -p $BAZEL_HOME
-mkdir -p $BAZEL_OUTPUT_BASE
diff --git a/bazel/master.WORKSPACE.bazel b/bazel/master.WORKSPACE.bazel
deleted file mode 100644
index e69de29..0000000
--- a/bazel/master.WORKSPACE.bazel
+++ /dev/null
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 38269cb..d32e4de 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -370,6 +370,9 @@
entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
+ if Bool(test.Properties.Test_options.Unit_test) {
+ entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+ }
})
androidMkWriteTestData(test.data, ctx, entries)
@@ -505,7 +508,7 @@
})
}
-func (c *vendorSnapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
// Each vendor snapshot is exported to androidMk only when BOARD_VNDK_VERSION != current
// and the version of the prebuilt is same as BOARD_VNDK_VERSION.
if c.shared() {
@@ -549,7 +552,7 @@
})
}
-func (c *vendorSnapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
if c.androidMkVendorSuffix {
@@ -563,7 +566,7 @@
})
}
-func (c *vendorSnapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
if c.androidMkVendorSuffix {
diff --git a/cc/cc.go b/cc/cc.go
index 5e4faf2..bd6e5d5 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -88,7 +88,7 @@
ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
})
- android.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
+ ctx.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
}
type Deps struct {
@@ -305,10 +305,11 @@
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
- // vendor snapshot, but this property allows a partner to exclude a
- // module normally thought of as a framework module from the vendor
- // snapshot.
- Exclude_from_vendor_snapshot *bool
+ // different snapshots (vendor, recovery, etc.), but these properties
+ // allow a partner to exclude a module normally thought of as a
+ // framework module from a snapshot.
+ Exclude_from_vendor_snapshot *bool
+ Exclude_from_recovery_snapshot *bool
}
type VendorProperties struct {
@@ -368,7 +369,7 @@
useSdk() bool
sdkVersion() string
useVndk() bool
- isNdk() bool
+ isNdk(config android.Config) bool
isLlndk(config android.Config) bool
isLlndkPublic(config android.Config) bool
isVndkPrivate(config android.Config) bool
@@ -938,8 +939,8 @@
return c.coverage.Properties.IsCoverageVariant
}
-func (c *Module) IsNdk() bool {
- return inList(c.BaseModuleName(), ndkKnownLibs)
+func (c *Module) IsNdk(config android.Config) bool {
+ return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
}
func (c *Module) isLlndk(config android.Config) bool {
@@ -1051,6 +1052,10 @@
return Bool(c.Properties.Exclude_from_vendor_snapshot)
}
+func (c *Module) ExcludeFromRecoverySnapshot() bool {
+ return Bool(c.Properties.Exclude_from_recovery_snapshot)
+}
+
func isBionic(name string) bool {
switch name {
case "libc", "libm", "libdl", "libdl_android", "linker":
@@ -1140,8 +1145,8 @@
return ctx.mod.UseVndk()
}
-func (ctx *moduleContextImpl) isNdk() bool {
- return ctx.mod.IsNdk()
+func (ctx *moduleContextImpl) isNdk(config android.Config) bool {
+ return ctx.mod.IsNdk(config)
}
func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
@@ -1761,7 +1766,7 @@
for _, entry := range list {
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
- if ctx.useSdk() && inList(name, ndkKnownLibs) {
+ if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
variantLibs = append(variantLibs, name+ndkLibrarySuffix)
} else if ctx.useVndk() {
nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry))
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f616cf3..f5ce867 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1551,6 +1551,8 @@
android.CheckErrorsAgainstExpectations(t, errs, []string{
`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
})
}
@@ -1597,6 +1599,132 @@
})
}
+func TestRecoverySnapshotCapture(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ recovery_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery_available",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_library_headers {
+ name: "librecovery_headers",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_bin",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_available_bin",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ toolchain_library {
+ name: "libb",
+ recovery_available: true,
+ src: "libb.a",
+ }
+
+ cc_object {
+ name: "obj",
+ recovery_available: true,
+ }
+`
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ ctx := testCcWithConfig(t, config)
+
+ // Check Recovery snapshot output.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, only recovery_available modules are captured.
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "libvndk.so.json"),
+ filepath.Join(sharedDir, "librecovery.so.json"),
+ filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+ // For static libraries, all recovery:true and recovery_available modules are captured.
+ staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "libb.a.json"),
+ filepath.Join(staticDir, "librecovery.a.json"),
+ filepath.Join(staticDir, "librecovery_available.a.json"))
+
+ // For binary executables, all recovery:true and recovery_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "recovery_bin.json"),
+ filepath.Join(binaryDir, "recovery_available_bin.json"))
+ }
+
+ // For header libraries, all vendor:true and vendor_available modules are captured.
+ headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+ jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
+
+ // For object modules, all vendor:true and vendor_available modules are captured.
+ objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+ checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+ jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+}
+
func TestDoubleLoadableDepError(t *testing.T) {
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
diff --git a/cc/compiler.go b/cc/compiler.go
index 3c86d20..04ed80d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -687,6 +687,9 @@
// list of shared libraries that provide headers for this binding.
Shared_libs []string `android:"arch_variant"`
+ // List of libraries which export include paths required for this module
+ Header_libs []string `android:"arch_variant,variant_prepend"`
+
// list of clang flags required to correctly interpret the headers.
Cflags []string `android:"arch_variant"`
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index d18ae25..563ce76 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -21,6 +21,8 @@
"android.hardware.automotive.occupant_awareness-ndk_platform",
"android.hardware.light-ndk_platform",
"android.hardware.identity-ndk_platform",
+ "android.hardware.keymint-ndk_platform",
+ "android.hardware.keymint-unstable-ndk_platform",
"android.hardware.nfc@1.2",
"android.hardware.power-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
diff --git a/cc/gen.go b/cc/gen.go
index ccc3d0e..134d6d9 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -75,7 +75,11 @@
cmd := rule.Command()
// Fix up #line markers to not use the sbox temporary directory
- sedCmd := "sed -i.bak 's#__SBOX_OUT_DIR__#" + outDir.String() + "#'"
+ // android.SboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
+ // directory itself, without any filename appended.
+ // TODO(ccross): make this cmd.PathForOutput(outDir) instead.
+ sboxOutDir := android.SboxPathForOutput(outDir, outDir)
+ sedCmd := "sed -i.bak 's#" + sboxOutDir + "#" + outDir.String() + "#'"
rule.Command().Text(sedCmd).Input(outFile)
rule.Command().Text(sedCmd).Input(headerFile)
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index 0c7952b..fa0c6f2 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -66,14 +66,14 @@
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
expected := []string{"foo"}
- if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
- t.Errorf(`want arm inputs %v, got %v`, expected, gen.Inputs.Strings())
+ if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+ t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
}
gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
expected = []string{"bar"}
- if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
- t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
+ if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+ t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Implicits.Strings())
}
}
@@ -108,10 +108,10 @@
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
var got []string
- for _, input := range gen.Inputs {
+ for _, input := range gen.Implicits {
got = append(got, input.Base())
}
- if !reflect.DeepEqual(expected, got) {
+ if !reflect.DeepEqual(expected, got[:len(expected)]) {
t.Errorf(`want inputs %v, got %v`, expected, got)
}
}
diff --git a/cc/library.go b/cc/library.go
index eeddd90..2127c08 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -567,7 +567,7 @@
return ""
}
// Return NDK if the library is both NDK and LLNDK.
- if ctx.isNdk() {
+ if ctx.isNdk(ctx.Config()) {
return "NDK"
}
if ctx.isLlndkPublic(ctx.Config()) {
@@ -1099,7 +1099,7 @@
func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
// The logic must be consistent with classifySourceAbiDump.
- isNdk := ctx.isNdk()
+ isNdk := ctx.isNdk(ctx.Config())
isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || (ctx.useVndk() && ctx.isVndk())
refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
@@ -1153,7 +1153,7 @@
library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
refAbiDumpFile, fileName, exportedHeaderFlags,
Bool(library.Properties.Header_abi_checker.Check_all_apis),
- ctx.isLlndk(ctx.Config()), ctx.isNdk(), ctx.isVndkExt())
+ ctx.isLlndk(ctx.Config()), ctx.isNdk(ctx.Config()), ctx.isVndkExt())
}
}
}
diff --git a/cc/makevars.go b/cc/makevars.go
index dcfd6d8..bd8aab5 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -171,6 +171,7 @@
ctx.StrictRaw("SRC_HEADERS", strings.Join(includes, " "))
ctx.StrictRaw("SRC_SYSTEM_HEADERS", strings.Join(systemIncludes, " "))
+ ndkKnownLibs := *getNDKKnownLibs(ctx.Config())
sort.Strings(ndkKnownLibs)
ctx.Strict("NDK_KNOWN_LIBS", strings.Join(ndkKnownLibs, " "))
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 9097e7b..a5c43fe 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -45,8 +45,7 @@
ndkLibrarySuffix = ".ndk"
- // Added as a variation dependency via depsMutator.
- ndkKnownLibs = []string{}
+ ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
// protects ndkKnownLibs writes during parallel BeginMutator.
ndkKnownLibsLock sync.Mutex
)
@@ -158,6 +157,12 @@
return true
}
+func getNDKKnownLibs(config android.Config) *[]string {
+ return config.Once(ndkKnownLibsKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
c.baseCompiler.compilerInit(ctx)
@@ -168,12 +173,13 @@
ndkKnownLibsLock.Lock()
defer ndkKnownLibsLock.Unlock()
- for _, lib := range ndkKnownLibs {
+ ndkKnownLibs := getNDKKnownLibs(ctx.Config())
+ for _, lib := range *ndkKnownLibs {
if lib == name {
return
}
}
- ndkKnownLibs = append(ndkKnownLibs, name)
+ *ndkKnownLibs = append(*ndkKnownLibs, name)
}
func addStubLibraryCompilerFlags(flags Flags) Flags {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b1326d9..eb61525 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -847,7 +847,7 @@
return true
}
- if p, ok := d.linker.(*vendorSnapshotLibraryDecorator); ok {
+ if p, ok := d.linker.(*snapshotLibraryDecorator); ok {
if Bool(p.properties.Sanitize_minimal_dep) {
c.sanitize.Properties.MinimalRuntimeDep = true
}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 05c06ac..a3d52e6 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -60,7 +60,8 @@
func isSnapshotAware(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool {
if _, _, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m, apexInfo); ok {
return ctx.Config().VndkSnapshotBuildArtifacts()
- } else if isVendorSnapshotModule(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) {
+ } else if isVendorSnapshotModule(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) ||
+ isRecoverySnapshotModule(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) {
return true
}
return false
diff --git a/cc/test.go b/cc/test.go
index 619dc4d..3772691 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -46,6 +46,9 @@
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
+
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
}
type TestBinaryProperties struct {
diff --git a/cc/testing.go b/cc/testing.go
index 5a311f4..7161313 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -563,6 +563,7 @@
RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 78bde38..6563f6e 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -25,6 +25,115 @@
"android/soong/android"
)
+// Defines the specifics of different images to which the snapshot process is
+// applicable, e.g., vendor, recovery, ramdisk.
+type image interface {
+ // Used to register callbacks with the build system.
+ init()
+
+ // Function that returns true if the module is included in this image.
+ // Using a function return instead of a value to prevent early
+ // evalution of a function that may be not be defined.
+ inImage(m *Module) func() bool
+
+ // Returns the value of the "available" property for a given module for
+ // and snapshot, e.g., "vendor_available", "recovery_available", etc.
+ // or nil if the property is not defined.
+ available(m *Module) *bool
+
+ // Returns true if a dir under source tree is an SoC-owned proprietary
+ // directory, such as device/, vendor/, etc.
+ //
+ // For a given snapshot (e.g., vendor, recovery, etc.) if
+ // isProprietaryPath(dir) returns true, then the module in dir will be
+ // built from sources.
+ isProprietaryPath(dir string) bool
+
+ // Whether to include VNDK in the snapshot for this image.
+ includeVndk() bool
+
+ // Whether a given module has been explicitly excluded from the
+ // snapshot, e.g., using the exclude_from_vendor_snapshot or
+ // exclude_from_recovery_snapshot properties.
+ excludeFromSnapshot(m *Module) bool
+}
+
+type vendorImage struct{}
+type recoveryImage struct{}
+
+func (vendorImage) init() {
+ android.RegisterSingletonType(
+ "vendor-snapshot", VendorSnapshotSingleton)
+ android.RegisterModuleType(
+ "vendor_snapshot_shared", VendorSnapshotSharedFactory)
+ android.RegisterModuleType(
+ "vendor_snapshot_static", VendorSnapshotStaticFactory)
+ android.RegisterModuleType(
+ "vendor_snapshot_header", VendorSnapshotHeaderFactory)
+ android.RegisterModuleType(
+ "vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+ android.RegisterModuleType(
+ "vendor_snapshot_object", VendorSnapshotObjectFactory)
+}
+
+func (vendorImage) inImage(m *Module) func() bool {
+ return m.inVendor
+}
+
+func (vendorImage) available(m *Module) *bool {
+ return m.VendorProperties.Vendor_available
+}
+
+func (vendorImage) isProprietaryPath(dir string) bool {
+ return isVendorProprietaryPath(dir)
+}
+
+func (vendorImage) includeVndk() bool {
+ return true
+}
+
+func (vendorImage) excludeFromSnapshot(m *Module) bool {
+ return m.ExcludeFromVendorSnapshot()
+}
+
+func (recoveryImage) init() {
+ android.RegisterSingletonType(
+ "recovery-snapshot", RecoverySnapshotSingleton)
+ android.RegisterModuleType(
+ "recovery_snapshot_shared", RecoverySnapshotSharedFactory)
+ android.RegisterModuleType(
+ "recovery_snapshot_static", RecoverySnapshotStaticFactory)
+ android.RegisterModuleType(
+ "recovery_snapshot_header", RecoverySnapshotHeaderFactory)
+ android.RegisterModuleType(
+ "recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
+ android.RegisterModuleType(
+ "recovery_snapshot_object", RecoverySnapshotObjectFactory)
+}
+
+func (recoveryImage) inImage(m *Module) func() bool {
+ return m.InRecovery
+}
+
+func (recoveryImage) available(m *Module) *bool {
+ return m.Properties.Recovery_available
+}
+
+func (recoveryImage) isProprietaryPath(dir string) bool {
+ return isRecoveryProprietaryPath(dir)
+}
+
+func (recoveryImage) includeVndk() bool {
+ return false
+}
+
+func (recoveryImage) excludeFromSnapshot(m *Module) bool {
+ return m.ExcludeFromRecoverySnapshot()
+}
+
+var vendorImageSingleton vendorImage
+var recoveryImageSingleton recoveryImage
+
const (
vendorSnapshotHeaderSuffix = ".vendor_header."
vendorSnapshotSharedSuffix = ".vendor_shared."
@@ -33,6 +142,14 @@
vendorSnapshotObjectSuffix = ".vendor_object."
)
+const (
+ recoverySnapshotHeaderSuffix = ".recovery_header."
+ recoverySnapshotSharedSuffix = ".recovery_shared."
+ recoverySnapshotStaticSuffix = ".recovery_static."
+ recoverySnapshotBinarySuffix = ".recovery_binary."
+ recoverySnapshotObjectSuffix = ".recovery_object."
+)
+
var (
vendorSnapshotsLock sync.Mutex
vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules")
@@ -136,7 +253,7 @@
}
}
-type vendorSnapshotLibraryProperties struct {
+type snapshotLibraryProperties struct {
// Prebuilt file for each arch.
Src *string `android:"arch_variant"`
@@ -161,25 +278,25 @@
setSanitizerVariation(t sanitizerType, enabled bool)
}
-type vendorSnapshotLibraryDecorator struct {
+type snapshotLibraryDecorator struct {
vendorSnapshotModuleBase
*libraryDecorator
- properties vendorSnapshotLibraryProperties
+ properties snapshotLibraryProperties
sanitizerProperties struct {
CfiEnabled bool `blueprint:"mutated"`
// Library flags for cfi variant.
- Cfi vendorSnapshotLibraryProperties `android:"arch_variant"`
+ Cfi snapshotLibraryProperties `android:"arch_variant"`
}
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
return p.libraryDecorator.linkerFlags(ctx, flags)
}
-func (p *vendorSnapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
arches := config.Arches()
if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
return false
@@ -190,7 +307,7 @@
return true
}
-func (p *vendorSnapshotLibraryDecorator) link(ctx ModuleContext,
+func (p *snapshotLibraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
m := ctx.Module().(*Module)
p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
@@ -246,17 +363,17 @@
return in
}
-func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
+func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
p.baseInstaller.install(ctx, file)
}
}
-func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
+func (p *snapshotLibraryDecorator) nativeCoverage() bool {
return false
}
-func (p *vendorSnapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
switch t {
case cfi:
return p.sanitizerProperties.Cfi.Src != nil
@@ -265,7 +382,7 @@
}
}
-func (p *vendorSnapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
if !enabled {
return
}
@@ -277,14 +394,14 @@
}
}
-func vendorSnapshotLibrary(suffix string) (*Module, *vendorSnapshotLibraryDecorator) {
+func snapshotLibrary(suffix string) (*Module, *snapshotLibraryDecorator) {
module, library := NewLibrary(android.DeviceSupported)
module.stl = nil
module.sanitize = nil
library.disableStripping()
- prebuilt := &vendorSnapshotLibraryDecorator{
+ prebuilt := &snapshotLibraryDecorator{
libraryDecorator: library,
}
@@ -310,38 +427,56 @@
}
func VendorSnapshotSharedFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary(vendorSnapshotSharedSuffix)
+ module, prebuilt := snapshotLibrary(vendorSnapshotSharedSuffix)
+ prebuilt.libraryDecorator.BuildOnlyShared()
+ return module.Init()
+}
+
+func RecoverySnapshotSharedFactory() android.Module {
+ module, prebuilt := snapshotLibrary(recoverySnapshotSharedSuffix)
prebuilt.libraryDecorator.BuildOnlyShared()
return module.Init()
}
func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary(vendorSnapshotStaticSuffix)
+ module, prebuilt := snapshotLibrary(vendorSnapshotStaticSuffix)
+ prebuilt.libraryDecorator.BuildOnlyStatic()
+ return module.Init()
+}
+
+func RecoverySnapshotStaticFactory() android.Module {
+ module, prebuilt := snapshotLibrary(recoverySnapshotStaticSuffix)
prebuilt.libraryDecorator.BuildOnlyStatic()
return module.Init()
}
func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary(vendorSnapshotHeaderSuffix)
+ module, prebuilt := snapshotLibrary(vendorSnapshotHeaderSuffix)
prebuilt.libraryDecorator.HeaderOnly()
return module.Init()
}
-var _ snapshotSanitizer = (*vendorSnapshotLibraryDecorator)(nil)
+func RecoverySnapshotHeaderFactory() android.Module {
+ module, prebuilt := snapshotLibrary(recoverySnapshotHeaderSuffix)
+ prebuilt.libraryDecorator.HeaderOnly()
+ return module.Init()
+}
-type vendorSnapshotBinaryProperties struct {
+var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
+type snapshotBinaryProperties struct {
// Prebuilt file for each arch.
Src *string `android:"arch_variant"`
}
-type vendorSnapshotBinaryDecorator struct {
+type snapshotBinaryDecorator struct {
vendorSnapshotModuleBase
*binaryDecorator
- properties vendorSnapshotBinaryProperties
+ properties snapshotBinaryProperties
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
if config.DeviceArch() != p.arch() {
return false
}
@@ -351,7 +486,7 @@
return true
}
-func (p *vendorSnapshotBinaryDecorator) link(ctx ModuleContext,
+func (p *snapshotBinaryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
@@ -382,11 +517,19 @@
return outputFile
}
-func (p *vendorSnapshotBinaryDecorator) nativeCoverage() bool {
+func (p *snapshotBinaryDecorator) nativeCoverage() bool {
return false
}
func VendorSnapshotBinaryFactory() android.Module {
+ return snapshotBinaryFactory(vendorSnapshotBinarySuffix)
+}
+
+func RecoverySnapshotBinaryFactory() android.Module {
+ return snapshotBinaryFactory(recoverySnapshotBinarySuffix)
+}
+
+func snapshotBinaryFactory(suffix string) android.Module {
module, binary := NewBinary(android.DeviceSupported)
binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
binary.baseLinker.Properties.Nocrt = BoolPtr(true)
@@ -396,7 +539,7 @@
binary.baseLinker.Properties.System_shared_libs = []string{}
}
- prebuilt := &vendorSnapshotBinaryDecorator{
+ prebuilt := &snapshotBinaryDecorator{
binaryDecorator: binary,
}
@@ -405,7 +548,7 @@
module.stl = nil
module.linker = prebuilt
- prebuilt.init(module, vendorSnapshotBinarySuffix)
+ prebuilt.init(module, suffix)
module.AddProperties(&prebuilt.properties)
return module.Init()
}
@@ -415,14 +558,14 @@
Src *string `android:"arch_variant"`
}
-type vendorSnapshotObjectLinker struct {
+type snapshotObjectLinker struct {
vendorSnapshotModuleBase
objectLinker
properties vendorSnapshotObjectProperties
androidMkVendorSuffix bool
}
-func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
if config.DeviceArch() != p.arch() {
return false
}
@@ -432,7 +575,7 @@
return true
}
-func (p *vendorSnapshotObjectLinker) link(ctx ModuleContext,
+func (p *snapshotObjectLinker) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
if !p.matchesWithDevice(ctx.DeviceConfig()) {
return nil
@@ -444,14 +587,14 @@
return android.PathForModuleSrc(ctx, *p.properties.Src)
}
-func (p *vendorSnapshotObjectLinker) nativeCoverage() bool {
+func (p *snapshotObjectLinker) nativeCoverage() bool {
return false
}
func VendorSnapshotObjectFactory() android.Module {
module := newObject()
- prebuilt := &vendorSnapshotObjectLinker{
+ prebuilt := &snapshotObjectLinker{
objectLinker: objectLinker{
baseLinker: NewBaseLinker(nil),
},
@@ -463,21 +606,68 @@
return module.Init()
}
+func RecoverySnapshotObjectFactory() android.Module {
+ module := newObject()
+
+ prebuilt := &snapshotObjectLinker{
+ objectLinker: objectLinker{
+ baseLinker: NewBaseLinker(nil),
+ },
+ }
+ module.linker = prebuilt
+
+ prebuilt.init(module, recoverySnapshotObjectSuffix)
+ module.AddProperties(&prebuilt.properties)
+ return module.Init()
+}
+
func init() {
- android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
- android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
- android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
- android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
- android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
- android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+ vendorImageSingleton.init()
+ recoveryImageSingleton.init()
+}
+
+var vendorSnapshotSingleton = snapshotSingleton{
+ "vendor",
+ "SOONG_VENDOR_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ true,
+ vendorImageSingleton,
+}
+
+var recoverySnapshotSingleton = snapshotSingleton{
+ "recovery",
+ "SOONG_RECOVERY_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ false,
+ recoveryImageSingleton,
}
func VendorSnapshotSingleton() android.Singleton {
- return &vendorSnapshotSingleton{}
+ return &vendorSnapshotSingleton
}
-type vendorSnapshotSingleton struct {
- vendorSnapshotZipFile android.OptionalPath
+func RecoverySnapshotSingleton() android.Singleton {
+ return &recoverySnapshotSingleton
+}
+
+type snapshotSingleton struct {
+ // Name, e.g., "vendor", "recovery", "ramdisk".
+ name string
+
+ // Make variable that points to the snapshot file, e.g.,
+ // "SOONG_RECOVERY_SNAPSHOT_ZIP".
+ makeVar string
+
+ // Path to the snapshot zip file.
+ snapshotZipFile android.OptionalPath
+
+ // Whether the image supports VNDK extension modules.
+ supportsVndkExt bool
+
+ // Implementation of the image interface specific to the image
+ // associated with this snapshot (e.g., specific to the vendor image,
+ // recovery image, etc.).
+ image image
}
var (
@@ -491,6 +681,17 @@
"hardware",
}
+ // Modules under following directories are ignored. They are OEM's and vendor's
+ // proprietary modules(device/, kernel/, vendor/, and hardware/).
+ // TODO(b/65377115): Clean up these with more maintainable way
+ recoveryProprietaryDirs = []string{
+ "bootable/recovery",
+ "device",
+ "hardware",
+ "kernel",
+ "vendor",
+ }
+
// Modules under following directories are included as they are in AOSP,
// although hardware/ and kernel/ are normally for vendor's own.
// TODO(b/65377115): Clean up these with more maintainable way
@@ -508,7 +709,17 @@
// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
// device/, vendor/, etc.
func isVendorProprietaryPath(dir string) bool {
- for _, p := range vendorProprietaryDirs {
+ return isProprietaryPath(dir, vendorProprietaryDirs)
+}
+
+func isRecoveryProprietaryPath(dir string) bool {
+ return isProprietaryPath(dir, recoveryProprietaryDirs)
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
+// device/, vendor/, etc.
+func isProprietaryPath(dir string, proprietaryDirs []string) bool {
+ for _, p := range proprietaryDirs {
if strings.HasPrefix(dir, p) {
// filter out AOSP defined directories, e.g. hardware/interfaces/
aosp := false
@@ -556,6 +767,14 @@
// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
// image and newer system image altogether.
func isVendorSnapshotModule(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
+ return isSnapshotModule(m, inVendorProprietaryPath, apexInfo, vendorImageSingleton)
+}
+
+func isRecoverySnapshotModule(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
+ return isSnapshotModule(m, inRecoveryProprietaryPath, apexInfo, recoveryImageSingleton)
+}
+
+func isSnapshotModule(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image image) bool {
if !m.Enabled() || m.Properties.HideFromMake {
return false
}
@@ -564,8 +783,9 @@
if m.IsSkipInstall() {
return false
}
- // skip proprietary modules, but include all VNDK (static)
- if inVendorProprietaryPath && !m.IsVndk() {
+ // skip proprietary modules, but (for the vendor snapshot only)
+ // include all VNDK (static)
+ if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) {
return false
}
// If the module would be included based on its path, check to see if
@@ -580,7 +800,7 @@
return false
}
// the module must be installed in /vendor
- if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !m.inVendor() {
+ if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
return false
}
// skip kernel_headers which always depend on vendor
@@ -612,29 +832,31 @@
}
}
if l.static() {
- return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+ return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
}
if l.shared() {
if !m.outputFile.Valid() {
return false
}
- if !m.IsVndk() {
- return true
+ if image.includeVndk() {
+ if !m.IsVndk() {
+ return true
+ }
+ return m.isVndkExt()
}
- return m.isVndkExt()
}
return true
}
// Binaries and Objects
if m.binary() || m.object() {
- return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+ return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
}
return false
}
-func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
// BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
if ctx.DeviceConfig().VndkVersion() != "current" {
return
@@ -675,7 +897,7 @@
(header files of same directory structure with source tree)
*/
- snapshotDir := "vendor-snapshot"
+ snapshotDir := c.name + "-snapshot"
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
includeDir := filepath.Join(snapshotArchDir, "include")
@@ -722,7 +944,7 @@
// Common properties among snapshots.
prop.ModuleName = ctx.ModuleName(m)
- if m.isVndkExt() {
+ if c.supportsVndkExt && m.isVndkExt() {
// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
if m.isVndkSp() {
prop.RelativeInstallPath = "vndk-sp"
@@ -843,26 +1065,30 @@
}
moduleDir := ctx.ModuleDir(module)
- inVendorProprietaryPath := isVendorProprietaryPath(moduleDir)
+ inProprietaryPath := c.image.isProprietaryPath(moduleDir)
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
if m.ExcludeFromVendorSnapshot() {
- if inVendorProprietaryPath {
+ if inProprietaryPath {
// Error: exclude_from_vendor_snapshot applies
// to framework-path modules only.
ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
return
}
- if Bool(m.VendorProperties.Vendor_available) {
+ if Bool(c.image.available(m)) {
// Error: may not combine "vendor_available:
// true" with "exclude_from_vendor_snapshot:
// true".
- ctx.Errorf("module %q may not use both \"vendor_available: true\" and \"exclude_from_vendor_snapshot: true\"", m.String())
+ ctx.Errorf(
+ "module %q may not use both \""+
+ c.name+
+ "_available: true\" and \"exclude_from_vendor_snapshot: true\"",
+ m.String())
return
}
}
- if !isVendorSnapshotModule(m, inVendorProprietaryPath, apexInfo) {
+ if !isSnapshotModule(m, inProprietaryPath, apexInfo, c.image) {
return
}
@@ -894,11 +1120,17 @@
return snapshotOutputs[i].String() < snapshotOutputs[j].String()
})
- zipPath := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+".zip")
+ zipPath := android.PathForOutput(
+ ctx,
+ snapshotDir,
+ c.name+"-"+ctx.Config().DeviceName()+".zip")
zipRule := android.NewRuleBuilder()
// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
- snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+"_list")
+ snapshotOutputList := android.PathForOutput(
+ ctx,
+ snapshotDir,
+ c.name+"-"+ctx.Config().DeviceName()+"_list")
zipRule.Command().
Text("tr").
FlagWithArg("-d ", "\\'").
@@ -913,13 +1145,15 @@
FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
FlagWithInput("-l ", snapshotOutputList)
- zipRule.Build(pctx, ctx, zipPath.String(), "vendor snapshot "+zipPath.String())
+ zipRule.Build(pctx, ctx, zipPath.String(), c.name+" snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
- c.vendorSnapshotZipFile = android.OptionalPathForPath(zipPath)
+ c.snapshotZipFile = android.OptionalPathForPath(zipPath)
}
-func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
- ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String())
+func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict(
+ c.makeVar,
+ c.snapshotZipFile.String())
}
type snapshotInterface interface {
@@ -927,9 +1161,9 @@
}
var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotObjectLinker)(nil)
+var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
+var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
+var _ snapshotInterface = (*snapshotObjectLinker)(nil)
// gathers all snapshot modules for vendor, and disable unnecessary snapshots
// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
@@ -970,9 +1204,9 @@
// header
snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
}
- } else if _, ok := module.linker.(*vendorSnapshotBinaryDecorator); ok {
+ } else if _, ok := module.linker.(*snapshotBinaryDecorator); ok {
snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if _, ok := module.linker.(*vendorSnapshotObjectLinker); ok {
+ } else if _, ok := module.linker.(*snapshotObjectLinker); ok {
snapshotMap = vendorSnapshotObjects(ctx.Config())
} else {
return
diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go
index d305d83..8020b17 100644
--- a/cmd/soong_env/soong_env.go
+++ b/cmd/soong_env/soong_env.go
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// soong_glob is the command line tool that checks if the list of files matching a glob has
-// changed, and only updates the output file list if it has changed. It is used to optimize
-// out build.ninja regenerations when non-matching files are added. See
-// android/soong/android/glob.go for a longer description.
+// soong_env determines if the given soong environment file (usually ".soong.environment") is stale
+// by comparing its contents to the current corresponding environment variable values.
+// It fails if the file cannot be opened or corrupted, or its contents differ from the current
+// values.
+
package main
import (
@@ -34,6 +35,7 @@
os.Exit(2)
}
+// This is a simple executable packaging, and the real work happens in env.StaleEnvFile.
func main() {
flag.Parse()
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index aee8e5a..29030d6 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -125,27 +125,35 @@
os.Exit(1)
}
+ // Create a terminal output that mimics Ninja's.
output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
+ // Attach a new logger instance to the terminal output.
log := logger.New(output)
defer log.Cleanup()
+ // Create a context to simplify the program termination process.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+ // Create a new trace file writer, making it log events to the log instance.
trace := tracer.New(log)
defer trace.Close()
+ // Create and start a new metric record.
met := metrics.New()
met.SetBuildDateTime(buildStarted)
met.SetBuildCommand(os.Args)
+ // Create a new Status instance, which manages action counts and event output channels.
stat := &status.Status{}
defer stat.Finish()
+ // Hook up the terminal output and tracer to Status.
stat.AddOutput(output)
stat.AddOutput(trace.StatusTracer())
+ // Set up a cleanup procedure in case the normal termination process doesn't work.
build.SetupSignals(log, cancel, func() {
trace.Close()
log.Cleanup()
@@ -165,6 +173,7 @@
build.SetupOutDir(buildCtx, config)
+ // Set up files to be outputted in the log directory.
logsDir := config.OutDir()
if config.Dist() {
logsDir = filepath.Join(config.DistDir(), "logs")
@@ -192,7 +201,10 @@
defer met.Dump(soongMetricsFile)
defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
+ // Read the time at the starting point.
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
+ // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
+ // which Darwin doesn't support. Check if it was executed properly before parsing the value.
if !strings.HasSuffix(start, "N") {
if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
log.Verbosef("Took %dms to start up.",
@@ -211,6 +223,7 @@
fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
+ // Create a source finder.
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
@@ -354,6 +367,8 @@
return terminal.StdioImpl{}
}
+// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
+// reporting events to keep stdout clean from noise.
func customStdio() terminal.StdioInterface {
return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
}
@@ -493,16 +508,6 @@
if c.flag == args[1] {
return &c, args[2:], nil
}
-
- // special case for --make-mode: if soong_ui was called from
- // build/make/core/main.mk, the makeparallel with --ninja
- // option specified puts the -j<num> before --make-mode.
- // TODO: Remove this hack once it has been fixed.
- if c.flag == makeModeFlagName {
- if inList(makeModeFlagName, args) {
- return &c, args[1:], nil
- }
- }
}
// command not found
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 77d6ee9..b910a70 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -71,6 +71,10 @@
// Nested class loader subcontexts for dependencies.
Subcontexts []*ClassLoaderContext
+
+ // If the library is a shared library. This affects which elements of class loader context are
+ // added as <uses-library> tags by the manifest_fixer (dependencies of shared libraries aren't).
+ IsSharedLibrary bool
}
// ClassLoaderContextMap is a map from SDK version to a class loader context.
@@ -81,7 +85,7 @@
// Add class loader context for the given library to the map entry for the given SDK version.
func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
+ shared bool, hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
// not found. However, this is likely to result is disabling dexpreopt, as it won't be
@@ -128,46 +132,46 @@
}
clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
- Name: lib,
- Host: hostPath,
- Device: devicePath,
- Subcontexts: subcontexts,
+ Name: lib,
+ Host: hostPath,
+ Device: devicePath,
+ Subcontexts: subcontexts,
+ IsSharedLibrary: shared,
})
return nil
}
// Wrapper around addContext that reports errors.
func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
+ shared bool, hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
- err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
+ err := clcMap.addContext(ctx, sdkVer, lib, shared, hostPath, installPath, strict, nestedClcMap)
if err != nil {
ctx.ModuleErrorf(err.Error())
- android.ReportPathErrorf(ctx, err.Error())
}
}
// Add class loader context. Fail on unknown build/install paths.
func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path) {
+ shared bool, hostPath, installPath android.Path) {
- clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
+ clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, shared, hostPath, installPath, true, nil)
}
// Add class loader context if the library exists. Don't fail on unknown build/install paths.
func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
- hostPath, installPath android.Path) {
+ shared bool, hostPath, installPath android.Path) {
if lib != nil {
- clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
+ clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, shared, hostPath, installPath, false, nil)
}
}
// Add class loader context for the given SDK version. Fail on unknown build/install paths.
func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
- lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+ lib string, shared bool, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
- clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
+ clcMap.addContextOrReportError(ctx, sdkVer, lib, shared, hostPath, installPath, true, nestedClcMap)
}
// Merge the other class loader context map into this one, do not override existing entries.
@@ -204,7 +208,9 @@
}
}
-// List of libraries in the unconditional class loader context, excluding dependencies of shared libraries.
+// List of libraries in the unconditional class loader context, excluding dependencies of shared
+// libraries. These libraries should be in the <uses-library> tags in the manifest. Some of them may
+// be present in the original manifest, others are added by the manifest_fixer.
func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
if clcMap != nil {
// compatibility libraries (those in conditional context) are not added to <uses-library> tags
@@ -217,7 +223,12 @@
func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
for _, clc := range clcs {
ulibs = append(ulibs, clc.Name)
- ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
+ // <uses-library> tags in the manifest should not include dependencies of shared libraries,
+ // because PackageManager already tracks all such dependencies and automatically adds their
+ // class loader contexts as subcontext of the shared library.
+ if !clc.IsSharedLibrary {
+ ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
+ }
}
return ulibs
}
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index e0a75bf..abfca27 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -25,6 +25,11 @@
"android/soong/android"
)
+const (
+ shared = true // dependencies are not added to uses libs
+ nonshared = false // dependencies are added to uses libs
+)
+
func TestCLC(t *testing.T) {
// Construct class loader context with the following structure:
// .
@@ -50,36 +55,36 @@
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
- m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
+ m.AddContext(ctx, "a", nonshared, buildPath(ctx, "a"), installPath(ctx, "a"))
+ m.AddContext(ctx, "b", shared, buildPath(ctx, "b"), installPath(ctx, "b"))
// "Maybe" variant in the good case: add as usual.
c := "c"
- m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
+ m.MaybeAddContext(ctx, &c, nonshared, buildPath(ctx, "c"), installPath(ctx, "c"))
// "Maybe" variant in the bad case: don't add library with unknown name, keep going.
- m.MaybeAddContext(ctx, nil, nil, nil)
+ m.MaybeAddContext(ctx, nil, nonshared, nil, nil)
// Add some libraries with nested subcontexts.
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
- m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+ m1.AddContext(ctx, "a1", nonshared, buildPath(ctx, "a1"), installPath(ctx, "a1"))
+ m1.AddContext(ctx, "b1", shared, buildPath(ctx, "b1"), installPath(ctx, "b1"))
m2 := make(ClassLoaderContextMap)
- m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
- m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
- m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+ m2.AddContext(ctx, "a2", nonshared, buildPath(ctx, "a2"), installPath(ctx, "a2"))
+ m2.AddContext(ctx, "b2", shared, buildPath(ctx, "b2"), installPath(ctx, "b2"))
+ m2.AddContextForSdk(ctx, AnySdkVersion, "c2", shared, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
m3 := make(ClassLoaderContextMap)
- m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
- m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+ m3.AddContext(ctx, "a3", nonshared, buildPath(ctx, "a3"), installPath(ctx, "a3"))
+ m3.AddContext(ctx, "b3", shared, buildPath(ctx, "b3"), installPath(ctx, "b3"))
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ m.AddContextForSdk(ctx, AnySdkVersion, "d", nonshared, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
// When the same library is both in conditional and unconditional context, it should be removed
// from conditional context.
- m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContextForSdk(ctx, 42, "f", nonshared, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContextForSdk(ctx, AnySdkVersion, "f", nonshared, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
// Merge map with implicit root library that is among toplevel contexts => does nothing.
m.AddContextMap(m1, "c")
@@ -88,12 +93,12 @@
m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
- m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+ m.AddContextForSdk(ctx, 29, AndroidHidlManager, nonshared, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContextForSdk(ctx, 29, AndroidHidlBase, nonshared, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+ m.AddContextForSdk(ctx, 30, AndroidTestMock, nonshared, buildPath(ctx, AndroidTestMock), nil, nil)
valid, validationError := validateClassLoaderContext(m)
@@ -153,7 +158,7 @@
// Test for libraries that are added by the manifest_fixer.
t.Run("uses libs", func(t *testing.T) {
- wantUsesLibs := []string{"a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3"}
+ wantUsesLibs := []string{"a", "b", "c", "d", "a2", "b2", "c2", "f", "a3", "b3"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
}
@@ -164,7 +169,7 @@
func TestCLCUnknownBuildPath(t *testing.T) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
+ err := m.addContext(ctx, AnySdkVersion, "a", nonshared, nil, nil, true, nil)
checkError(t, err, "unknown build path to <uses-library> \"a\"")
}
@@ -172,7 +177,7 @@
func TestCLCUnknownInstallPath(t *testing.T) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
+ err := m.addContext(ctx, AnySdkVersion, "a", nonshared, buildPath(ctx, "a"), nil, true, nil)
checkError(t, err, "unknown install path to <uses-library> \"a\"")
}
@@ -181,7 +186,7 @@
m := make(ClassLoaderContextMap)
a := "a"
- m.MaybeAddContext(ctx, &a, nil, nil)
+ m.MaybeAddContext(ctx, &a, nonshared, nil, nil)
// The library should be added to <uses-library> tags by the manifest_fixer.
t.Run("maybe add", func(t *testing.T) {
@@ -203,9 +208,9 @@
func TestCLCNestedConditional(t *testing.T) {
ctx := testContext()
m1 := make(ClassLoaderContextMap)
- m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m1.AddContextForSdk(ctx, 42, "a", nonshared, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+ err := m.addContext(ctx, AnySdkVersion, "b", nonshared, buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
checkError(t, err, "nested class loader context shouldn't have conditional part")
}
diff --git a/env/env.go b/env/env.go
index a98e1f6..735a38a 100644
--- a/env/env.go
+++ b/env/env.go
@@ -27,6 +27,15 @@
type envFileEntry struct{ Key, Value string }
type envFileData []envFileEntry
+// Serializes the given environment variable name/value map into JSON formatted bytes by converting
+// to envFileEntry values and marshaling them.
+//
+// e.g. OUT_DIR = "out"
+// is converted to:
+// {
+// "Key": "OUT_DIR",
+// "Value": "out",
+// },
func EnvFileContents(envDeps map[string]string) ([]byte, error) {
contents := make(envFileData, 0, len(envDeps))
for key, value := range envDeps {
@@ -45,8 +54,11 @@
return data, nil
}
-func StaleEnvFile(filename string) (bool, error) {
- data, err := ioutil.ReadFile(filename)
+// Reads and deserializes a Soong environment file located at the given file path to determine its
+// staleness. If any environment variable values have changed, it prints them out and returns true.
+// Failing to read or parse the file also causes it to return true.
+func StaleEnvFile(filepath string) (bool, error) {
+ data, err := ioutil.ReadFile(filepath)
if err != nil {
return true, err
}
@@ -79,6 +91,7 @@
return false, nil
}
+// Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
func (e envFileData) Len() int {
return len(e)
}
@@ -90,3 +103,5 @@
func (e envFileData) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
+
+var _ sort.Interface = envFileData{}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 44b8149..a6d1fcf 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -14,9 +14,20 @@
package etc
-import (
- "strconv"
+// This file implements module types that install prebuilt artifacts.
+//
+// There exist two classes of prebuilt modules in the Android tree. The first class are the ones
+// based on `android.Prebuilt`, such as `cc_prebuilt_library` and `java_import`. This kind of
+// modules may exist both as prebuilts and source at the same time, though only one would be
+// installed and the other would be marked disabled. The `prebuilt_postdeps` mutator would select
+// the actual modules to be installed. More details in android/prebuilt.go.
+//
+// The second class is described in this file. Unlike `android.Prebuilt` based module types,
+// `prebuilt_etc` exist only as prebuilts and cannot have a same-named source module counterpart.
+// This makes the logic of `prebuilt_etc` to be much simpler as they don't need to go through the
+// various `prebuilt_*` mutators.
+import (
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -42,7 +53,7 @@
}
type prebuiltEtcProperties struct {
- // Source file of this prebuilt.
+ // Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
Src *string `android:"path,arch_variant"`
// optional subdirectory under which this file is installed into, cannot be specified with relative_install_path, prefer relative_install_path
@@ -209,6 +220,11 @@
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.sourceFilePath = android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+
+ // Determine the output file basename.
+ // If Filename is set, use the name specified by the property.
+ // If Filename_from_src is set, use the source file name.
+ // Otherwise use the module name.
filename := android.String(p.properties.Filename)
filename_from_src := android.Bool(p.properties.Filename_from_src)
if filename == "" {
@@ -274,11 +290,9 @@
if len(p.properties.Symlinks) > 0 {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
}
- entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.Installable())
if p.additionalDependencies != nil {
- for _, path := range *p.additionalDependencies {
- entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
- }
+ entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...)
}
},
},
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 53b9dbe..f85146c 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -17,6 +17,7 @@
import (
"fmt"
"io"
+ "path/filepath"
"strconv"
"strings"
@@ -25,9 +26,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/shared"
- "crypto/sha256"
- "path/filepath"
)
func init() {
@@ -156,14 +154,14 @@
type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
type generateTask struct {
- in android.Paths
- out android.WritablePaths
- copyTo android.WritablePaths
- genDir android.WritablePath
- sandboxOuts []string
- cmd string
- shard int
- shards int
+ in android.Paths
+ out android.WritablePaths
+ depFile android.WritablePath
+ copyTo android.WritablePaths
+ genDir android.WritablePath
+ cmd string
+ shard int
+ shards int
}
func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -330,19 +328,23 @@
var zipArgs strings.Builder
for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
- for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
+ if len(task.out) == 0 {
+ ctx.ModuleErrorf("must have at least one output file")
+ return
}
- referencedIn := false
+ for _, out := range task.out {
+ addLocationLabel(out.Rel(), []string{android.SboxPathForOutput(out, task.genDir)})
+ }
+
referencedDepfile := false
- rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
+ rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
- reportError := func(fmt string, args ...interface{}) (string, bool, error) {
+ reportError := func(fmt string, args ...interface{}) (string, error) {
ctx.PropertyErrorf("cmd", fmt, args...)
- return "SOONG_ERROR", false, nil
+ return "SOONG_ERROR", nil
}
switch name {
@@ -357,20 +359,23 @@
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], false, nil
+ return locationLabels[firstLabel][0], nil
case "in":
- referencedIn = true
- return "${in}", true, nil
+ return strings.Join(srcFiles.Strings(), " "), nil
case "out":
- return "__SBOX_OUT_FILES__", false, nil
+ var sandboxOuts []string
+ for _, out := range task.out {
+ sandboxOuts = append(sandboxOuts, android.SboxPathForOutput(out, task.genDir))
+ }
+ return strings.Join(sandboxOuts, " "), nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
return reportError("$(depfile) used without depfile property")
}
- return "__SBOX_DEPFILE__", false, nil
+ return "__SBOX_DEPFILE__", nil
case "genDir":
- return "__SBOX_OUT_DIR__", false, nil
+ return android.SboxPathForOutput(task.genDir, task.genDir), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -381,7 +386,7 @@
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
- return paths[0], false, nil
+ return paths[0], nil
} else {
return reportError("unknown location label %q", label)
}
@@ -391,7 +396,7 @@
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
- return strings.Join(paths, " "), false, nil
+ return strings.Join(paths, " "), nil
} else {
return reportError("unknown locations label %q", label)
}
@@ -410,50 +415,39 @@
ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
return
}
-
- // tell the sbox command which directory to use as its sandbox root
- buildDir := android.PathForOutput(ctx).String()
- sandboxPath := shared.TempDirForOutDir(buildDir)
-
- // recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
- // to be replaced later by ninja_strings.go
- depfilePlaceholder := ""
- if Bool(g.properties.Depfile) {
- depfilePlaceholder = "$depfileArgs"
- }
-
- // Escape the command for the shell
- rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
g.rawCommands = append(g.rawCommands, rawCommand)
- sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s",
- task.genDir, sandboxPath, task.genDir)
-
- if !referencedIn {
- sandboxCommand = sandboxCommand + hashSrcFiles(srcFiles)
- }
-
- sandboxCommand = sandboxCommand + fmt.Sprintf(" -c %s %s $allouts",
- rawCommand, depfilePlaceholder)
-
- ruleParams := blueprint.RuleParams{
- Command: sandboxCommand,
- CommandDeps: []string{"$sboxCmd"},
- }
- args := []string{"allouts"}
- if Bool(g.properties.Depfile) {
- ruleParams.Deps = blueprint.DepsGCC
- args = append(args, "depfileArgs")
- }
+ // Pick a unique rule name and the user-visible description.
+ desc := "generate"
name := "generator"
- if task.shards > 1 {
+ if task.shards > 0 {
+ desc += " " + strconv.Itoa(task.shard)
name += strconv.Itoa(task.shard)
+ } else if len(task.out) == 1 {
+ desc += " " + task.out[0].Base()
}
- rule := ctx.Rule(pctx, name, ruleParams, args...)
- g.generateSourceFile(ctx, task, rule)
+ // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
+ rule := android.NewRuleBuilder().Sbox(task.genDir)
+ cmd := rule.Command()
+ cmd.Text(rawCommand)
+ cmd.ImplicitOutputs(task.out)
+ cmd.Implicits(task.in)
+ cmd.Implicits(g.deps)
+ if Bool(g.properties.Depfile) {
+ cmd.ImplicitDepFile(task.depFile)
+ }
+
+ // Create the rule to run the genrule command inside sbox.
+ rule.Build(pctx, ctx, name, desc)
if len(task.copyTo) > 0 {
+ // If copyTo is set, multiple shards need to be copied into a single directory.
+ // task.out contains the per-shard paths, and copyTo contains the corresponding
+ // final path. The files need to be copied into the final directory by a
+ // single rule so it can remove the directory before it starts to ensure no
+ // old files remain. zipsync already does this, so build up zipArgs that
+ // zip all the per-shard directories into a single zip.
outputFiles = append(outputFiles, task.copyTo...)
copyFrom = append(copyFrom, task.out.Paths()...)
zipArgs.WriteString(" -C " + task.genDir.String())
@@ -464,6 +458,8 @@
}
if len(copyFrom) > 0 {
+ // Create a rule that zips all the per-shard directories into a single zip and then
+ // uses zipsync to unzip it into the final directory.
ctx.Build(pctx, android.BuildParams{
Rule: gensrcsMerge,
Implicits: copyFrom,
@@ -501,51 +497,6 @@
}
}
}
-func hashSrcFiles(srcFiles android.Paths) string {
- h := sha256.New()
- for _, src := range srcFiles {
- h.Write([]byte(src.String()))
- }
- return fmt.Sprintf(" --input-hash %x", h.Sum(nil))
-}
-
-func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
- desc := "generate"
- if len(task.out) == 0 {
- ctx.ModuleErrorf("must have at least one output file")
- return
- }
- if len(task.out) == 1 {
- desc += " " + task.out[0].Base()
- }
-
- var depFile android.ModuleGenPath
- if Bool(g.properties.Depfile) {
- depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
- }
-
- if task.shards > 1 {
- desc += " " + strconv.Itoa(task.shard)
- }
-
- params := android.BuildParams{
- Rule: rule,
- Description: desc,
- Output: task.out[0],
- ImplicitOutputs: task.out[1:],
- Inputs: task.in,
- Implicits: g.deps,
- Args: map[string]string{
- "allouts": strings.Join(task.sandboxOuts, " "),
- },
- }
- if Bool(g.properties.Depfile) {
- params.Depfile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
- params.Args["depfileArgs"] = "--depfile-out " + depFile.String()
- }
-
- ctx.Build(pctx, params)
-}
// Collect information for opening IDE project files in java/jdeps.go.
func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
@@ -610,16 +561,6 @@
func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
-// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
-func pathToSandboxOut(path android.Path, genDir android.Path) string {
- relOut, err := filepath.Rel(genDir.String(), path.String())
- if err != nil {
- panic(fmt.Sprintf("Could not make ${out} relative: %v", err))
- }
- return filepath.Join("__SBOX_OUT_DIR__", relOut)
-
-}
-
func NewGenSrcs() *Module {
properties := &genSrcsProperties{}
@@ -638,7 +579,7 @@
var outFiles android.WritablePaths
var copyTo android.WritablePaths
var shardDir android.WritablePath
- var sandboxOuts []string
+ var depFile android.WritablePath
if len(shards) > 1 {
shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i))
@@ -646,9 +587,11 @@
shardDir = genDir
}
- for _, in := range shard {
+ for j, in := range shard {
outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension))
- sandboxOutfile := pathToSandboxOut(outFile, genDir)
+ if j == 0 {
+ depFile = outFile.ReplaceExtension(ctx, "d")
+ }
if len(shards) > 1 {
shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension))
@@ -657,14 +600,13 @@
}
outFiles = append(outFiles, outFile)
- sandboxOuts = append(sandboxOuts, sandboxOutfile)
command, err := android.Expand(rawCommand, func(name string) (string, error) {
switch name {
case "in":
return in.String(), nil
case "out":
- return sandboxOutfile, nil
+ return android.SboxPathForOutput(outFile, shardDir), nil
default:
return "$(" + name + ")", nil
}
@@ -680,14 +622,14 @@
fullCommand := strings.Join(commands, " && ")
generateTasks = append(generateTasks, generateTask{
- in: shard,
- out: outFiles,
- copyTo: copyTo,
- genDir: shardDir,
- sandboxOuts: sandboxOuts,
- cmd: fullCommand,
- shard: i,
- shards: len(shards),
+ in: shard,
+ out: outFiles,
+ depFile: depFile,
+ copyTo: copyTo,
+ genDir: shardDir,
+ cmd: fullCommand,
+ shard: i,
+ shards: len(shards),
})
}
@@ -720,18 +662,20 @@
taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
outs := make(android.WritablePaths, len(properties.Out))
- sandboxOuts := make([]string, len(properties.Out))
- genDir := android.PathForModuleGen(ctx)
+ var depFile android.WritablePath
for i, out := range properties.Out {
- outs[i] = android.PathForModuleGen(ctx, out)
- sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
+ outPath := android.PathForModuleGen(ctx, out)
+ if i == 0 {
+ depFile = outPath.ReplaceExtension(ctx, "d")
+ }
+ outs[i] = outPath
}
return []generateTask{{
- in: srcFiles,
- out: outs,
- genDir: android.PathForModuleGen(ctx),
- sandboxOuts: sandboxOuts,
- cmd: rawCommand,
+ in: srcFiles,
+ out: outs,
+ depFile: depFile,
+ genDir: android.PathForModuleGen(ctx),
+ cmd: rawCommand,
}}
}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index c19078f..6779c73 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -141,7 +141,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool2",
@@ -150,7 +150,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool file",
@@ -159,7 +159,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "tool_file1 > __SBOX_OUT_FILES__",
+ expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool file fg",
@@ -168,7 +168,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "tool_file1 > __SBOX_OUT_FILES__",
+ expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "empty location tool and tool file",
@@ -178,7 +178,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool",
@@ -187,7 +187,7 @@
out: ["out"],
cmd: "$(location tool) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool2",
@@ -196,7 +196,7 @@
out: ["out"],
cmd: "$(location :tool) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ expect: "out/tool > __SBOX_OUT_DIR__/out",
},
{
name: "tool file",
@@ -205,7 +205,7 @@
out: ["out"],
cmd: "$(location tool_file1) > $(out)",
`,
- expect: "tool_file1 > __SBOX_OUT_FILES__",
+ expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "tool file fg",
@@ -214,7 +214,7 @@
out: ["out"],
cmd: "$(location :1tool_file) > $(out)",
`,
- expect: "tool_file1 > __SBOX_OUT_FILES__",
+ expect: "tool_file1 > __SBOX_OUT_DIR__/out",
},
{
name: "tool files",
@@ -223,7 +223,7 @@
out: ["out"],
cmd: "$(locations :tool_files) > $(out)",
`,
- expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
+ expect: "tool_file1 tool_file2 > __SBOX_OUT_DIR__/out",
},
{
name: "in1",
@@ -232,7 +232,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "in1 fg",
@@ -241,7 +241,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "ins",
@@ -250,7 +250,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "ins fg",
@@ -259,7 +259,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "location in1",
@@ -268,7 +268,7 @@
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location in1 fg",
@@ -277,7 +277,7 @@
out: ["out"],
cmd: "cat $(location :1in) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location ins",
@@ -286,7 +286,7 @@
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_OUT_DIR__/out",
},
{
name: "location ins fg",
@@ -295,7 +295,7 @@
out: ["out"],
cmd: "cat $(locations :ins) > $(out)",
`,
- expect: "cat in1 in2 > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_OUT_DIR__/out",
},
{
name: "outs",
@@ -303,7 +303,7 @@
out: ["out", "out2"],
cmd: "echo foo > $(out)",
`,
- expect: "echo foo > __SBOX_OUT_FILES__",
+ expect: "echo foo > __SBOX_OUT_DIR__/out __SBOX_OUT_DIR__/out2",
},
{
name: "location out",
@@ -320,7 +320,7 @@
depfile: true,
cmd: "echo foo > $(out) && touch $(depfile)",
`,
- expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
+ expect: "echo foo > __SBOX_OUT_DIR__/out && touch __SBOX_DEPFILE__",
},
{
name: "gendir",
@@ -328,7 +328,7 @@
out: ["out"],
cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
`,
- expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
+ expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_DIR__/out",
},
{
@@ -443,7 +443,7 @@
allowMissingDependencies: true,
- expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
+ expect: "cat ***missing srcs :missing*** > __SBOX_OUT_DIR__/out",
},
{
name: "tool allow missing dependencies",
@@ -455,7 +455,7 @@
allowMissingDependencies: true,
- expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
+ expect: "***missing tool :missing*** > __SBOX_OUT_DIR__/out",
},
}
@@ -495,7 +495,7 @@
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- if g, w := gen.rawCommands[0], "'"+test.expect+"'"; w != g {
+ if g, w := gen.rawCommands[0], test.expect; w != g {
t.Errorf("want %q, got %q", w, g)
}
})
@@ -542,18 +542,18 @@
}{
{
name: "hash0",
- // sha256 value obtained from: echo -n 'in1.txtin2.txt' | sha256sum
- expectedHash: "031097e11e0a8c822c960eb9742474f46336360a515744000d086d94335a9cb9",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
+ expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
},
{
name: "hash1",
- // sha256 value obtained from: echo -n 'in1.txtin2.txtin3.txt' | sha256sum
- expectedHash: "de5d22a4a7ab50d250cc59fcdf7a7e0775790d270bfca3a7a9e1f18a70dd996c",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
{
name: "hash2",
- // $(in) is present, option should not appear
- expectedHash: "",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
}
@@ -575,7 +575,7 @@
if len(test.expectedHash) > 0 {
// We add spaces before and after to make sure that
// this option doesn't abutt another sbox option.
- expectedInputHashOption := " --input-hash " + test.expectedHash + " "
+ expectedInputHashOption := " --input-hash " + test.expectedHash
if !strings.Contains(command, expectedInputHashOption) {
t.Errorf("Expected command \"%s\" to contain \"%s\"", command, expectedInputHashOption)
@@ -609,7 +609,7 @@
cmd: "$(location) $(in) > $(out)",
`,
cmds: []string{
- "'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
+ "bash -c 'out/tool in1.txt > __SBOX_OUT_DIR__/in1.h' && bash -c 'out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
@@ -623,8 +623,8 @@
shard_size: 2,
`,
cmds: []string{
- "'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
- "'bash -c '\\''out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'\\'''",
+ "bash -c 'out/tool in1.txt > __SBOX_OUT_DIR__/in1.h' && bash -c 'out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'",
+ "bash -c 'out/tool in3.txt > __SBOX_OUT_DIR__/in3.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
@@ -710,7 +710,7 @@
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
+ expectedCmd := "cp in1 __SBOX_OUT_DIR__/out"
if gen.rawCommands[0] != expectedCmd {
t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
}
diff --git a/java/aar.go b/java/aar.go
index 7c3840b..051715d 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -259,16 +259,16 @@
})
func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext,
- sdkLibraries dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
- aaptLibs(ctx, sdkContext, sdkLibraries)
+ aaptLibs(ctx, sdkContext, classLoaderContexts)
// App manifest file
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
- manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries,
+ manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
a.LoggingParent)
@@ -389,15 +389,15 @@
}
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, sdkLibraries dexpreopt.ClassLoaderContextMap) (
+func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) {
var sharedLibs android.Paths
- if sdkLibraries == nil {
+ if classLoaderContexts == nil {
// Not all callers need to compute class loader context, those who don't just pass nil.
// Create a temporary class loader context here (it will be computed, but not used).
- sdkLibraries = make(dexpreopt.ClassLoaderContextMap)
+ classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
}
sdkDep := decodeSdkDep(ctx, sdkContext)
@@ -426,7 +426,7 @@
// (including the java_sdk_library) itself then append any implicit sdk library
// names to the list of sdk libraries to be added to the manifest.
if component, ok := module.(SdkLibraryComponentDependency); ok {
- sdkLibraries.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(),
+ classLoaderContexts.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(), true,
component.DexJarBuildPath(), component.DexJarInstallPath())
}
@@ -439,7 +439,7 @@
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
- sdkLibraries.AddContextMap(aarDep.ExportedSdkLibs(), depName)
+ classLoaderContexts.AddContextMap(aarDep.ClassLoaderContexts(), depName)
if aarDep.ExportedAssets().Valid() {
assets = append(assets, aarDep.ExportedAssets().Path())
}
@@ -461,7 +461,7 @@
// Add nested dependencies after processing the direct dependency: if it is a <uses-library>,
// nested context is added as its subcontext, and should not be re-added at the top-level.
if dep, ok := module.(Dependency); ok {
- sdkLibraries.AddContextMap(dep.ExportedSdkLibs(), depName)
+ classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), depName)
}
})
@@ -514,8 +514,8 @@
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.aapt.isLibrary = true
- a.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
- a.aapt.buildActions(ctx, sdkContext(a), a.exportedSdkLibs)
+ a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+ a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts)
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
@@ -832,7 +832,7 @@
return nil
}
-func (a *AARImport) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
+func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return nil
}
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 6b39c35..c76bb2f 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -44,7 +44,7 @@
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
- sdkLibraries dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
var args []string
@@ -71,7 +71,7 @@
args = append(args, "--use-embedded-dex")
}
- for _, usesLib := range sdkLibraries.UsesLibs() {
+ for _, usesLib := range classLoaderContexts.UsesLibs() {
if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
args = append(args, "--optional-uses-library", usesLib)
} else {
diff --git a/java/androidmk.go b/java/androidmk.go
index c606245..386a97f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -115,7 +115,7 @@
entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
}
- entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.exportedSdkLibs.UsesLibs()...)
+ entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...)
if len(library.additionalCheckedModules) != 0 {
entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
@@ -160,6 +160,9 @@
entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", j.testProperties.Test_mainline_modules...)
+ if Bool(j.testProperties.Test_options.Unit_test) {
+ entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+ }
})
return entriesList
diff --git a/java/app.go b/java/app.go
index 9ff413c..3446739 100755
--- a/java/app.go
+++ b/java/app.go
@@ -566,7 +566,7 @@
a.aapt.splitNames = a.appProperties.Package_splits
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
- a.aapt.buildActions(ctx, sdkContext(a), a.exportedSdkLibs, aaptLinkFlags...)
+ a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -608,7 +608,7 @@
}
a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
- a.dexpreopter.classLoaderContexts = a.exportedSdkLibs
+ a.dexpreopter.classLoaderContexts = a.classLoaderContexts
a.dexpreopter.manifestFile = a.mergedManifestFile
if ctx.ModuleName() != "framework-res" {
@@ -779,7 +779,7 @@
a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
}
- a.exportedSdkLibs = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+ a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
// Process all building blocks, from AAPT to certificates.
a.aaptBuildActions(ctx)
@@ -788,7 +788,7 @@
a.usesLibrary.freezeEnforceUsesLibraries()
// Add implicit SDK libraries to <uses-library> list.
- for _, usesLib := range a.exportedSdkLibs.UsesLibs() {
+ for _, usesLib := range a.classLoaderContexts.UsesLibs() {
a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
}
@@ -890,7 +890,7 @@
if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
if dep, ok := module.(*cc.Module); ok {
- if dep.IsNdk() || dep.IsStubs() {
+ if dep.IsNdk(ctx.Config()) || dep.IsStubs() {
return false
}
@@ -1981,8 +1981,8 @@
if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
if lib, ok := m.(Dependency); ok {
- clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
- lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ExportedSdkLibs())
+ clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep, isSharedSdkLibrary(m),
+ lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep})
} else {
@@ -1995,6 +1995,11 @@
return clcMap
}
+func isSharedSdkLibrary(m android.Module) bool {
+ lib, ok := m.(SdkLibraryDependency)
+ return ok && lib.IsSharedLibrary()
+}
+
// enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs
// properties. Defaults to true if either of uses_libs or optional_uses_libs is specified. Will default to true
// unconditionally in the future.
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index d8b617e..cd395b1 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -163,7 +163,7 @@
return nil
}
-func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
+func (d *DeviceHostConverter) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return nil
}
diff --git a/java/java.go b/java/java.go
index d6dc148..a23c649 100644
--- a/java/java.go
+++ b/java/java.go
@@ -417,7 +417,7 @@
overrideManifest android.OptionalPath
// map of SDK version to class loader context
- exportedSdkLibs dexpreopt.ClassLoaderContextMap
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
// list of plugins that this java module is exporting
exportedPluginJars android.Paths
@@ -509,7 +509,7 @@
ImplementationJars() android.Paths
ResourceJars() android.Paths
AidlIncludeDirs() android.Paths
- ExportedSdkLibs() dexpreopt.ClassLoaderContextMap
+ ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
ExportedPlugins() (android.Paths, []string)
SrcJarArgs() ([]string, android.Paths)
BaseModuleName() string
@@ -1027,7 +1027,7 @@
case libTag:
deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
// names of sdk libs that are directly depended are exported
- j.exportedSdkLibs.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(),
+ j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(), true,
dep.DexJarBuildPath(), dep.DexJarInstallPath())
case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
@@ -1039,7 +1039,7 @@
case libTag, instrumentationForTag:
deps.classpath = append(deps.classpath, dep.HeaderJars()...)
// sdk lib names from dependencies are re-exported
- j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName)
+ j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1051,7 +1051,7 @@
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
// sdk lib names from dependencies are re-exported
- j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName)
+ j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1903,8 +1903,8 @@
return j.exportAidlIncludeDirs
}
-func (j *Module) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
- return j.exportedSdkLibs
+func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return j.classLoaderContexts
}
func (j *Module) ExportedPlugins() (android.Paths, []string) {
@@ -2042,7 +2042,7 @@
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
}
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
- j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
+ j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
j.compile(ctx, nil)
// Collect the module directory for IDE info in java/jdeps.go.
@@ -2062,12 +2062,12 @@
// add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest.
- j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
+ j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), true,
j.DexJarBuildPath(), j.DexJarInstallPath())
// A non-SDK library may provide a <uses-library> (the name may be different from the module name).
if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
- j.exportedSdkLibs.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
+ j.classLoaderContexts.AddContext(ctx, lib, true, j.DexJarBuildPath(), j.DexJarInstallPath())
}
j.distFiles = j.GenerateTaggedDistFiles(ctx)
@@ -2226,6 +2226,9 @@
type TestOptions struct {
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
+
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
}
type testProperties struct {
@@ -2646,7 +2649,7 @@
dexJarFile android.Path
combinedClasspathFile android.Path
- exportedSdkLibs dexpreopt.ClassLoaderContextMap
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
exportAidlIncludeDirs android.Paths
hideApexVariantFromMake bool
@@ -2721,7 +2724,7 @@
TransformJetifier(ctx, outputFile, inputFile)
}
j.combinedClasspathFile = outputFile
- j.exportedSdkLibs = make(dexpreopt.ClassLoaderContextMap)
+ j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags
@@ -2735,7 +2738,7 @@
case libTag, staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars()...)
// sdk lib names from dependencies are re-exported
- j.exportedSdkLibs.AddContextMap(dep.ExportedSdkLibs(), otherName)
+ j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
}
@@ -2744,7 +2747,7 @@
case libTag:
flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
// names of sdk libs that are directly depended are exported
- j.exportedSdkLibs.AddContext(ctx, otherName,
+ j.classLoaderContexts.AddContext(ctx, otherName, dep.IsSharedLibrary(),
dep.DexJarBuildPath(), dep.DexJarInstallPath())
}
}
@@ -2760,7 +2763,7 @@
// add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest.
- j.exportedSdkLibs.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
+ j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(), true,
outputFile, installFile)
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@@ -2843,8 +2846,8 @@
return j.exportAidlIncludeDirs
}
-func (j *Import) ExportedSdkLibs() dexpreopt.ClassLoaderContextMap {
- return j.exportedSdkLibs
+func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return j.classLoaderContexts
}
func (j *Import) ExportedPlugins() (android.Paths, []string) {
diff --git a/java/java_test.go b/java/java_test.go
index 4594b81..845a03a 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -92,8 +92,8 @@
ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
- ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
+ ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory))
+ ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory))
android.RegisterPrebuiltMutators(ctx)
@@ -1379,8 +1379,8 @@
baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
- if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() {
- t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings())
+ if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected jargen inputs [%q], got %q", w, g)
}
if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
@@ -1593,7 +1593,7 @@
// test if baz has exported SDK lib names foo and bar to qux
qux := ctx.ModuleForTests("qux", "android_common")
if quxLib, ok := qux.Module().(*Library); ok {
- sdkLibs := quxLib.ExportedSdkLibs().UsesLibs()
+ sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
t.Errorf("qux should export %q but exports %q", w, sdkLibs)
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 21c03cd..13cd5f9 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -673,6 +673,11 @@
return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
}
+// If the SDK library is a shared library.
+func (c *commonToSdkLibraryAndImport) IsSharedLibrary() bool {
+ return c.sharedLibrary()
+}
+
// The component names for different outputs of the java_sdk_library.
//
// They are similar to the names used for the child modules it creates
@@ -918,6 +923,9 @@
// jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise
// they are identical to the corresponding header jars.
SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+
+ // If the SDK library is a shared library.
+ IsSharedLibrary() bool
}
type SdkLibrary struct {
diff --git a/python/androidmk.go b/python/androidmk.go
index 8ad5889..040b6be 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -80,6 +80,10 @@
fmt.Fprintln(w, "LOCAL_TEST_DATA :=",
strings.Join(android.AndroidMkDataPaths(p.data), " "))
}
+
+ if Bool(p.testProperties.Test_options.Unit_test) {
+ fmt.Fprintln(w, "LOCAL_IS_UNIT_TEST := true")
+ }
})
base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
}
diff --git a/python/test.go b/python/test.go
index 434e71a..f9baa46 100644
--- a/python/test.go
+++ b/python/test.go
@@ -26,6 +26,12 @@
android.RegisterModuleType("python_test", PythonTestFactory)
}
+// Test option struct.
+type TestOptions struct {
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
+}
+
type TestProperties struct {
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
@@ -38,6 +44,9 @@
// list of files or filegroup modules that provide data that should be installed alongside
// the test
Data []string `android:"path,arch_variant"`
+
+ // Test options.
+ Test_options TestOptions
}
type testDecorator struct {
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 29e4bd7..f98360a 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -116,6 +116,9 @@
if !BoolDefault(test.Properties.Auto_gen_config, true) {
fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
}
+ if Bool(test.Properties.Test_options.Unit_test) {
+ fmt.Fprintln(w, "LOCAL_IS_UNIT_TEST := true")
+ }
})
// TODO(chh): add test data with androidMkWriteTestData(test.data, ctx, ret)
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 7cc0fc8..35a807b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -258,5 +258,6 @@
deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...)
+ deps.HeaderLibs = append(deps.StaticLibs, b.ClangProperties.Header_libs...)
return deps
}
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index c7ce42b..af04cfc 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -32,6 +32,7 @@
cflags: ["--clang-flag()"],
shared_libs: ["libfoo_shared"],
static_libs: ["libfoo_static"],
+ header_libs: ["libfoo_header"],
}
cc_library_shared {
name: "libfoo_shared",
@@ -41,6 +42,10 @@
name: "libfoo_static",
export_include_dirs: ["static_include"],
}
+ cc_library_headers {
+ name: "libfoo_header",
+ export_include_dirs: ["header_include"],
+ }
cc_defaults {
name: "cc_defaults_flags",
cflags: ["--default-flag"],
@@ -60,6 +65,9 @@
if !strings.Contains(libbindgen.Args["cflags"], "-Istatic_include") {
t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
}
+ if !strings.Contains(libbindgen.Args["cflags"], "-Iheader_include") {
+ t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+ }
if !strings.Contains(libbindgen.Args["cflags"], "--default-flag") {
t.Errorf("rust_bindgen missing cflags defined in cc_defaults: cflags %#v", libbindgen.Args["cflags"])
}
diff --git a/rust/builder.go b/rust/builder.go
index a09b1d1..6079e30 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -200,7 +200,9 @@
profileEmitArg := strings.TrimPrefix(cc.PwdPrefix(), "PWD=") + "/"
if outputFile.Ext() != "" {
- gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno"))
+ // rustc seems to split the output filename at the first '.' when determining the gcno filename
+ // so we need to do the same here.
+ gcnoFile = android.PathForModuleOut(ctx, strings.Split(outputFile.Base(), ".")[0]+".gcno")
rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcda")).String())
} else {
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 90155ca..e7f873e 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -160,7 +160,7 @@
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
}
if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
- !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
+ !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.gcno") {
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
}
if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 76fed30..7d6e1fd 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -53,7 +53,7 @@
Proto_flags []string `android:"arch_variant"`
// List of libraries which export include paths required for this module
- Header_libs []string `android:"arch_variant"`
+ Header_libs []string `android:"arch_variant,variant_prepend"`
}
type protobufDecorator struct {
diff --git a/rust/test.go b/rust/test.go
index bc7f53c..408e03a 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -19,6 +19,12 @@
"android/soong/tradefed"
)
+// Test option struct.
+type TestOptions struct {
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
+}
+
type TestProperties struct {
// Disables the creation of a test-specific directory when used with
// relative_install_path. Useful if several tests need to be in the same
@@ -44,6 +50,9 @@
// if set, build with the standard Rust test harness. Defaults to true.
Test_harness *bool
+
+ // Test options.
+ Test_options TestOptions
}
// A test module is a binary module with extra --test compiler flag
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
index 63fc9a9..c271211 100755
--- a/scripts/check_boot_jars/check_boot_jars.py
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -10,6 +10,7 @@
import re
import subprocess
import sys
+import xml.etree.ElementTree
# The compiled allow list RE.
@@ -38,46 +39,40 @@
return False
return True
-# Pattern that matches the class descriptor in a "Class descriptor" line output
-# by dexdump and extracts the class name - with / instead of .
-CLASS_DESCRIPTOR_RE = re.compile("'L([^;]+);'")
-
def CheckDexJar(dexdump_path, allow_list_path, jar):
"""Check a dex jar file.
"""
- # Get the class descriptor lines in the dexdump output. This filters out lines
- # that do not contain class descriptors to reduce the size of the data read by
- # this script.
- p = subprocess.Popen(args='%s %s | grep "Class descriptor "' % (dexdump_path, jar),
+ # Use dexdump to generate the XML representation of the dex jar file.
+ p = subprocess.Popen(args='%s -l xml %s' % (dexdump_path, jar),
stdout=subprocess.PIPE, shell=True)
stdout, _ = p.communicate()
if p.returncode != 0:
return False
- # Split the output into lines
- lines = stdout.split('\n')
- classes = 0
- for line in lines:
- # The last line will be empty
- if line == '':
- continue
- # Try and find the descriptor on the line. Fail immediately if it cannot be found
- # as the dexdump output has probably changed.
- found = CLASS_DESCRIPTOR_RE.search(line)
- if not found:
- print >> sys.stderr, ('Could not find class descriptor in line `%s`' % line)
- return False
- # Extract the class name (using / instead of .) from the class descriptor line
- f = found.group(1)
- classes += 1
- package_name = os.path.dirname(f)
- package_name = package_name.replace('/', '.')
+
+ packages = 0
+ try:
+ # TODO(b/172063475) - improve performance
+ root = xml.etree.ElementTree.fromstring(stdout)
+ except xml.etree.ElementTree.ParseError as e:
+ print >> sys.stderr, 'Error processing jar %s - %s' % (jar, e)
+ print >> sys.stderr, stdout
+ return False
+ for package_elt in root.iterfind('package'):
+ packages += 1
+ package_name = package_elt.get('name')
if not package_name or not allow_list_re.match(package_name):
+ # Report the name of a class in the package as it is easier to navigate to
+ # the source of a concrete class than to a package which is often required
+ # to investigate this failure.
+ class_name = package_elt[0].get('name')
+ if package_name != "":
+ class_name = package_name + "." + class_name
print >> sys.stderr, ('Error: %s contains class file %s, whose package name "%s" is empty or'
' not in the allow list %s of packages allowed on the bootclasspath.'
- % (jar, f, package_name, allow_list_path))
+ % (jar, class_name, package_name, allow_list_path))
return False
- if classes == 0:
- print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
+ if packages == 0:
+ print >> sys.stderr, ('Error: %s does not contain any packages.' % jar)
return False
return True
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
index 7cc7caf..2d36f67 100644
--- a/ui/build/bazel.go
+++ b/ui/build/bazel.go
@@ -24,8 +24,8 @@
"android/soong/ui/metrics"
)
-// Main entry point to construct the Bazel build command line, environment variables
-// and post-processing steps (e.g. converge output directories)
+// Main entry point to construct the Bazel build command line, environment
+// variables and post-processing steps (e.g. converge output directories)
func runBazel(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunBazel, "bazel")
defer ctx.EndTrace()
@@ -41,53 +41,86 @@
outputGroups = strings.Join(config.ninjaArgs, ",")
}
+ // Environment variables are the primary mechanism to pass information from
+ // soong_ui configuration or context to Bazel.
+ //
+ // Use *_NINJA variables to pass the root-relative path of the combined,
+ // kati-generated, soong-generated, and packaging Ninja files to Bazel.
+ // Bazel reads these from the lunch() repository rule.
config.environ.Set("COMBINED_NINJA", config.CombinedNinjaFile())
config.environ.Set("KATI_NINJA", config.KatiBuildNinjaFile())
config.environ.Set("PACKAGE_NINJA", config.KatiPackageNinjaFile())
config.environ.Set("SOONG_NINJA", config.SoongNinjaFile())
+ // `tools/bazel` is the default entry point for executing Bazel in the AOSP
+ // source tree.
bazelExecutable := filepath.Join("tools", "bazel")
cmd := Command(ctx, config, "bazel", bazelExecutable)
- if extra_startup_args, ok := cmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
- cmd.Args = append(cmd.Args, strings.Fields(extra_startup_args)...)
+ // Append custom startup flags to the Bazel command. Startup flags affect
+ // the Bazel server itself, and any changes to these flags would incur a
+ // restart of the server, losing much of the in-memory incrementality.
+ if extraStartupArgs, ok := cmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
+ cmd.Args = append(cmd.Args, strings.Fields(extraStartupArgs)...)
}
+ // Start constructing the `build` command.
actionName := "build"
cmd.Args = append(cmd.Args,
actionName,
+ // Use output_groups to select the set of outputs to produce from a
+ // ninja_build target.
"--output_groups="+outputGroups,
+ // Generate a performance profile
"--profile="+filepath.Join(shared.BazelMetricsFilename(config.OutDir(), actionName)),
"--slim_profile=true",
)
- if extra_build_args, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
- cmd.Args = append(cmd.Args, strings.Fields(extra_build_args)...)
+ // Append custom build flags to the Bazel command. Changes to these flags
+ // may invalidate Bazel's analysis cache.
+ if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
+ cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...)
}
+ // Append the label of the default ninja_build target.
cmd.Args = append(cmd.Args,
"//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(),
)
+ // Ensure that the PATH environment variable value used in the action
+ // environment is the restricted set computed from soong_ui, and not a
+ // user-provided one, for hermeticity reasons.
if pathEnvValue, ok := config.environ.Get("PATH"); ok {
cmd.Environment.Set("PATH", pathEnvValue)
cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
}
+
cmd.Environment.Set("DIST_DIR", config.DistDir())
cmd.Environment.Set("SHELL", "/bin/bash")
+ // Print the full command line for debugging purposes.
ctx.Println(cmd.Cmd)
+
+ // Execute the command at the root of the directory.
cmd.Dir = filepath.Join(config.OutDir(), "..")
ctx.Status.Status("Starting Bazel..")
+
+ // Execute the build command.
cmd.RunAndStreamOrFatal()
+ // Post-processing steps start here. Once the Bazel build completes, the
+ // output files are still stored in the execution root, not in $OUT_DIR.
+ // Ensure that the $OUT_DIR contains the expected set of files by symlinking
+ // the files from the execution root's output direction into $OUT_DIR.
+
// Obtain the Bazel output directory for ninja_build.
infoCmd := Command(ctx, config, "bazel", bazelExecutable)
- if extra_startup_args, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
- infoCmd.Args = append(infoCmd.Args, strings.Fields(extra_startup_args)...)
+ if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
+ infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...)
}
+ // Obtain the output directory path in the execution root.
infoCmd.Args = append(infoCmd.Args,
"info",
"output_path",
diff --git a/ui/build/config.go b/ui/build/config.go
index 641aae6..229bd5c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -703,6 +703,10 @@
return c.ninjaArgs
}
+func (c *configImpl) BazelOutDir() string {
+ return filepath.Join(c.OutDir(), "bazel")
+}
+
func (c *configImpl) SoongOutDir() string {
return filepath.Join(c.OutDir(), "soong")
}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 7a85657..2eb84ca 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -27,8 +27,10 @@
"android/soong/ui/metrics"
)
-// This file provides an interface to the Finder for use in Soong UI
-// This file stores configuration information about which files to find
+// This file provides an interface to the Finder type for soong_ui. Finder is
+// used to recursively traverse the source tree to gather paths of files, such
+// as Android.bp or Android.mk, and store the lists/database of paths in files
+// under `$OUT_DIR/.module_paths`. This directory can also be dist'd.
// NewSourceFinder returns a new Finder configured to search for source files.
// Callers of NewSourceFinder should call <f.Shutdown()> when done
@@ -36,14 +38,18 @@
ctx.BeginTrace(metrics.RunSetupTool, "find modules")
defer ctx.EndTrace()
+ // Set up the working directory for the Finder.
dir, err := os.Getwd()
if err != nil {
ctx.Fatalf("No working directory for module-finder: %v", err.Error())
}
filesystem := fs.OsFs
- // if the root dir is ignored, then the subsequent error messages are very confusing,
- // so check for that upfront
+ // .out-dir and .find-ignore are markers for Finder to ignore siblings and
+ // subdirectories of the directory Finder finds them in, hence stopping the
+ // search recursively down those branches. It's possible that these files
+ // are in the root directory, and if they are, then the subsequent error
+ // messages are very confusing, so check for that here.
pruneFiles := []string{".out-dir", ".find-ignore"}
for _, name := range pruneFiles {
prunePath := filepath.Join(dir, name)
@@ -53,22 +59,34 @@
}
}
+ // Set up configuration parameters for the Finder cache.
cacheParams := finder.CacheParams{
WorkingDirectory: dir,
RootDirs: []string{"."},
ExcludeDirs: []string{".git", ".repo"},
PruneFiles: pruneFiles,
IncludeFiles: []string{
+ // Kati build definitions.
"Android.mk",
+ // Product configuration files.
"AndroidProducts.mk",
+ // General Soong build definitions, using the Blueprint syntax.
"Android.bp",
+ // build/blueprint build definitions, using the Blueprint syntax.
"Blueprints",
+ // Bazel build definitions.
"BUILD.bazel",
+ // Kati clean definitions.
"CleanSpec.mk",
+ // Ownership definition.
"OWNERS",
+ // Test configuration for modules in directories that contain this
+ // file.
"TEST_MAPPING",
+ // Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
},
+ // Bazel Starlark configuration files.
IncludeSuffixes: []string{".bzl"},
}
dumpDir := config.FileListDir()
@@ -80,6 +98,7 @@
return f
}
+// Finds the list of Bazel-related files (BUILD, WORKSPACE and Starlark) in the tree.
func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
matches := []string{}
for _, foundName := range entries.FileNames {
@@ -98,12 +117,21 @@
dumpDir := config.FileListDir()
os.MkdirAll(dumpDir, 0777)
+ // Stop searching a subdirectory recursively after finding an Android.mk.
androidMks := f.FindFirstNamedAt(".", "Android.mk")
err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
}
+ // Stop searching a subdirectory recursively after finding a CleanSpec.mk.
+ cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
+ err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
+ if err != nil {
+ ctx.Fatalf("Could not export module list: %v", err)
+ }
+
+ // Only consider AndroidProducts.mk in device/, vendor/ and product/, recursively in these directories.
androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
@@ -112,31 +140,30 @@
ctx.Fatalf("Could not export product list: %v", err)
}
+ // Recursively look for all Bazel related files.
bazelFiles := f.FindMatching(".", findBazelFiles)
err = dumpListToFile(ctx, config, bazelFiles, filepath.Join(dumpDir, "bazel.list"))
if err != nil {
ctx.Fatalf("Could not export bazel BUILD list: %v", err)
}
- cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
- err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
- if err != nil {
- ctx.Fatalf("Could not export module list: %v", err)
- }
-
+ // Recursively look for all OWNERS files.
owners := f.FindNamedAt(".", "OWNERS")
err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list"))
if err != nil {
ctx.Fatalf("Could not find OWNERS: %v", err)
}
+ // Recursively look for all TEST_MAPPING files.
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
if err != nil {
ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
}
+ // Recursively look for all Android.bp files
androidBps := f.FindNamedAt(".", "Android.bp")
+ // The files are named "Blueprints" only in the build/blueprint directory.
androidBps = append(androidBps, f.FindNamedAt("build/blueprint", "Blueprints")...)
if len(androidBps) == 0 {
ctx.Fatalf("No Android.bp found")
@@ -148,10 +175,12 @@
if config.Dist() {
f.WaitForDbDump()
+ // Dist the files.db plain text database.
distFile(ctx, config, f.DbPath, "module_paths")
}
}
+// Write the .list files to disk.
func dumpListToFile(ctx Context, config Config, list []string, filePath string) (err error) {
desiredText := strings.Join(list, "\n")
desiredBytes := []byte(desiredText)
diff --git a/ui/build/soong.go b/ui/build/soong.go
index b20237c..bb5cbf0 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -133,6 +133,13 @@
"-j", strconv.Itoa(config.Parallel()),
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
+
+ // For Bazel mixed builds.
+ cmd.Environment.Set("BAZEL_PATH", "./tools/bazel")
+ cmd.Environment.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
+ cmd.Environment.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
+ cmd.Environment.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
+
cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
cmd.Sandbox = soongSandbox
cmd.RunAndStreamOrFatal()
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index d603586..fc976f6 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -12,6 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// soong_zip is a utility used during the build to create a zip archive by pulling the entries from
+// various sources:
+// * explicitly specified files
+// * files whose paths are read from a file
+// * directories traversed recursively
+// It can optionally change the recorded path of an entry.
+
package main
import (
diff --git a/zip/zip.go b/zip/zip.go
index e27432c..cb85f5c 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -126,6 +126,7 @@
return b
}
+// List reads the file names from the given file and adds them to the source files list.
func (b *FileArgsBuilder) List(name string) *FileArgsBuilder {
if b.err != nil {
return b
@@ -150,6 +151,7 @@
return b
}
+// RspFile reads the file names from given .rsp file and adds them to the source files list.
func (b *FileArgsBuilder) RspFile(name string) *FileArgsBuilder {
if b.err != nil {
return b
@@ -291,7 +293,7 @@
return args
}
-func ZipTo(args ZipArgs, w io.Writer) error {
+func zipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
}
@@ -392,6 +394,7 @@
return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SrcJar, args.NumParallelJobs)
}
+// Zip creates an output zip archive from given sources.
func Zip(args ZipArgs) error {
if args.OutputFilePath == "" {
return fmt.Errorf("output file path must be nonempty")
@@ -416,7 +419,7 @@
out = f
}
- err := ZipTo(args, out)
+ err := zipTo(args, out)
if err != nil {
return err
}
@@ -450,7 +453,6 @@
RelativeRoot: fa.SourcePrefixToStrip,
}
}
-
}
dest = filepath.Join(fa.PathPrefixInZip, dest)
@@ -465,10 +467,9 @@
}
func jarSort(mappings []pathMapping) {
- less := func(i int, j int) (smaller bool) {
+ sort.SliceStable(mappings, func(i int, j int) bool {
return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
- }
- sort.SliceStable(mappings, less)
+ })
}
func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar, srcJar bool,
@@ -709,7 +710,7 @@
}
}
-func (z *ZipWriter) addManifest(dest string, src string, method uint16) error {
+func (z *ZipWriter) addManifest(dest string, src string, _ uint16) error {
if prev, exists := z.createdDirs[dest]; exists {
return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
}
@@ -963,7 +964,7 @@
dir = filepath.Clean(dir)
// discover any uncreated directories in the path
- zipDirs := []string{}
+ var zipDirs []string
for dir != "" && dir != "." {
if _, exists := z.createdDirs[dir]; exists {
break
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 302a749..a16e092 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -442,7 +442,7 @@
args.Stderr = &bytes.Buffer{}
buf := &bytes.Buffer{}
- err := ZipTo(args, buf)
+ err := zipTo(args, buf)
if (err != nil) != (test.err != nil) {
t.Fatalf("want error %v, got %v", test.err, err)
@@ -627,7 +627,7 @@
args.Stderr = &bytes.Buffer{}
buf := &bytes.Buffer{}
- err := ZipTo(args, buf)
+ err := zipTo(args, buf)
if err != nil {
t.Fatalf("got error %v", err)
}