Merge "Revert "Revert "Switch to toybox timeout."""
diff --git a/Android.bp b/Android.bp
index 4db98f8..fff17ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -78,10 +78,12 @@
"android/env.go",
],
testSrcs: [
+ "android/android_test.go",
"android/arch_test.go",
"android/config_test.go",
"android/expand_test.go",
"android/module_test.go",
+ "android/mutator_test.go",
"android/namespace_test.go",
"android/neverallow_test.go",
"android/onceper_test.go",
@@ -294,6 +296,7 @@
"java/jdeps_test.go",
"java/kotlin_test.go",
"java/plugin_test.go",
+ "java/robolectric_test.go",
"java/sdk_test.go",
],
pluginFor: ["soong_build"],
diff --git a/android/android_test.go b/android/android_test.go
new file mode 100644
index 0000000..46b7054
--- /dev/null
+++ b/android/android_test.go
@@ -0,0 +1,46 @@
+// Copyright 2019 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 (
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_android_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
diff --git a/android/api_levels.go b/android/api_levels.go
index 51d4703..961685a 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -85,7 +85,7 @@
// * Numeric API levels are simply converted.
// * "minimum" and "current" are not currently handled since the former is
// NDK specific and the latter has inconsistent meaning.
-func ApiStrToNum(ctx BaseContext, apiLevel string) (int, error) {
+func ApiStrToNum(ctx BaseModuleContext, apiLevel string) (int, error) {
num, ok := getApiLevelsMap(ctx.Config())[apiLevel]
if ok {
return num, nil
diff --git a/android/arch.go b/android/arch.go
index 04eb1e2..46e582c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -713,7 +713,7 @@
// If host is supported for the module, the Host and HostCross OsClasses are selected. If device is supported
// for the module, the Device OsClass is selected.
// Within each selected OsClass, the multilib selection is determined by:
-// - The compile_multilib property if it set (which may be overriden by target.android.compile_multlib or
+// - The compile_multilib property if it set (which may be overridden by target.android.compile_multilib or
// target.host.compile_multilib).
// - The default multilib passed to InitAndroidArchModule if compile_multilib was not set.
// Valid multilib values include:
@@ -1129,7 +1129,7 @@
var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
-func (a *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
+func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
src = src.FieldByName(field)
@@ -1167,16 +1167,16 @@
}
// Rewrite the module's properties structs to contain arch-specific values.
-func (a *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
- arch := a.Arch()
- os := a.Os()
+func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
+ arch := m.Arch()
+ os := m.Os()
- for i := range a.generalProperties {
- genProps := a.generalProperties[i]
- if a.archProperties[i] == nil {
+ for i := range m.generalProperties {
+ genProps := m.generalProperties[i]
+ if m.archProperties[i] == nil {
continue
}
- for _, archProperties := range a.archProperties[i] {
+ for _, archProperties := range m.archProperties[i] {
archPropValues := reflect.ValueOf(archProperties).Elem()
archProp := archPropValues.FieldByName("Arch")
@@ -1197,7 +1197,7 @@
if arch.ArchType != Common {
field := proptools.FieldNameForProperty(t.Name)
prefix := "arch." + t.Name
- archStruct := a.appendProperties(ctx, genProps, archProp, field, prefix)
+ archStruct := m.appendProperties(ctx, genProps, archProp, field, prefix)
// Handle arch-variant-specific properties in the form:
// arch: {
@@ -1209,7 +1209,7 @@
if v != "" {
field := proptools.FieldNameForProperty(v)
prefix := "arch." + t.Name + "." + v
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ m.appendProperties(ctx, genProps, archStruct, field, prefix)
}
// Handle cpu-variant-specific properties in the form:
@@ -1223,7 +1223,7 @@
if c != "" {
field := proptools.FieldNameForProperty(c)
prefix := "arch." + t.Name + "." + c
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ m.appendProperties(ctx, genProps, archStruct, field, prefix)
}
}
@@ -1236,7 +1236,7 @@
for _, feature := range arch.ArchFeatures {
field := proptools.FieldNameForProperty(feature)
prefix := "arch." + t.Name + "." + feature
- a.appendProperties(ctx, genProps, archStruct, field, prefix)
+ m.appendProperties(ctx, genProps, archStruct, field, prefix)
}
// Handle multilib-specific properties in the form:
@@ -1247,7 +1247,7 @@
// },
field = proptools.FieldNameForProperty(t.Multilib)
prefix = "multilib." + t.Multilib
- a.appendProperties(ctx, genProps, multilibProp, field, prefix)
+ m.appendProperties(ctx, genProps, multilibProp, field, prefix)
}
// Handle host-specific properties in the form:
@@ -1259,7 +1259,7 @@
if os.Class == Host || os.Class == HostCross {
field = "Host"
prefix = "target.host"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
// Handle target OS generalities of the form:
@@ -1274,24 +1274,24 @@
if os.Linux() {
field = "Linux"
prefix = "target.linux"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
if arch.ArchType != Common {
field = "Linux_" + arch.ArchType.Name
prefix = "target.linux_" + arch.ArchType.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
}
if os.Bionic() {
field = "Bionic"
prefix = "target.bionic"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
if arch.ArchType != Common {
field = "Bionic_" + t.Name
prefix = "target.bionic_" + t.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
}
@@ -1321,18 +1321,18 @@
// },
field = os.Field
prefix = "target." + os.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
if arch.ArchType != Common {
field = os.Field + "_" + t.Name
prefix = "target." + os.Name + "_" + t.Name
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
if (os.Class == Host || os.Class == HostCross) && os != Windows {
field := "Not_windows"
prefix := "target.not_windows"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
// Handle 64-bit device properties in the form:
@@ -1352,11 +1352,11 @@
if ctx.Config().Android64() {
field := "Android64"
prefix := "target.android64"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
} else {
field := "Android32"
prefix := "target.android32"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
if (arch.ArchType == X86 && (hasArmAbi(arch) ||
@@ -1365,7 +1365,7 @@
hasX86AndroidArch(ctx.Config().Targets[Android])) {
field := "Arm_on_x86"
prefix := "target.arm_on_x86"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
if (arch.ArchType == X86_64 && (hasArmAbi(arch) ||
hasArmAndroidArch(ctx.Config().Targets[Android]))) ||
@@ -1373,7 +1373,7 @@
hasX8664AndroidArch(ctx.Config().Targets[Android])) {
field := "Arm_on_x86_64"
prefix := "target.arm_on_x86_64"
- a.appendProperties(ctx, genProps, targetProp, field, prefix)
+ m.appendProperties(ctx, genProps, targetProp, field, prefix)
}
}
}
diff --git a/android/defaults.go b/android/defaults.go
index d4fbf48..844b4d4 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -80,6 +80,9 @@
return d.defaultableProperties
}
+func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
func InitDefaultsModule(module DefaultableModule) {
module.AddProperties(
&hostAndDeviceProperties{},
diff --git a/android/defaults_test.go b/android/defaults_test.go
new file mode 100644
index 0000000..fa26595
--- /dev/null
+++ b/android/defaults_test.go
@@ -0,0 +1,116 @@
+// Copyright 2019 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"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type defaultsTestProperties struct {
+ Foo []string
+}
+
+type defaultsTestModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties defaultsTestProperties
+}
+
+func (d *defaultsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: PathForModuleOut(ctx, "out"),
+ })
+}
+
+func defaultsTestModuleFactory() Module {
+ module := &defaultsTestModule{}
+ module.AddProperties(&module.properties)
+ InitDefaultableModule(module)
+ InitAndroidModule(module)
+ return module
+}
+
+type defaultsTestDefaults struct {
+ ModuleBase
+ DefaultsModuleBase
+}
+
+func defaultsTestDefaultsFactory() Module {
+ defaults := &defaultsTestDefaults{}
+ defaults.AddProperties(&defaultsTestProperties{})
+ InitDefaultsModule(defaults)
+ return defaults
+}
+
+func TestDefaultsAllowMissingDependencies(t *testing.T) {
+ config := TestConfig(buildDir, nil)
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+
+ ctx := NewTestContext()
+ ctx.SetAllowMissingDependencies(true)
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(defaultsTestModuleFactory))
+ ctx.RegisterModuleType("defaults", ModuleFactoryAdaptor(defaultsTestDefaultsFactory))
+
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+
+ ctx.Register()
+
+ bp := `
+ defaults {
+ name: "defaults",
+ defaults: ["missing"],
+ foo: ["defaults"],
+ }
+
+ test {
+ name: "missing_defaults",
+ defaults: ["missing"],
+ foo: ["module"],
+ }
+
+ test {
+ name: "missing_transitive_defaults",
+ defaults: ["defaults"],
+ foo: ["module"],
+ }
+ `
+
+ ctx.MockFileSystem(map[string][]byte{
+ "Android.bp": []byte(bp),
+ })
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ missingDefaults := ctx.ModuleForTests("missing_defaults", "").Output("out")
+ missingTransitiveDefaults := ctx.ModuleForTests("missing_transitive_defaults", "").Output("out")
+
+ if missingDefaults.Rule != ErrorRule {
+ t.Errorf("expected missing_defaults rule to be ErrorRule, got %#v", missingDefaults.Rule)
+ }
+
+ if g, w := missingDefaults.Args["error"], "module missing_defaults missing dependencies: missing\n"; g != w {
+ t.Errorf("want error %q, got %q", w, g)
+ }
+
+ // TODO: missing transitive defaults is currently not handled
+ _ = missingTransitiveDefaults
+}
diff --git a/android/hooks.go b/android/hooks.go
index d55678e..2d2f797 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -27,7 +27,7 @@
// been applied.
type LoadHookContext interface {
// TODO: a new context that includes Config() but not Target(), etc.?
- BaseContext
+ BaseModuleContext
AppendProperties(...interface{})
PrependProperties(...interface{})
CreateModule(blueprint.ModuleFactory, ...interface{})
@@ -36,7 +36,7 @@
// Arch hooks are run after the module has been split into architecture variants, and can be used
// to add architecture-specific properties.
type ArchHookContext interface {
- BaseContext
+ BaseModuleContext
AppendProperties(...interface{})
PrependProperties(...interface{})
}
@@ -129,18 +129,18 @@
func LoadHookMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(Module); ok {
- // Cast through *androidTopDownMutatorContext because AppendProperties is implemented
- // on *androidTopDownMutatorContext but not exposed through TopDownMutatorContext
- var loadHookCtx LoadHookContext = ctx.(*androidTopDownMutatorContext)
+ // Cast through *topDownMutatorContext because AppendProperties is implemented
+ // on *topDownMutatorContext but not exposed through TopDownMutatorContext
+ var loadHookCtx LoadHookContext = ctx.(*topDownMutatorContext)
m.base().hooks.runLoadHooks(loadHookCtx, m.base())
}
}
func archHookMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(Module); ok {
- // Cast through *androidTopDownMutatorContext because AppendProperties is implemented
- // on *androidTopDownMutatorContext but not exposed through TopDownMutatorContext
- var archHookCtx ArchHookContext = ctx.(*androidTopDownMutatorContext)
+ // Cast through *topDownMutatorContext because AppendProperties is implemented
+ // on *topDownMutatorContext but not exposed through TopDownMutatorContext
+ var archHookCtx ArchHookContext = ctx.(*topDownMutatorContext)
m.base().hooks.runArchHooks(archHookCtx, m.base())
}
}
diff --git a/android/module.go b/android/module.go
index eb9b0fc..87e2ca7 100644
--- a/android/module.go
+++ b/android/module.go
@@ -18,7 +18,6 @@
"fmt"
"path"
"path/filepath"
- "sort"
"strings"
"text/scanner"
@@ -56,7 +55,61 @@
type ModuleBuildParams BuildParams
-type androidBaseContext interface {
+// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
+// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module
+// instead of a blueprint.Module, plus some extra methods that return Android-specific information
+// about the current module.
+type BaseModuleContext interface {
+ Module() Module
+ ModuleName() string
+ ModuleDir() string
+ ModuleType() string
+ Config() Config
+
+ OtherModuleName(m blueprint.Module) string
+ OtherModuleDir(m blueprint.Module) string
+ OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
+ OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
+ OtherModuleExists(name string) bool
+
+ GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
+ GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
+ GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+
+ VisitDirectDepsBlueprint(visit func(blueprint.Module))
+ VisitDirectDeps(visit func(Module))
+ VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
+ VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
+ VisitDepsDepthFirst(visit func(Module))
+ // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
+ VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
+ WalkDeps(visit func(Module, Module) bool)
+ WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
+ // GetWalkPath is supposed to be called in visit function passed in WalkDeps()
+ // and returns a top-down dependency path from a start module to current child module.
+ GetWalkPath() []Module
+
+ ContainsProperty(name string) bool
+ Errorf(pos scanner.Position, fmt string, args ...interface{})
+ ModuleErrorf(fmt string, args ...interface{})
+ PropertyErrorf(property, fmt string, args ...interface{})
+ Failed() bool
+
+ // GlobWithDeps returns a list of files that match the specified pattern but do not match any
+ // of the patterns in excludes. It also adds efficient dependencies to rerun the primary
+ // builder whenever a file matching the pattern as added or removed, without rerunning if a
+ // file that does not match the pattern is added to a searched directory.
+ GlobWithDeps(pattern string, excludes []string) ([]string, error)
+
+ Glob(globPattern string, excludes []string) Paths
+ GlobFiles(globPattern string, excludes []string) Paths
+
+ Fs() pathtools.FileSystem
+ AddNinjaFileDeps(deps ...string)
+
+ AddMissingDependencies(missingDeps []string)
+
Target() Target
TargetPrimary() bool
MultiTargets() []Target
@@ -78,37 +131,12 @@
DeviceConfig() DeviceConfig
}
+// Deprecated: use BaseModuleContext instead
type BaseContext interface {
BaseModuleContext
- androidBaseContext
-}
-
-// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
-// a Config instead of an interface{}.
-type BaseModuleContext interface {
- ModuleName() string
- ModuleDir() string
- ModuleType() string
- Config() Config
-
- ContainsProperty(name string) bool
- Errorf(pos scanner.Position, fmt string, args ...interface{})
- ModuleErrorf(fmt string, args ...interface{})
- PropertyErrorf(property, fmt string, args ...interface{})
- Failed() bool
-
- // GlobWithDeps returns a list of files that match the specified pattern but do not match any
- // of the patterns in excludes. It also adds efficient dependencies to rerun the primary
- // builder whenever a file matching the pattern as added or removed, without rerunning if a
- // file that does not match the pattern is added to a searched directory.
- GlobWithDeps(pattern string, excludes []string) ([]string, error)
-
- Fs() pathtools.FileSystem
- AddNinjaFileDeps(deps ...string)
}
type ModuleContext interface {
- androidBaseContext
BaseModuleContext
// Deprecated: use ModuleContext.Build instead.
@@ -117,8 +145,6 @@
ExpandSources(srcFiles, excludes []string) Paths
ExpandSource(srcFile, prop string) Path
ExpandOptionalSource(srcFile *string, prop string) OptionalPath
- Glob(globPattern string, excludes []string) Paths
- GlobFiles(globPattern string, excludes []string) Paths
InstallExecutable(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
InstallFile(installPath OutputPath, name string, srcPath Path, deps ...Path) OutputPath
@@ -126,8 +152,6 @@
InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath
CheckbuildFile(srcPath Path)
- AddMissingDependencies(deps []string)
-
InstallInData() bool
InstallInSanitizerDir() bool
InstallInRecovery() bool
@@ -136,30 +160,8 @@
HostRequiredModuleNames() []string
TargetRequiredModuleNames() []string
- // android.ModuleContext methods
- // These are duplicated instead of embedded so that can eventually be wrapped to take an
- // android.Module instead of a blueprint.Module
- OtherModuleName(m blueprint.Module) string
- OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
- OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
-
- GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
- GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
- GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
-
ModuleSubDir() string
- VisitDirectDepsBlueprint(visit func(blueprint.Module))
- VisitDirectDeps(visit func(Module))
- VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
- VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
- // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
- VisitDepsDepthFirst(visit func(Module))
- // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module
- VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
- WalkDeps(visit func(Module, Module) bool)
- WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool)
-
Variable(pctx PackageContext, name, value string)
Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
// Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string,
@@ -341,6 +343,8 @@
SkipInstall bool `blueprint:"mutated"`
NamespaceExportedToMake bool `blueprint:"mutated"`
+
+ MissingDeps []string `blueprint:"mutated"`
}
type hostAndDeviceProperties struct {
@@ -457,9 +461,9 @@
// The ModuleBase type is responsible for implementing the GenerateBuildActions
// method to support the blueprint.Module interface. This method will then call
// the module's GenerateAndroidBuildActions method once for each build variant
-// that is to be built. GenerateAndroidBuildActions is passed a
-// AndroidModuleContext rather than the usual blueprint.ModuleContext.
-// AndroidModuleContext exposes extra functionality specific to the Android build
+// that is to be built. GenerateAndroidBuildActions is passed a ModuleContext
+// rather than the usual blueprint.ModuleContext.
+// ModuleContext exposes extra functionality specific to the Android build
// system including details about the particular build variant that is to be
// generated.
//
@@ -526,83 +530,83 @@
prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool
}
-func (a *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
+func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
-func (a *ModuleBase) AddProperties(props ...interface{}) {
- a.registerProps = append(a.registerProps, props...)
+func (m *ModuleBase) AddProperties(props ...interface{}) {
+ m.registerProps = append(m.registerProps, props...)
}
-func (a *ModuleBase) GetProperties() []interface{} {
- return a.registerProps
+func (m *ModuleBase) GetProperties() []interface{} {
+ return m.registerProps
}
-func (a *ModuleBase) BuildParamsForTests() []BuildParams {
- return a.buildParams
+func (m *ModuleBase) BuildParamsForTests() []BuildParams {
+ return m.buildParams
}
-func (a *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
- return a.ruleParams
+func (m *ModuleBase) RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams {
+ return m.ruleParams
}
-func (a *ModuleBase) VariablesForTests() map[string]string {
- return a.variables
+func (m *ModuleBase) VariablesForTests() map[string]string {
+ return m.variables
}
-func (a *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool) {
- a.prefer32 = prefer32
+func (m *ModuleBase) Prefer32(prefer32 func(ctx BaseModuleContext, base *ModuleBase, class OsClass) bool) {
+ m.prefer32 = prefer32
}
// Name returns the name of the module. It may be overridden by individual module types, for
// example prebuilts will prepend prebuilt_ to the name.
-func (a *ModuleBase) Name() string {
- return String(a.nameProperties.Name)
+func (m *ModuleBase) Name() string {
+ return String(m.nameProperties.Name)
}
// BaseModuleName returns the name of the module as specified in the blueprints file.
-func (a *ModuleBase) BaseModuleName() string {
- return String(a.nameProperties.Name)
+func (m *ModuleBase) BaseModuleName() string {
+ return String(m.nameProperties.Name)
}
-func (a *ModuleBase) base() *ModuleBase {
- return a
+func (m *ModuleBase) base() *ModuleBase {
+ return m
}
-func (a *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
- a.commonProperties.CompileTarget = target
- a.commonProperties.CompileMultiTargets = multiTargets
- a.commonProperties.CompilePrimary = primary
+func (m *ModuleBase) SetTarget(target Target, multiTargets []Target, primary bool) {
+ m.commonProperties.CompileTarget = target
+ m.commonProperties.CompileMultiTargets = multiTargets
+ m.commonProperties.CompilePrimary = primary
}
-func (a *ModuleBase) Target() Target {
- return a.commonProperties.CompileTarget
+func (m *ModuleBase) Target() Target {
+ return m.commonProperties.CompileTarget
}
-func (a *ModuleBase) TargetPrimary() bool {
- return a.commonProperties.CompilePrimary
+func (m *ModuleBase) TargetPrimary() bool {
+ return m.commonProperties.CompilePrimary
}
-func (a *ModuleBase) MultiTargets() []Target {
- return a.commonProperties.CompileMultiTargets
+func (m *ModuleBase) MultiTargets() []Target {
+ return m.commonProperties.CompileMultiTargets
}
-func (a *ModuleBase) Os() OsType {
- return a.Target().Os
+func (m *ModuleBase) Os() OsType {
+ return m.Target().Os
}
-func (a *ModuleBase) Host() bool {
- return a.Os().Class == Host || a.Os().Class == HostCross
+func (m *ModuleBase) Host() bool {
+ return m.Os().Class == Host || m.Os().Class == HostCross
}
-func (a *ModuleBase) Arch() Arch {
- return a.Target().Arch
+func (m *ModuleBase) Arch() Arch {
+ return m.Target().Arch
}
-func (a *ModuleBase) ArchSpecific() bool {
- return a.commonProperties.ArchSpecific
+func (m *ModuleBase) ArchSpecific() bool {
+ return m.commonProperties.ArchSpecific
}
-func (a *ModuleBase) OsClassSupported() []OsClass {
- switch a.commonProperties.HostOrDeviceSupported {
+func (m *ModuleBase) OsClassSupported() []OsClass {
+ switch m.commonProperties.HostOrDeviceSupported {
case HostSupported:
return []OsClass{Host, HostCross}
case HostSupportedNoCross:
@@ -611,13 +615,13 @@
return []OsClass{Device}
case HostAndDeviceSupported, HostAndDeviceDefault:
var supported []OsClass
- if Bool(a.hostAndDeviceProperties.Host_supported) ||
- (a.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
- a.hostAndDeviceProperties.Host_supported == nil) {
+ if Bool(m.hostAndDeviceProperties.Host_supported) ||
+ (m.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
+ m.hostAndDeviceProperties.Host_supported == nil) {
supported = append(supported, Host, HostCross)
}
- if a.hostAndDeviceProperties.Device_supported == nil ||
- *a.hostAndDeviceProperties.Device_supported {
+ if m.hostAndDeviceProperties.Device_supported == nil ||
+ *m.hostAndDeviceProperties.Device_supported {
supported = append(supported, Device)
}
return supported
@@ -626,49 +630,49 @@
}
}
-func (a *ModuleBase) DeviceSupported() bool {
- return a.commonProperties.HostOrDeviceSupported == DeviceSupported ||
- a.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
- (a.hostAndDeviceProperties.Device_supported == nil ||
- *a.hostAndDeviceProperties.Device_supported)
+func (m *ModuleBase) DeviceSupported() bool {
+ return m.commonProperties.HostOrDeviceSupported == DeviceSupported ||
+ m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
+ (m.hostAndDeviceProperties.Device_supported == nil ||
+ *m.hostAndDeviceProperties.Device_supported)
}
-func (a *ModuleBase) Platform() bool {
- return !a.DeviceSpecific() && !a.SocSpecific() && !a.ProductSpecific() && !a.ProductServicesSpecific()
+func (m *ModuleBase) Platform() bool {
+ return !m.DeviceSpecific() && !m.SocSpecific() && !m.ProductSpecific() && !m.ProductServicesSpecific()
}
-func (a *ModuleBase) DeviceSpecific() bool {
- return Bool(a.commonProperties.Device_specific)
+func (m *ModuleBase) DeviceSpecific() bool {
+ return Bool(m.commonProperties.Device_specific)
}
-func (a *ModuleBase) SocSpecific() bool {
- return Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
+func (m *ModuleBase) SocSpecific() bool {
+ return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
}
-func (a *ModuleBase) ProductSpecific() bool {
- return Bool(a.commonProperties.Product_specific)
+func (m *ModuleBase) ProductSpecific() bool {
+ return Bool(m.commonProperties.Product_specific)
}
-func (a *ModuleBase) ProductServicesSpecific() bool {
- return Bool(a.commonProperties.Product_services_specific)
+func (m *ModuleBase) ProductServicesSpecific() bool {
+ return Bool(m.commonProperties.Product_services_specific)
}
-func (a *ModuleBase) Enabled() bool {
- if a.commonProperties.Enabled == nil {
- return !a.Os().DefaultDisabled
+func (m *ModuleBase) Enabled() bool {
+ if m.commonProperties.Enabled == nil {
+ return !m.Os().DefaultDisabled
}
- return *a.commonProperties.Enabled
+ return *m.commonProperties.Enabled
}
-func (a *ModuleBase) SkipInstall() {
- a.commonProperties.SkipInstall = true
+func (m *ModuleBase) SkipInstall() {
+ m.commonProperties.SkipInstall = true
}
-func (a *ModuleBase) ExportedToMake() bool {
- return a.commonProperties.NamespaceExportedToMake
+func (m *ModuleBase) ExportedToMake() bool {
+ return m.commonProperties.NamespaceExportedToMake
}
-func (a *ModuleBase) computeInstallDeps(
+func (m *ModuleBase) computeInstallDeps(
ctx blueprint.ModuleContext) Paths {
result := Paths{}
@@ -683,35 +687,35 @@
return result
}
-func (a *ModuleBase) filesToInstall() Paths {
- return a.installFiles
+func (m *ModuleBase) filesToInstall() Paths {
+ return m.installFiles
}
-func (p *ModuleBase) NoAddressSanitizer() bool {
- return p.noAddressSanitizer
+func (m *ModuleBase) NoAddressSanitizer() bool {
+ return m.noAddressSanitizer
}
-func (p *ModuleBase) InstallInData() bool {
+func (m *ModuleBase) InstallInData() bool {
return false
}
-func (p *ModuleBase) InstallInSanitizerDir() bool {
+func (m *ModuleBase) InstallInSanitizerDir() bool {
return false
}
-func (p *ModuleBase) InstallInRecovery() bool {
- return Bool(p.commonProperties.Recovery)
+func (m *ModuleBase) InstallInRecovery() bool {
+ return Bool(m.commonProperties.Recovery)
}
-func (a *ModuleBase) Owner() string {
- return String(a.commonProperties.Owner)
+func (m *ModuleBase) Owner() string {
+ return String(m.commonProperties.Owner)
}
-func (a *ModuleBase) NoticeFile() OptionalPath {
- return a.noticeFile
+func (m *ModuleBase) NoticeFile() OptionalPath {
+ return m.noticeFile
}
-func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
+func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
allInstalledFiles := Paths{}
allCheckbuildFiles := Paths{}
ctx.VisitAllModuleVariants(func(module Module) {
@@ -736,7 +740,7 @@
Default: !ctx.Config().EmbeddedInMake(),
})
deps = append(deps, name)
- a.installTarget = name
+ m.installTarget = name
}
if len(allCheckbuildFiles) > 0 {
@@ -747,7 +751,7 @@
Implicits: allCheckbuildFiles,
})
deps = append(deps, name)
- a.checkbuildTarget = name
+ m.checkbuildTarget = name
}
if len(deps) > 0 {
@@ -763,26 +767,26 @@
Implicits: deps,
})
- a.blueprintDir = ctx.ModuleDir()
+ m.blueprintDir = ctx.ModuleDir()
}
}
-func determineModuleKind(a *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
- var socSpecific = Bool(a.commonProperties.Vendor) || Bool(a.commonProperties.Proprietary) || Bool(a.commonProperties.Soc_specific)
- var deviceSpecific = Bool(a.commonProperties.Device_specific)
- var productSpecific = Bool(a.commonProperties.Product_specific)
- var productServicesSpecific = Bool(a.commonProperties.Product_services_specific)
+func determineModuleKind(m *ModuleBase, ctx blueprint.BaseModuleContext) moduleKind {
+ var socSpecific = Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Proprietary) || Bool(m.commonProperties.Soc_specific)
+ var deviceSpecific = Bool(m.commonProperties.Device_specific)
+ var productSpecific = Bool(m.commonProperties.Product_specific)
+ var productServicesSpecific = Bool(m.commonProperties.Product_services_specific)
msg := "conflicting value set here"
if socSpecific && deviceSpecific {
ctx.PropertyErrorf("device_specific", "a module cannot be specific to SoC and device at the same time.")
- if Bool(a.commonProperties.Vendor) {
+ if Bool(m.commonProperties.Vendor) {
ctx.PropertyErrorf("vendor", msg)
}
- if Bool(a.commonProperties.Proprietary) {
+ if Bool(m.commonProperties.Proprietary) {
ctx.PropertyErrorf("proprietary", msg)
}
- if Bool(a.commonProperties.Soc_specific) {
+ if Bool(m.commonProperties.Soc_specific) {
ctx.PropertyErrorf("soc_specific", msg)
}
}
@@ -801,13 +805,13 @@
if deviceSpecific {
ctx.PropertyErrorf("device_specific", msg)
} else {
- if Bool(a.commonProperties.Vendor) {
+ if Bool(m.commonProperties.Vendor) {
ctx.PropertyErrorf("vendor", msg)
}
- if Bool(a.commonProperties.Proprietary) {
+ if Bool(m.commonProperties.Proprietary) {
ctx.PropertyErrorf("proprietary", msg)
}
- if Bool(a.commonProperties.Soc_specific) {
+ if Bool(m.commonProperties.Soc_specific) {
ctx.PropertyErrorf("soc_specific", msg)
}
}
@@ -826,27 +830,36 @@
}
}
-func (a *ModuleBase) androidBaseContextFactory(ctx blueprint.BaseModuleContext) androidBaseContextImpl {
- return androidBaseContextImpl{
- target: a.commonProperties.CompileTarget,
- targetPrimary: a.commonProperties.CompilePrimary,
- multiTargets: a.commonProperties.CompileMultiTargets,
- kind: determineModuleKind(a, ctx),
- config: ctx.Config().(Config),
+func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) baseModuleContext {
+ return baseModuleContext{
+ BaseModuleContext: ctx,
+ target: m.commonProperties.CompileTarget,
+ targetPrimary: m.commonProperties.CompilePrimary,
+ multiTargets: m.commonProperties.CompileMultiTargets,
+ kind: determineModuleKind(m, ctx),
+ config: ctx.Config().(Config),
}
}
-func (a *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
- ctx := &androidModuleContext{
- module: a.module,
- ModuleContext: blueprintCtx,
- androidBaseContextImpl: a.androidBaseContextFactory(blueprintCtx),
- installDeps: a.computeInstallDeps(blueprintCtx),
- installFiles: a.installFiles,
- missingDeps: blueprintCtx.GetMissingDependencies(),
- variables: make(map[string]string),
+func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) {
+ ctx := &moduleContext{
+ module: m.module,
+ bp: blueprintCtx,
+ baseModuleContext: m.baseModuleContextFactory(blueprintCtx),
+ installDeps: m.computeInstallDeps(blueprintCtx),
+ installFiles: m.installFiles,
+ variables: make(map[string]string),
}
+ // Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never
+ // reporting missing dependency errors in Blueprint when AllowMissingDependencies == true.
+ // TODO: This will be removed once defaults modules handle missing dependency errors
+ blueprintCtx.GetMissingDependencies()
+
+ // For the final GenerateAndroidBuildActions pass, require that all visited dependencies Soong modules and
+ // are enabled.
+ ctx.baseModuleContext.strictVisitDeps = true
+
if ctx.config.captureBuild {
ctx.ruleParams = make(map[blueprint.Rule]blueprint.RuleParams)
}
@@ -869,70 +882,80 @@
ctx.Variable(pctx, "moduleDescSuffix", s)
// Some common property checks for properties that will be used later in androidmk.go
- if a.commonProperties.Dist.Dest != nil {
- _, err := validateSafePath(*a.commonProperties.Dist.Dest)
+ if m.commonProperties.Dist.Dest != nil {
+ _, err := validateSafePath(*m.commonProperties.Dist.Dest)
if err != nil {
ctx.PropertyErrorf("dist.dest", "%s", err.Error())
}
}
- if a.commonProperties.Dist.Dir != nil {
- _, err := validateSafePath(*a.commonProperties.Dist.Dir)
+ if m.commonProperties.Dist.Dir != nil {
+ _, err := validateSafePath(*m.commonProperties.Dist.Dir)
if err != nil {
ctx.PropertyErrorf("dist.dir", "%s", err.Error())
}
}
- if a.commonProperties.Dist.Suffix != nil {
- if strings.Contains(*a.commonProperties.Dist.Suffix, "/") {
+ if m.commonProperties.Dist.Suffix != nil {
+ if strings.Contains(*m.commonProperties.Dist.Suffix, "/") {
ctx.PropertyErrorf("dist.suffix", "Suffix may not contain a '/' character.")
}
}
- if a.Enabled() {
- a.module.GenerateAndroidBuildActions(ctx)
+ if m.Enabled() {
+ m.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
}
- a.installFiles = append(a.installFiles, ctx.installFiles...)
- a.checkbuildFiles = append(a.checkbuildFiles, ctx.checkbuildFiles...)
+ m.installFiles = append(m.installFiles, ctx.installFiles...)
+ m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
- notice := proptools.StringDefault(a.commonProperties.Notice, "NOTICE")
- if m := SrcIsModule(notice); m != "" {
- a.noticeFile = ctx.ExpandOptionalSource(¬ice, "notice")
+ notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
+ if module := SrcIsModule(notice); module != "" {
+ m.noticeFile = ctx.ExpandOptionalSource(¬ice, "notice")
} else {
noticePath := filepath.Join(ctx.ModuleDir(), notice)
- a.noticeFile = ExistentPathForSource(ctx, noticePath)
+ m.noticeFile = ExistentPathForSource(ctx, noticePath)
}
+ } else if ctx.Config().AllowMissingDependencies() {
+ // If the module is not enabled it will not create any build rules, nothing will call
+ // ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
+ // and report them as an error even when AllowMissingDependencies = true. Call
+ // ctx.GetMissingDependencies() here to tell blueprint not to handle them.
+ ctx.GetMissingDependencies()
}
- if a == ctx.FinalModule().(Module).base() {
- a.generateModuleTarget(ctx)
+ if m == ctx.FinalModule().(Module).base() {
+ m.generateModuleTarget(ctx)
if ctx.Failed() {
return
}
}
- a.buildParams = ctx.buildParams
- a.ruleParams = ctx.ruleParams
- a.variables = ctx.variables
+ m.buildParams = ctx.buildParams
+ m.ruleParams = ctx.ruleParams
+ m.variables = ctx.variables
}
-type androidBaseContextImpl struct {
+type baseModuleContext struct {
+ blueprint.BaseModuleContext
target Target
multiTargets []Target
targetPrimary bool
debug bool
kind moduleKind
config Config
+
+ walkPath []Module
+
+ strictVisitDeps bool // If true, enforce that all dependencies are enabled
}
-type androidModuleContext struct {
- blueprint.ModuleContext
- androidBaseContextImpl
+type moduleContext struct {
+ bp blueprint.ModuleContext
+ baseModuleContext
installDeps Paths
installFiles Paths
checkbuildFiles Paths
- missingDeps []string
module Module
// For tests
@@ -941,25 +964,22 @@
variables map[string]string
}
-func (a *androidModuleContext) ninjaError(desc string, outputs []string, err error) {
- a.ModuleContext.Build(pctx.PackageContext, blueprint.BuildParams{
- Rule: ErrorRule,
- Description: desc,
- Outputs: outputs,
- Optional: true,
+func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) {
+ return pctx, BuildParams{
+ Rule: ErrorRule,
+ Description: params.Description,
+ Output: params.Output,
+ Outputs: params.Outputs,
+ ImplicitOutput: params.ImplicitOutput,
+ ImplicitOutputs: params.ImplicitOutputs,
Args: map[string]string{
"error": err.Error(),
},
- })
- return
+ }
}
-func (a *androidModuleContext) Config() Config {
- return a.ModuleContext.Config().(Config)
-}
-
-func (a *androidModuleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) {
- a.Build(pctx, BuildParams(params))
+func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) {
+ m.Build(pctx, BuildParams(params))
}
func convertBuildParams(params BuildParams) blueprint.BuildParams {
@@ -1002,86 +1022,100 @@
return bparams
}
-func (a *androidModuleContext) Variable(pctx PackageContext, name, value string) {
- if a.config.captureBuild {
- a.variables[name] = value
+func (m *moduleContext) Variable(pctx PackageContext, name, value string) {
+ if m.config.captureBuild {
+ m.variables[name] = value
}
- a.ModuleContext.Variable(pctx.PackageContext, name, value)
+ m.bp.Variable(pctx.PackageContext, name, value)
}
-func (a *androidModuleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
+func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams,
argNames ...string) blueprint.Rule {
- rule := a.ModuleContext.Rule(pctx.PackageContext, name, params, argNames...)
+ rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...)
- if a.config.captureBuild {
- a.ruleParams[rule] = params
+ if m.config.captureBuild {
+ m.ruleParams[rule] = params
}
return rule
}
-func (a *androidModuleContext) Build(pctx PackageContext, params BuildParams) {
- if a.config.captureBuild {
- a.buildParams = append(a.buildParams, params)
+func (m *moduleContext) Build(pctx PackageContext, params BuildParams) {
+ if params.Description != "" {
+ params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
}
- bparams := convertBuildParams(params)
-
- if bparams.Description != "" {
- bparams.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}"
+ if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 {
+ pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n",
+ m.ModuleName(), strings.Join(missingDeps, ", ")))
}
- if a.missingDeps != nil {
- a.ninjaError(bparams.Description, bparams.Outputs,
- fmt.Errorf("module %s missing dependencies: %s\n",
- a.ModuleName(), strings.Join(a.missingDeps, ", ")))
- return
+ if m.config.captureBuild {
+ m.buildParams = append(m.buildParams, params)
}
- a.ModuleContext.Build(pctx.PackageContext, bparams)
+ m.bp.Build(pctx.PackageContext, convertBuildParams(params))
}
-func (a *androidModuleContext) GetMissingDependencies() []string {
- return a.missingDeps
+func (b *baseModuleContext) Module() Module {
+ module, _ := b.BaseModuleContext.Module().(Module)
+ return module
}
-func (a *androidModuleContext) AddMissingDependencies(deps []string) {
+func (b *baseModuleContext) Config() Config {
+ return b.BaseModuleContext.Config().(Config)
+}
+
+func (m *moduleContext) GetMissingDependencies() []string {
+ var missingDeps []string
+ missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...)
+ missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...)
+ missingDeps = FirstUniqueStrings(missingDeps)
+ return missingDeps
+}
+
+func (b *baseModuleContext) AddMissingDependencies(deps []string) {
if deps != nil {
- a.missingDeps = append(a.missingDeps, deps...)
- a.missingDeps = FirstUniqueStrings(a.missingDeps)
+ missingDeps := &b.Module().base().commonProperties.MissingDeps
+ *missingDeps = append(*missingDeps, deps...)
+ *missingDeps = FirstUniqueStrings(*missingDeps)
}
}
-func (a *androidModuleContext) validateAndroidModule(module blueprint.Module) Module {
+func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, strict bool) Module {
aModule, _ := module.(Module)
+
+ if !strict {
+ return aModule
+ }
+
if aModule == nil {
- a.ModuleErrorf("module %q not an android module", a.OtherModuleName(aModule))
+ b.ModuleErrorf("module %q not an android module", b.OtherModuleName(module))
return nil
}
if !aModule.Enabled() {
- if a.Config().AllowMissingDependencies() {
- a.AddMissingDependencies([]string{a.OtherModuleName(aModule)})
+ if b.Config().AllowMissingDependencies() {
+ b.AddMissingDependencies([]string{b.OtherModuleName(aModule)})
} else {
- a.ModuleErrorf("depends on disabled module %q", a.OtherModuleName(aModule))
+ b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule))
}
return nil
}
-
return aModule
}
-func (a *androidModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
+func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) {
type dep struct {
mod blueprint.Module
tag blueprint.DependencyTag
}
var deps []dep
- a.VisitDirectDepsBlueprint(func(m blueprint.Module) {
- if aModule, _ := m.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
- returnedTag := a.ModuleContext.OtherModuleDependencyTag(aModule)
+ b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+ if aModule, _ := module.(Module); aModule != nil && aModule.base().BaseModuleName() == name {
+ returnedTag := b.BaseModuleContext.OtherModuleDependencyTag(aModule)
if tag == nil || returnedTag == tag {
deps = append(deps, dep{aModule, returnedTag})
}
@@ -1091,17 +1125,17 @@
return deps[0].mod, deps[0].tag
} else if len(deps) >= 2 {
panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q",
- name, a.ModuleName()))
+ name, b.ModuleName()))
} else {
return nil, nil
}
}
-func (a *androidModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
+func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module {
var deps []Module
- a.VisitDirectDepsBlueprint(func(m blueprint.Module) {
- if aModule, _ := m.(Module); aModule != nil {
- if a.ModuleContext.OtherModuleDependencyTag(aModule) == tag {
+ b.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+ if aModule, _ := module.(Module); aModule != nil {
+ if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
deps = append(deps, aModule)
}
}
@@ -1109,42 +1143,42 @@
return deps
}
-func (a *androidModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
- m, _ := a.getDirectDepInternal(name, tag)
- return m
+func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
+ module, _ := m.getDirectDepInternal(name, tag)
+ return module
}
-func (a *androidModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
- return a.getDirectDepInternal(name, nil)
+func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) {
+ return b.getDirectDepInternal(name, nil)
}
-func (a *androidModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
- a.ModuleContext.VisitDirectDeps(visit)
+func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
+ b.BaseModuleContext.VisitDirectDeps(visit)
}
-func (a *androidModuleContext) VisitDirectDeps(visit func(Module)) {
- a.ModuleContext.VisitDirectDeps(func(module blueprint.Module) {
- if aModule := a.validateAndroidModule(module); aModule != nil {
+func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) {
+ b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+ if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
visit(aModule)
}
})
}
-func (a *androidModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
- a.ModuleContext.VisitDirectDeps(func(module blueprint.Module) {
- if aModule := a.validateAndroidModule(module); aModule != nil {
- if a.ModuleContext.OtherModuleDependencyTag(aModule) == tag {
+func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
+ b.BaseModuleContext.VisitDirectDeps(func(module blueprint.Module) {
+ if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
+ if b.BaseModuleContext.OtherModuleDependencyTag(aModule) == tag {
visit(aModule)
}
}
})
}
-func (a *androidModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
- a.ModuleContext.VisitDirectDepsIf(
+func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
+ b.BaseModuleContext.VisitDirectDepsIf(
// pred
func(module blueprint.Module) bool {
- if aModule := a.validateAndroidModule(module); aModule != nil {
+ if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
return pred(aModule)
} else {
return false
@@ -1156,19 +1190,19 @@
})
}
-func (a *androidModuleContext) VisitDepsDepthFirst(visit func(Module)) {
- a.ModuleContext.VisitDepsDepthFirst(func(module blueprint.Module) {
- if aModule := a.validateAndroidModule(module); aModule != nil {
+func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) {
+ b.BaseModuleContext.VisitDepsDepthFirst(func(module blueprint.Module) {
+ if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
visit(aModule)
}
})
}
-func (a *androidModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
- a.ModuleContext.VisitDepsDepthFirstIf(
+func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
+ b.BaseModuleContext.VisitDepsDepthFirstIf(
// pred
func(module blueprint.Module) bool {
- if aModule := a.validateAndroidModule(module); aModule != nil {
+ if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil {
return pred(aModule)
} else {
return false
@@ -1180,15 +1214,21 @@
})
}
-func (a *androidModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
- a.ModuleContext.WalkDeps(visit)
+func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) {
+ b.BaseModuleContext.WalkDeps(visit)
}
-func (a *androidModuleContext) WalkDeps(visit func(Module, Module) bool) {
- a.ModuleContext.WalkDeps(func(child, parent blueprint.Module) bool {
- childAndroidModule := a.validateAndroidModule(child)
- parentAndroidModule := a.validateAndroidModule(parent)
+func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
+ b.walkPath = []Module{b.Module()}
+ b.BaseModuleContext.WalkDeps(func(child, parent blueprint.Module) bool {
+ childAndroidModule, _ := child.(Module)
+ parentAndroidModule, _ := parent.(Module)
if childAndroidModule != nil && parentAndroidModule != nil {
+ // record walkPath before visit
+ for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
+ b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
+ }
+ b.walkPath = append(b.walkPath, childAndroidModule)
return visit(childAndroidModule, parentAndroidModule)
} else {
return false
@@ -1196,143 +1236,151 @@
})
}
-func (a *androidModuleContext) VisitAllModuleVariants(visit func(Module)) {
- a.ModuleContext.VisitAllModuleVariants(func(module blueprint.Module) {
+func (b *baseModuleContext) GetWalkPath() []Module {
+ return b.walkPath
+}
+
+func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
+ m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
visit(module.(Module))
})
}
-func (a *androidModuleContext) PrimaryModule() Module {
- return a.ModuleContext.PrimaryModule().(Module)
+func (m *moduleContext) PrimaryModule() Module {
+ return m.bp.PrimaryModule().(Module)
}
-func (a *androidModuleContext) FinalModule() Module {
- return a.ModuleContext.FinalModule().(Module)
+func (m *moduleContext) FinalModule() Module {
+ return m.bp.FinalModule().(Module)
}
-func (a *androidBaseContextImpl) Target() Target {
- return a.target
+func (m *moduleContext) ModuleSubDir() string {
+ return m.bp.ModuleSubDir()
}
-func (a *androidBaseContextImpl) TargetPrimary() bool {
- return a.targetPrimary
+func (b *baseModuleContext) Target() Target {
+ return b.target
}
-func (a *androidBaseContextImpl) MultiTargets() []Target {
- return a.multiTargets
+func (b *baseModuleContext) TargetPrimary() bool {
+ return b.targetPrimary
}
-func (a *androidBaseContextImpl) Arch() Arch {
- return a.target.Arch
+func (b *baseModuleContext) MultiTargets() []Target {
+ return b.multiTargets
}
-func (a *androidBaseContextImpl) Os() OsType {
- return a.target.Os
+func (b *baseModuleContext) Arch() Arch {
+ return b.target.Arch
}
-func (a *androidBaseContextImpl) Host() bool {
- return a.target.Os.Class == Host || a.target.Os.Class == HostCross
+func (b *baseModuleContext) Os() OsType {
+ return b.target.Os
}
-func (a *androidBaseContextImpl) Device() bool {
- return a.target.Os.Class == Device
+func (b *baseModuleContext) Host() bool {
+ return b.target.Os.Class == Host || b.target.Os.Class == HostCross
}
-func (a *androidBaseContextImpl) Darwin() bool {
- return a.target.Os == Darwin
+func (b *baseModuleContext) Device() bool {
+ return b.target.Os.Class == Device
}
-func (a *androidBaseContextImpl) Fuchsia() bool {
- return a.target.Os == Fuchsia
+func (b *baseModuleContext) Darwin() bool {
+ return b.target.Os == Darwin
}
-func (a *androidBaseContextImpl) Windows() bool {
- return a.target.Os == Windows
+func (b *baseModuleContext) Fuchsia() bool {
+ return b.target.Os == Fuchsia
}
-func (a *androidBaseContextImpl) Debug() bool {
- return a.debug
+func (b *baseModuleContext) Windows() bool {
+ return b.target.Os == Windows
}
-func (a *androidBaseContextImpl) PrimaryArch() bool {
- if len(a.config.Targets[a.target.Os]) <= 1 {
+func (b *baseModuleContext) Debug() bool {
+ return b.debug
+}
+
+func (b *baseModuleContext) PrimaryArch() bool {
+ if len(b.config.Targets[b.target.Os]) <= 1 {
return true
}
- return a.target.Arch.ArchType == a.config.Targets[a.target.Os][0].Arch.ArchType
+ return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType
}
-func (a *androidBaseContextImpl) AConfig() Config {
- return a.config
+func (b *baseModuleContext) AConfig() Config {
+ return b.config
}
-func (a *androidBaseContextImpl) DeviceConfig() DeviceConfig {
- return DeviceConfig{a.config.deviceConfig}
+func (b *baseModuleContext) DeviceConfig() DeviceConfig {
+ return DeviceConfig{b.config.deviceConfig}
}
-func (a *androidBaseContextImpl) Platform() bool {
- return a.kind == platformModule
+func (b *baseModuleContext) Platform() bool {
+ return b.kind == platformModule
}
-func (a *androidBaseContextImpl) DeviceSpecific() bool {
- return a.kind == deviceSpecificModule
+func (b *baseModuleContext) DeviceSpecific() bool {
+ return b.kind == deviceSpecificModule
}
-func (a *androidBaseContextImpl) SocSpecific() bool {
- return a.kind == socSpecificModule
+func (b *baseModuleContext) SocSpecific() bool {
+ return b.kind == socSpecificModule
}
-func (a *androidBaseContextImpl) ProductSpecific() bool {
- return a.kind == productSpecificModule
+func (b *baseModuleContext) ProductSpecific() bool {
+ return b.kind == productSpecificModule
}
-func (a *androidBaseContextImpl) ProductServicesSpecific() bool {
- return a.kind == productServicesSpecificModule
+func (b *baseModuleContext) ProductServicesSpecific() bool {
+ return b.kind == productServicesSpecificModule
}
// Makes this module a platform module, i.e. not specific to soc, device,
// product, or product_services.
-func (a *ModuleBase) MakeAsPlatform() {
- a.commonProperties.Vendor = boolPtr(false)
- a.commonProperties.Proprietary = boolPtr(false)
- a.commonProperties.Soc_specific = boolPtr(false)
- a.commonProperties.Product_specific = boolPtr(false)
- a.commonProperties.Product_services_specific = boolPtr(false)
+func (m *ModuleBase) MakeAsPlatform() {
+ m.commonProperties.Vendor = boolPtr(false)
+ m.commonProperties.Proprietary = boolPtr(false)
+ m.commonProperties.Soc_specific = boolPtr(false)
+ m.commonProperties.Product_specific = boolPtr(false)
+ m.commonProperties.Product_services_specific = boolPtr(false)
}
-func (a *ModuleBase) EnableNativeBridgeSupportByDefault() {
- a.commonProperties.Native_bridge_supported = boolPtr(true)
+func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
+ m.commonProperties.Native_bridge_supported = boolPtr(true)
}
-func (a *androidModuleContext) InstallInData() bool {
- return a.module.InstallInData()
+func (m *moduleContext) InstallInData() bool {
+ return m.module.InstallInData()
}
-func (a *androidModuleContext) InstallInSanitizerDir() bool {
- return a.module.InstallInSanitizerDir()
+func (m *moduleContext) InstallInSanitizerDir() bool {
+ return m.module.InstallInSanitizerDir()
}
-func (a *androidModuleContext) InstallInRecovery() bool {
- return a.module.InstallInRecovery()
+func (m *moduleContext) InstallInRecovery() bool {
+ return m.module.InstallInRecovery()
}
-func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool {
- if a.module.base().commonProperties.SkipInstall {
+func (m *moduleContext) skipInstall(fullInstallPath OutputPath) bool {
+ if m.module.base().commonProperties.SkipInstall {
return true
}
// We'll need a solution for choosing which of modules with the same name in different
// namespaces to install. For now, reuse the list of namespaces exported to Make as the
// list of namespaces to install in a Soong-only build.
- if !a.module.base().commonProperties.NamespaceExportedToMake {
+ if !m.module.base().commonProperties.NamespaceExportedToMake {
return true
}
- if a.Device() {
- if a.Config().SkipDeviceInstall() {
+ if m.Device() {
+ if m.Config().SkipDeviceInstall() {
return true
}
- if a.Config().SkipMegaDeviceInstall(fullInstallPath.String()) {
+ if m.Config().SkipMegaDeviceInstall(fullInstallPath.String()) {
return true
}
}
@@ -1340,29 +1388,29 @@
return false
}
-func (a *androidModuleContext) InstallFile(installPath OutputPath, name string, srcPath Path,
+func (m *moduleContext) InstallFile(installPath OutputPath, name string, srcPath Path,
deps ...Path) OutputPath {
- return a.installFile(installPath, name, srcPath, Cp, deps)
+ return m.installFile(installPath, name, srcPath, Cp, deps)
}
-func (a *androidModuleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path,
+func (m *moduleContext) InstallExecutable(installPath OutputPath, name string, srcPath Path,
deps ...Path) OutputPath {
- return a.installFile(installPath, name, srcPath, CpExecutable, deps)
+ return m.installFile(installPath, name, srcPath, CpExecutable, deps)
}
-func (a *androidModuleContext) installFile(installPath OutputPath, name string, srcPath Path,
+func (m *moduleContext) installFile(installPath OutputPath, name string, srcPath Path,
rule blueprint.Rule, deps []Path) OutputPath {
- fullInstallPath := installPath.Join(a, name)
- a.module.base().hooks.runInstallHooks(a, fullInstallPath, false)
+ fullInstallPath := installPath.Join(m, name)
+ m.module.base().hooks.runInstallHooks(m, fullInstallPath, false)
- if !a.skipInstall(fullInstallPath) {
+ if !m.skipInstall(fullInstallPath) {
- deps = append(deps, a.installDeps...)
+ deps = append(deps, m.installDeps...)
var implicitDeps, orderOnlyDeps Paths
- if a.Host() {
+ if m.Host() {
// Installed host modules might be used during the build, depend directly on their
// dependencies so their timestamp is updated whenever their dependency is updated
implicitDeps = deps
@@ -1370,73 +1418,73 @@
orderOnlyDeps = deps
}
- a.Build(pctx, BuildParams{
+ m.Build(pctx, BuildParams{
Rule: rule,
Description: "install " + fullInstallPath.Base(),
Output: fullInstallPath,
Input: srcPath,
Implicits: implicitDeps,
OrderOnly: orderOnlyDeps,
- Default: !a.Config().EmbeddedInMake(),
+ Default: !m.Config().EmbeddedInMake(),
})
- a.installFiles = append(a.installFiles, fullInstallPath)
+ m.installFiles = append(m.installFiles, fullInstallPath)
}
- a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+ m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
return fullInstallPath
}
-func (a *androidModuleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath {
- fullInstallPath := installPath.Join(a, name)
- a.module.base().hooks.runInstallHooks(a, fullInstallPath, true)
+func (m *moduleContext) InstallSymlink(installPath OutputPath, name string, srcPath OutputPath) OutputPath {
+ fullInstallPath := installPath.Join(m, name)
+ m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
- if !a.skipInstall(fullInstallPath) {
+ if !m.skipInstall(fullInstallPath) {
relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
if err != nil {
panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
}
- a.Build(pctx, BuildParams{
+ m.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base(),
Output: fullInstallPath,
OrderOnly: Paths{srcPath},
- Default: !a.Config().EmbeddedInMake(),
+ Default: !m.Config().EmbeddedInMake(),
Args: map[string]string{
"fromPath": relPath,
},
})
- a.installFiles = append(a.installFiles, fullInstallPath)
- a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+ m.installFiles = append(m.installFiles, fullInstallPath)
+ m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
return fullInstallPath
}
// installPath/name -> absPath where absPath might be a path that is available only at runtime
// (e.g. /apex/...)
-func (a *androidModuleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
- fullInstallPath := installPath.Join(a, name)
- a.module.base().hooks.runInstallHooks(a, fullInstallPath, true)
+func (m *moduleContext) InstallAbsoluteSymlink(installPath OutputPath, name string, absPath string) OutputPath {
+ fullInstallPath := installPath.Join(m, name)
+ m.module.base().hooks.runInstallHooks(m, fullInstallPath, true)
- if !a.skipInstall(fullInstallPath) {
- a.Build(pctx, BuildParams{
+ if !m.skipInstall(fullInstallPath) {
+ m.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
Output: fullInstallPath,
- Default: !a.Config().EmbeddedInMake(),
+ Default: !m.Config().EmbeddedInMake(),
Args: map[string]string{
"fromPath": absPath,
},
})
- a.installFiles = append(a.installFiles, fullInstallPath)
+ m.installFiles = append(m.installFiles, fullInstallPath)
}
return fullInstallPath
}
-func (a *androidModuleContext) CheckbuildFile(srcPath Path) {
- a.checkbuildFiles = append(a.checkbuildFiles, srcPath)
+func (m *moduleContext) CheckbuildFile(srcPath Path) {
+ m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
type fileInstaller interface {
@@ -1551,54 +1599,54 @@
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead.
-func (ctx *androidModuleContext) ExpandSources(srcFiles, excludes []string) Paths {
- return PathsForModuleSrcExcludes(ctx, srcFiles, excludes)
+func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths {
+ return PathsForModuleSrcExcludes(m, srcFiles, excludes)
}
// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
// Deprecated: use PathForModuleSrc instead.
-func (ctx *androidModuleContext) ExpandSource(srcFile, prop string) Path {
- return PathForModuleSrc(ctx, srcFile)
+func (m *moduleContext) ExpandSource(srcFile, prop string) Path {
+ return PathForModuleSrc(m, srcFile)
}
// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if
// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module
// dependency resolution.
-func (ctx *androidModuleContext) ExpandOptionalSource(srcFile *string, prop string) OptionalPath {
+func (m *moduleContext) ExpandOptionalSource(srcFile *string, prop string) OptionalPath {
if srcFile != nil {
- return OptionalPathForPath(PathForModuleSrc(ctx, *srcFile))
+ return OptionalPathForPath(PathForModuleSrc(m, *srcFile))
}
return OptionalPath{}
}
-func (ctx *androidModuleContext) RequiredModuleNames() []string {
- return ctx.module.base().commonProperties.Required
+func (m *moduleContext) RequiredModuleNames() []string {
+ return m.module.base().commonProperties.Required
}
-func (ctx *androidModuleContext) HostRequiredModuleNames() []string {
- return ctx.module.base().commonProperties.Host_required
+func (m *moduleContext) HostRequiredModuleNames() []string {
+ return m.module.base().commonProperties.Host_required
}
-func (ctx *androidModuleContext) TargetRequiredModuleNames() []string {
- return ctx.module.base().commonProperties.Target_required
+func (m *moduleContext) TargetRequiredModuleNames() []string {
+ return m.module.base().commonProperties.Target_required
}
-func (ctx *androidModuleContext) Glob(globPattern string, excludes []string) Paths {
- ret, err := ctx.GlobWithDeps(globPattern, excludes)
+func (b *baseModuleContext) Glob(globPattern string, excludes []string) Paths {
+ ret, err := b.GlobWithDeps(globPattern, excludes)
if err != nil {
- ctx.ModuleErrorf("glob: %s", err.Error())
+ b.ModuleErrorf("glob: %s", err.Error())
}
- return pathsForModuleSrcFromFullPath(ctx, ret, true)
+ return pathsForModuleSrcFromFullPath(b, ret, true)
}
-func (ctx *androidModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
- ret, err := ctx.GlobWithDeps(globPattern, excludes)
+func (b *baseModuleContext) GlobFiles(globPattern string, excludes []string) Paths {
+ ret, err := b.GlobWithDeps(globPattern, excludes)
if err != nil {
- ctx.ModuleErrorf("glob: %s", err.Error())
+ b.ModuleErrorf("glob: %s", err.Error())
}
- return pathsForModuleSrcFromFullPath(ctx, ret, false)
+ return pathsForModuleSrcFromFullPath(b, ret, false)
}
func init() {
@@ -1658,17 +1706,8 @@
return
}
- sortedKeys := func(m map[string]Paths) []string {
- s := make([]string, 0, len(m))
- for k := range m {
- s = append(s, k)
- }
- sort.Strings(s)
- return s
- }
-
// Ensure ancestor directories are in modulesInDir
- dirs := sortedKeys(modulesInDir)
+ dirs := SortedStringKeys(modulesInDir)
for _, dir := range dirs {
dir := parentDir(dir)
for dir != "." && dir != "/" {
@@ -1681,7 +1720,6 @@
}
// Make directories build their direct subdirectories
- dirs = sortedKeys(modulesInDir)
for _, dir := range dirs {
p := parentDir(dir)
if p != "." && p != "/" {
@@ -1738,8 +1776,7 @@
}
// Wrap those into host|host-cross|target phony rules
- osClasses := sortedKeys(osClass)
- for _, class := range osClasses {
+ for _, class := range SortedStringKeys(osClass) {
ctx.Build(pctx, BuildParams{
Rule: blueprint.Phony,
Output: PathForPhony(ctx, class),
diff --git a/android/mutator.go b/android/mutator.go
index 0e80249..081c2b2 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -66,8 +66,8 @@
}
type RegisterMutatorsContext interface {
- TopDown(name string, m AndroidTopDownMutator) MutatorHandle
- BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle
+ TopDown(name string, m TopDownMutator) MutatorHandle
+ BottomUp(name string, m BottomUpMutator) MutatorHandle
}
type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -110,52 +110,27 @@
postDeps = append(postDeps, f)
}
-type AndroidTopDownMutator func(TopDownMutatorContext)
+type TopDownMutator func(TopDownMutatorContext)
type TopDownMutatorContext interface {
BaseModuleContext
- androidBaseContext
- OtherModuleExists(name string) bool
Rename(name string)
- Module() Module
-
- OtherModuleName(m blueprint.Module) string
- OtherModuleDir(m blueprint.Module) string
- OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
- OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
CreateModule(blueprint.ModuleFactory, ...interface{})
-
- GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
- GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
-
- VisitDirectDeps(visit func(Module))
- VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module))
- VisitDirectDepsIf(pred func(Module) bool, visit func(Module))
- VisitDepsDepthFirst(visit func(Module))
- VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module))
- WalkDeps(visit func(Module, Module) bool)
- // GetWalkPath is supposed to be called in visit function passed in WalkDeps()
- // and returns a top-down dependency path from a start module to current child module.
- GetWalkPath() []Module
}
-type androidTopDownMutatorContext struct {
- blueprint.TopDownMutatorContext
- androidBaseContextImpl
- walkPath []Module
+type topDownMutatorContext struct {
+ bp blueprint.TopDownMutatorContext
+ baseModuleContext
}
-type AndroidBottomUpMutator func(BottomUpMutatorContext)
+type BottomUpMutator func(BottomUpMutatorContext)
type BottomUpMutatorContext interface {
BaseModuleContext
- androidBaseContext
- OtherModuleExists(name string) bool
Rename(name string)
- Module() blueprint.Module
AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string)
AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string)
@@ -168,17 +143,17 @@
ReplaceDependencies(string)
}
-type androidBottomUpMutatorContext struct {
- blueprint.BottomUpMutatorContext
- androidBaseContextImpl
+type bottomUpMutatorContext struct {
+ bp blueprint.BottomUpMutatorContext
+ baseModuleContext
}
-func (x *registerMutatorsContext) BottomUp(name string, m AndroidBottomUpMutator) MutatorHandle {
+func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
f := func(ctx blueprint.BottomUpMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
- actx := &androidBottomUpMutatorContext{
- BottomUpMutatorContext: ctx,
- androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+ actx := &bottomUpMutatorContext{
+ bp: ctx,
+ baseModuleContext: a.base().baseModuleContextFactory(ctx),
}
m(actx)
}
@@ -188,12 +163,12 @@
return mutator
}
-func (x *registerMutatorsContext) TopDown(name string, m AndroidTopDownMutator) MutatorHandle {
+func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
f := func(ctx blueprint.TopDownMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
- actx := &androidTopDownMutatorContext{
- TopDownMutatorContext: ctx,
- androidBaseContextImpl: a.base().androidBaseContextFactory(ctx),
+ actx := &topDownMutatorContext{
+ bp: ctx,
+ baseModuleContext: a.base().baseModuleContextFactory(ctx),
}
m(actx)
}
@@ -218,106 +193,13 @@
}
}
-func (a *androidTopDownMutatorContext) Config() Config {
- return a.config
-}
-
-func (a *androidBottomUpMutatorContext) Config() Config {
- return a.config
-}
-
-func (a *androidTopDownMutatorContext) Module() Module {
- module, _ := a.TopDownMutatorContext.Module().(Module)
- return module
-}
-
-func (a *androidTopDownMutatorContext) VisitDirectDeps(visit func(Module)) {
- a.TopDownMutatorContext.VisitDirectDeps(func(module blueprint.Module) {
- if aModule, _ := module.(Module); aModule != nil {
- visit(aModule)
- }
- })
-}
-
-func (a *androidTopDownMutatorContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
- a.TopDownMutatorContext.VisitDirectDeps(func(module blueprint.Module) {
- if aModule, _ := module.(Module); aModule != nil {
- if a.TopDownMutatorContext.OtherModuleDependencyTag(aModule) == tag {
- visit(aModule)
- }
- }
- })
-}
-
-func (a *androidTopDownMutatorContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) {
- a.TopDownMutatorContext.VisitDirectDepsIf(
- // pred
- func(module blueprint.Module) bool {
- if aModule, _ := module.(Module); aModule != nil {
- return pred(aModule)
- } else {
- return false
- }
- },
- // visit
- func(module blueprint.Module) {
- visit(module.(Module))
- })
-}
-
-func (a *androidTopDownMutatorContext) VisitDepsDepthFirst(visit func(Module)) {
- a.TopDownMutatorContext.VisitDepsDepthFirst(func(module blueprint.Module) {
- if aModule, _ := module.(Module); aModule != nil {
- visit(aModule)
- }
- })
-}
-
-func (a *androidTopDownMutatorContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) {
- a.TopDownMutatorContext.VisitDepsDepthFirstIf(
- // pred
- func(module blueprint.Module) bool {
- if aModule, _ := module.(Module); aModule != nil {
- return pred(aModule)
- } else {
- return false
- }
- },
- // visit
- func(module blueprint.Module) {
- visit(module.(Module))
- })
-}
-
-func (a *androidTopDownMutatorContext) WalkDeps(visit func(Module, Module) bool) {
- a.walkPath = []Module{a.Module()}
- a.TopDownMutatorContext.WalkDeps(func(child, parent blueprint.Module) bool {
- childAndroidModule, _ := child.(Module)
- parentAndroidModule, _ := parent.(Module)
- if childAndroidModule != nil && parentAndroidModule != nil {
- // record walkPath before visit
- for a.walkPath[len(a.walkPath)-1] != parentAndroidModule {
- a.walkPath = a.walkPath[0 : len(a.walkPath)-1]
- }
- a.walkPath = append(a.walkPath, childAndroidModule)
- return visit(childAndroidModule, parentAndroidModule)
- } else {
- return false
- }
- })
-}
-
-func (a *androidTopDownMutatorContext) GetWalkPath() []Module {
- return a.walkPath
-}
-
-func (a *androidTopDownMutatorContext) AppendProperties(props ...interface{}) {
+func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
for _, p := range props {
- err := proptools.AppendMatchingProperties(a.Module().base().customizableProperties,
+ err := proptools.AppendMatchingProperties(t.Module().base().customizableProperties,
p, nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- a.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
} else {
panic(err)
}
@@ -325,16 +207,74 @@
}
}
-func (a *androidTopDownMutatorContext) PrependProperties(props ...interface{}) {
+func (t *topDownMutatorContext) PrependProperties(props ...interface{}) {
for _, p := range props {
- err := proptools.PrependMatchingProperties(a.Module().base().customizableProperties,
+ err := proptools.PrependMatchingProperties(t.Module().base().customizableProperties,
p, nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- a.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
} else {
panic(err)
}
}
}
}
+
+// android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
+// has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid
+// ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every
+// non-overridden method has to be forwarded. There are fewer non-overridden methods, so use the latter. The following
+// methods forward to the identical blueprint versions for topDownMutatorContext and bottomUpMutatorContext.
+
+func (t *topDownMutatorContext) Rename(name string) {
+ t.bp.Rename(name)
+}
+
+func (t *topDownMutatorContext) CreateModule(factory blueprint.ModuleFactory, props ...interface{}) {
+ t.bp.CreateModule(factory, props...)
+}
+
+func (b *bottomUpMutatorContext) Rename(name string) {
+ b.bp.Rename(name)
+}
+
+func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) {
+ b.bp.AddDependency(module, tag, name...)
+}
+
+func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
+ b.bp.AddReverseDependency(module, tag, name)
+}
+
+func (b *bottomUpMutatorContext) CreateVariations(variations ...string) []blueprint.Module {
+ return b.bp.CreateVariations(variations...)
+}
+
+func (b *bottomUpMutatorContext) CreateLocalVariations(variations ...string) []blueprint.Module {
+ return b.bp.CreateLocalVariations(variations...)
+}
+
+func (b *bottomUpMutatorContext) SetDependencyVariation(variation string) {
+ b.bp.SetDependencyVariation(variation)
+}
+
+func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
+ names ...string) {
+
+ b.bp.AddVariationDependencies(variations, tag, names...)
+}
+
+func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
+ tag blueprint.DependencyTag, names ...string) {
+
+ b.bp.AddFarVariationDependencies(variations, tag, names...)
+}
+
+func (b *bottomUpMutatorContext) AddInterVariantDependency(tag blueprint.DependencyTag, from, to blueprint.Module) {
+ b.bp.AddInterVariantDependency(tag, from, to)
+}
+
+func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
+ b.bp.ReplaceDependencies(name)
+}
diff --git a/android/mutator_test.go b/android/mutator_test.go
new file mode 100644
index 0000000..4cef400
--- /dev/null
+++ b/android/mutator_test.go
@@ -0,0 +1,99 @@
+// Copyright 2015 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 (
+ "io/ioutil"
+ "os"
+ "reflect"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type mutatorTestModule struct {
+ ModuleBase
+ props struct {
+ }
+
+ missingDeps []string
+}
+
+func mutatorTestModuleFactory() Module {
+ module := &mutatorTestModule{}
+ module.AddProperties(&module.props)
+ InitAndroidModule(module)
+ return module
+}
+
+func (m *mutatorTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: PathForModuleOut(ctx, "output"),
+ })
+
+ m.missingDeps = ctx.GetMissingDependencies()
+}
+
+func (m *mutatorTestModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), nil, "regular_missing_dep")
+}
+
+func addMissingDependenciesMutator(ctx TopDownMutatorContext) {
+ ctx.AddMissingDependencies([]string{"added_missing_dep"})
+}
+
+func TestMutatorAddMissingDependencies(t *testing.T) {
+ buildDir, err := ioutil.TempDir("", "soong_mutator_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(buildDir)
+
+ config := TestConfig(buildDir, nil)
+ config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
+
+ ctx := NewTestContext()
+ ctx.SetAllowMissingDependencies(true)
+
+ ctx.RegisterModuleType("test", ModuleFactoryAdaptor(mutatorTestModuleFactory))
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
+ })
+
+ bp := `
+ test {
+ name: "foo",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ foo := ctx.ModuleForTests("foo", "").Module().(*mutatorTestModule)
+
+ if g, w := foo.missingDeps, []string{"added_missing_dep", "regular_missing_dep"}; !reflect.DeepEqual(g, w) {
+ t.Errorf("want foo missing deps %q, got %q", w, g)
+ }
+}
diff --git a/android/namespace.go b/android/namespace.go
index 78d7f3c..27ec163 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -222,6 +222,11 @@
}
func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
+ if sourceNamespace.visibleNamespaces == nil {
+ // When handling dependencies before namespaceMutator, assume they are non-Soong Blueprint modules and give
+ // access to all namespaces.
+ return r.sortedNamespaces.sortedItems()
+ }
return sourceNamespace.visibleNamespaces
}
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 9a791a5..20241fe 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -16,8 +16,6 @@
import (
"errors"
- "io/ioutil"
- "os"
"path/filepath"
"reflect"
"testing"
@@ -93,6 +91,28 @@
// setupTest will report any errors
}
+func TestDependingOnBlueprintModuleInRootNamespace(t *testing.T) {
+ _ = setupTest(t,
+ map[string]string{
+ ".": `
+ blueprint_test_module {
+ name: "a",
+ }
+ `,
+ "dir1": `
+ soong_namespace {
+ }
+ blueprint_test_module {
+ name: "b",
+ deps: ["a"],
+ }
+ `,
+ },
+ )
+
+ // setupTest will report any errors
+}
+
func TestDependingOnModuleInImportedNamespace(t *testing.T) {
ctx := setupTest(t,
map[string]string{
@@ -613,18 +633,13 @@
}
func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
- buildDir, err := ioutil.TempDir("", "soong_namespace_test")
- if err != nil {
- return nil, []error{err}
- }
- defer os.RemoveAll(buildDir)
-
config := TestConfig(buildDir, nil)
ctx = NewTestContext()
ctx.MockFileSystem(bps)
ctx.RegisterModuleType("test_module", ModuleFactoryAdaptor(newTestModule))
ctx.RegisterModuleType("soong_namespace", ModuleFactoryAdaptor(NamespaceFactory))
+ ctx.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
ctx.PreArchMutators(RegisterNamespaceMutator)
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("rename", renameMutator)
@@ -649,6 +664,7 @@
}
func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
+ t.Helper()
ctx, errs := setupTestExpectErrs(bps)
FailIfErrored(t, errs)
return ctx
@@ -726,3 +742,22 @@
InitAndroidModule(m)
return m
}
+
+type blueprintTestModule struct {
+ blueprint.SimpleName
+ properties struct {
+ Deps []string
+ }
+}
+
+func (b *blueprintTestModule) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
+ return b.properties.Deps
+}
+
+func (b *blueprintTestModule) GenerateBuildActions(blueprint.ModuleContext) {
+}
+
+func newBlueprintTestModule() (blueprint.Module, []interface{}) {
+ m := &blueprintTestModule{}
+ return m, []interface{}{&m.properties, &m.SimpleName.Properties}
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index 9314483..f35d1fe 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -95,15 +95,17 @@
"external/icu",
"external/okhttp",
"external/wycheproof",
+
+ // Not really a core library but still needs access to same capabilities.
+ "development",
}
- // Core library constraints. The no_standard_libs can only be used in core
- // library projects. Access to core library targets is restricted using
- // visibility rules.
+ // Core library constraints. The sdk_version: "none" can only be used in core library projects.
+ // Access to core library targets is restricted using visibility rules.
rules := []*rule{
neverallow().
- notIn(append(coreLibraryProjects, "development")...).
- with("no_standard_libs", "true"),
+ notIn(coreLibraryProjects...).
+ with("sdk_version", "none"),
}
return rules
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 00c51ea..ee3c94f 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -15,8 +15,6 @@
package android
import (
- "io/ioutil"
- "os"
"testing"
)
@@ -148,15 +146,41 @@
},
expectedError: "java_device_for_host can only be used in whitelisted projects",
},
+ // Libcore rule tests
+ {
+ name: "sdk_version: \"none\" inside core libraries",
+ fs: map[string][]byte{
+ "libcore/Blueprints": []byte(`
+ java_library {
+ name: "inside_core_libraries",
+ sdk_version: "none",
+ }`),
+ },
+ },
+ {
+ name: "sdk_version: \"none\" outside core libraries",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ java_library {
+ name: "outside_core_libraries",
+ sdk_version: "none",
+ }`),
+ },
+ expectedError: "module \"outside_core_libraries\": violates neverallow",
+ },
+ {
+ name: "sdk_version: \"current\"",
+ fs: map[string][]byte{
+ "Blueprints": []byte(`
+ java_library {
+ name: "outside_core_libraries",
+ sdk_version: "current",
+ }`),
+ },
+ },
}
func TestNeverallow(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_neverallow_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
config := TestConfig(buildDir, nil)
for _, test := range neverallowTests {
@@ -176,6 +200,7 @@
ctx := NewTestContext()
ctx.RegisterModuleType("cc_library", ModuleFactoryAdaptor(newMockCcLibraryModule))
ctx.RegisterModuleType("java_library", ModuleFactoryAdaptor(newMockJavaLibraryModule))
+ ctx.RegisterModuleType("java_library_host", ModuleFactoryAdaptor(newMockJavaLibraryModule))
ctx.RegisterModuleType("java_device_for_host", ModuleFactoryAdaptor(newMockJavaLibraryModule))
ctx.PostDepsMutators(registerNeverallowMutator)
ctx.Register()
@@ -227,7 +252,8 @@
}
type mockJavaLibraryProperties struct {
- Libs []string
+ Libs []string
+ Sdk_version *string
}
type mockJavaLibraryModule struct {
diff --git a/android/paths.go b/android/paths.go
index 3915ff4..20b8b82 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -41,9 +41,7 @@
var _ PathContext = ModuleContext(nil)
type ModuleInstallPathContext interface {
- PathContext
-
- androidBaseContext
+ BaseModuleContext
InstallInData() bool
InstallInSanitizerDir() bool
@@ -369,7 +367,7 @@
// each string. If incDirs is false, strip paths with a trailing '/' from the list.
// It intended for use in globs that only list files that exist, so it allows '$' in
// filenames.
-func pathsForModuleSrcFromFullPath(ctx ModuleContext, paths []string, incDirs bool) Paths {
+func pathsForModuleSrcFromFullPath(ctx BaseModuleContext, paths []string, incDirs bool) Paths {
prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
if prefix == "./" {
prefix = ""
diff --git a/android/paths_test.go b/android/paths_test.go
index 85c8d84..7bcfe41 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -17,8 +17,6 @@
import (
"errors"
"fmt"
- "io/ioutil"
- "os"
"reflect"
"strings"
"testing"
@@ -200,7 +198,7 @@
}
type moduleInstallPathContextImpl struct {
- androidBaseContextImpl
+ baseModuleContext
inData bool
inSanitizerDir bool
@@ -212,7 +210,7 @@
}
func (m moduleInstallPathContextImpl) Config() Config {
- return m.androidBaseContextImpl.config
+ return m.baseModuleContext.config
}
func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {}
@@ -244,7 +242,7 @@
{
name: "host binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: hostTarget,
},
},
@@ -255,7 +253,7 @@
{
name: "system binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
},
},
@@ -265,7 +263,7 @@
{
name: "vendor binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: socSpecificModule,
},
@@ -276,7 +274,7 @@
{
name: "odm binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: deviceSpecificModule,
},
@@ -287,7 +285,7 @@
{
name: "product binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productSpecificModule,
},
@@ -298,7 +296,7 @@
{
name: "product_services binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productServicesSpecificModule,
},
@@ -310,7 +308,7 @@
{
name: "system native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
},
inData: true,
@@ -321,7 +319,7 @@
{
name: "vendor native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: socSpecificModule,
},
@@ -333,7 +331,7 @@
{
name: "odm native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: deviceSpecificModule,
},
@@ -345,7 +343,7 @@
{
name: "product native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productSpecificModule,
},
@@ -358,7 +356,7 @@
{
name: "product_services native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productServicesSpecificModule,
},
@@ -371,7 +369,7 @@
{
name: "sanitized system binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
},
inSanitizerDir: true,
@@ -382,7 +380,7 @@
{
name: "sanitized vendor binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: socSpecificModule,
},
@@ -394,7 +392,7 @@
{
name: "sanitized odm binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: deviceSpecificModule,
},
@@ -406,7 +404,7 @@
{
name: "sanitized product binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productSpecificModule,
},
@@ -419,7 +417,7 @@
{
name: "sanitized product_services binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productServicesSpecificModule,
},
@@ -432,7 +430,7 @@
{
name: "sanitized system native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
},
inData: true,
@@ -444,7 +442,7 @@
{
name: "sanitized vendor native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: socSpecificModule,
},
@@ -457,7 +455,7 @@
{
name: "sanitized odm native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: deviceSpecificModule,
},
@@ -470,7 +468,7 @@
{
name: "sanitized product native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productSpecificModule,
},
@@ -483,7 +481,7 @@
{
name: "sanitized product_services native test binary",
ctx: &moduleInstallPathContextImpl{
- androidBaseContextImpl: androidBaseContextImpl{
+ baseModuleContext: baseModuleContext{
target: deviceTarget,
kind: productServicesSpecificModule,
},
@@ -497,7 +495,7 @@
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- tc.ctx.androidBaseContextImpl.config = testConfig
+ tc.ctx.baseModuleContext.config = testConfig
output := PathForModuleInstall(tc.ctx, tc.in...)
if output.basePath.path != tc.out {
t.Errorf("unexpected path:\n got: %q\nwant: %q\n",
@@ -758,6 +756,11 @@
if !p.props.Module_handles_missing_deps {
p.missingDeps = ctx.GetMissingDependencies()
}
+
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: PathForModuleOut(ctx, "output"),
+ })
}
type pathForModuleSrcOutputFileProviderModule struct {
@@ -875,12 +878,6 @@
}
func TestPathsForModuleSrc(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
tests := []pathForModuleSrcTestCase{
{
name: "path",
@@ -961,12 +958,6 @@
}
func TestPathForModuleSrc(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_path_for_module_src_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
tests := []pathForModuleSrcTestCase{
{
name: "path",
@@ -1034,12 +1025,6 @@
}
func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_paths_for_module_src_allow_missing_dependencies_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
config := TestConfig(buildDir, nil)
config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
diff --git a/android/prebuilt_etc_test.go b/android/prebuilt_etc_test.go
index d977c30..0a2c7a4 100644
--- a/android/prebuilt_etc_test.go
+++ b/android/prebuilt_etc_test.go
@@ -15,16 +15,13 @@
package android
import (
- "io/ioutil"
- "os"
"path/filepath"
"reflect"
"testing"
)
func testPrebuiltEtc(t *testing.T, bp string) (*TestContext, Config) {
- config, buildDir := setUp(t)
- defer tearDown(buildDir)
+ config := TestArchConfig(buildDir, nil)
ctx := NewTestArchContext()
ctx.RegisterModuleType("prebuilt_etc", ModuleFactoryAdaptor(PrebuiltEtcFactory))
ctx.RegisterModuleType("prebuilt_etc_host", ModuleFactoryAdaptor(PrebuiltEtcHostFactory))
@@ -51,20 +48,6 @@
return ctx, config
}
-func setUp(t *testing.T) (config Config, buildDir string) {
- buildDir, err := ioutil.TempDir("", "soong_prebuilt_etc_test")
- if err != nil {
- t.Fatal(err)
- }
-
- config = TestArchConfig(buildDir, nil)
- return
-}
-
-func tearDown(buildDir string) {
- os.RemoveAll(buildDir)
-}
-
func TestPrebuiltEtcVariants(t *testing.T) {
ctx, _ := testPrebuiltEtc(t, `
prebuilt_etc {
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index a5b85c8..0a18e2c 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -16,8 +16,6 @@
import (
"fmt"
- "io/ioutil"
- "os"
"testing"
"github.com/google/blueprint"
@@ -127,12 +125,6 @@
}
func TestPrebuilts(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_prebuilt_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
config := TestConfig(buildDir, nil)
for _, test := range prebuiltsTests {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 4a3b022..8d7e74b 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -525,6 +525,15 @@
return c
}
+// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
+// and will be the temporary output directory managed by sbox, not the final one.
+func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
+ if !c.sbox {
+ panic("OutputDir only valid with Sbox")
+ }
+ return c.Text("__SBOX_OUT_DIR__")
+}
+
// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
// line, and causes RuleBuilder.Build file to set the depfile flag for ninja. If multiple depfiles are added to
// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index df0f256..cfbc2ab 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -16,8 +16,6 @@
import (
"fmt"
- "io/ioutil"
- "os"
"path/filepath"
"reflect"
"strings"
@@ -418,12 +416,6 @@
}
func TestRuleBuilder_Build(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
bp := `
rule_builder_test {
name: "foo",
diff --git a/android/sh_binary_test.go b/android/sh_binary_test.go
index becb35a..c99e18c 100644
--- a/android/sh_binary_test.go
+++ b/android/sh_binary_test.go
@@ -1,19 +1,11 @@
package android
import (
- "io/ioutil"
- "os"
"reflect"
"testing"
)
func testShBinary(t *testing.T, bp string) (*TestContext, Config) {
- buildDir, err := ioutil.TempDir("", "soong_sh_binary_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
config := TestArchConfig(buildDir, nil)
ctx := NewTestArchContext()
diff --git a/android/testing.go b/android/testing.go
index c0db75e..44bee4b 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -179,7 +179,7 @@
func maybeBuildParamsFromDescription(provider testBuildProvider, desc string) TestingBuildParams {
for _, p := range provider.BuildParamsForTests() {
- if p.Description == desc {
+ if strings.Contains(p.Description, desc) {
return newTestingBuildParams(provider, p)
}
}
diff --git a/android/util.go b/android/util.go
index f7a3437..3b8bc78 100644
--- a/android/util.go
+++ b/android/util.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"regexp"
"runtime"
"sort"
@@ -77,10 +78,15 @@
return string(ret)
}
-func sortedKeys(m map[string][]string) []string {
- s := make([]string, 0, len(m))
- for k := range m {
- s = append(s, k)
+func SortedStringKeys(m interface{}) []string {
+ v := reflect.ValueOf(m)
+ if v.Kind() != reflect.Map {
+ panic(fmt.Sprintf("%#v is not a map", m))
+ }
+ keys := v.MapKeys()
+ s := make([]string, 0, len(keys))
+ for _, key := range keys {
+ s = append(s, key.String())
}
sort.Strings(s)
return s
diff --git a/android/variable.go b/android/variable.go
index d039a16..b4f31c6 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -402,12 +402,12 @@
}
}
-func (a *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
+func (m *ModuleBase) setVariableProperties(ctx BottomUpMutatorContext,
prefix string, productVariablePropertyValue reflect.Value, variableValue interface{}) {
printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
- err := proptools.AppendMatchingProperties(a.generalProperties,
+ err := proptools.AppendMatchingProperties(m.generalProperties,
productVariablePropertyValue.Addr().Interface(), nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 09c5b1b..1a51495 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1,8 +1,6 @@
package android
import (
- "io/ioutil"
- "os"
"testing"
"github.com/google/blueprint"
@@ -663,12 +661,6 @@
}
func TestVisibility(t *testing.T) {
- buildDir, err := ioutil.TempDir("", "soong_neverallow_test")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(buildDir)
-
for _, test := range visibilityTests {
t.Run(test.name, func(t *testing.T) {
_, errs := testVisibility(buildDir, test.fs)
@@ -759,6 +751,3 @@
InitDefaultsModule(m)
return m
}
-
-func (*mockDefaults) GenerateAndroidBuildActions(ctx ModuleContext) {
-}
diff --git a/android/vts_config_test.go b/android/vts_config_test.go
index 7d4c9b1..142b2f5 100644
--- a/android/vts_config_test.go
+++ b/android/vts_config_test.go
@@ -15,19 +15,11 @@
package android
import (
- "io/ioutil"
- "os"
"testing"
)
func testVtsConfig(test *testing.T, bpFileContents string) *TestContext {
- buildDir, err := ioutil.TempDir("", "soong_vts_config_test")
- if err != nil {
- test.Fatal(err)
- }
-
config := TestArchConfig(buildDir, nil)
- defer func() { os.RemoveAll(buildDir) }()
ctx := NewTestArchContext()
ctx.RegisterModuleType("vts_config", ModuleFactoryAdaptor(VtsConfigFactory))
diff --git a/androidmk/Android.bp b/androidmk/Android.bp
index 1d939b0..79fe530 100644
--- a/androidmk/Android.bp
+++ b/androidmk/Android.bp
@@ -30,6 +30,7 @@
"androidmk-parser",
"blueprint-parser",
"bpfix-lib",
+ "soong-android",
],
}
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 24057af..af81e43 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -15,9 +15,9 @@
package main
import (
+ "android/soong/android"
mkparser "android/soong/androidmk/parser"
"fmt"
- "sort"
"strings"
bpparser "github.com/google/blueprint/parser"
@@ -185,7 +185,6 @@
"LOCAL_NO_CRT": "nocrt",
"LOCAL_ALLOW_UNDEFINED_SYMBOLS": "allow_undefined_symbols",
"LOCAL_RTTI_FLAG": "rtti",
- "LOCAL_NO_STANDARD_LIBRARIES": "no_standard_libs",
"LOCAL_PACK_MODULE_RELOCATIONS": "pack_relocations",
"LOCAL_TIDY": "tidy",
"LOCAL_USE_CLANG_LLD": "use_clang_lld",
@@ -335,15 +334,6 @@
}
}
-func sortedMapKeys(inputMap map[string]string) (sortedKeys []string) {
- keys := make([]string, 0, len(inputMap))
- for key := range inputMap {
- keys = append(keys, key)
- }
- sort.Strings(keys)
- return keys
-}
-
// splitAndAssign splits a Make list into components and then
// creates the corresponding variable assignments.
func splitAndAssign(ctx variableAssignmentContext, splitFunc listSplitFunc, namesByClassification map[string]string) error {
@@ -357,7 +347,7 @@
return err
}
- for _, nameClassification := range sortedMapKeys(namesByClassification) {
+ for _, nameClassification := range android.SortedStringKeys(namesByClassification) {
name := namesByClassification[nameClassification]
if component, ok := lists[nameClassification]; ok && !emptyList(component) {
err = setVariable(ctx.file, ctx.append, ctx.prefix, name, component, true)
diff --git a/apex/apex.go b/apex/apex.go
index aed7d6f..84e5497 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -61,7 +61,7 @@
`--key ${key} ${opt_flags} ${image_dir} ${out} `,
CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
- "${soong_zip}", "${zipalign}", "${aapt2}"},
+ "${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
Description: "APEX ${image_dir} => ${out}",
}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
@@ -552,7 +552,7 @@
}
}
-func (a *apexBundle) getCertString(ctx android.BaseContext) string {
+func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
if overridden {
return ":" + certificate
@@ -1062,6 +1062,10 @@
Description: "signapk",
Output: a.outputFiles[apexType],
Input: unsignedOutputFile,
+ Implicits: []android.Path{
+ a.container_certificate_file,
+ a.container_private_key_file,
+ },
Args: map[string]string{
"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
"flags": "-a 4096", //alignment
@@ -1295,9 +1299,6 @@
android.DefaultsModuleBase
}
-func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-}
-
func defaultsFactory() android.Module {
return DefaultsFactory()
}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 32ccf4c..272d3d4 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -156,12 +156,18 @@
func (library *libraryDecorator) androidMkWriteExportedFlags(w io.Writer) {
exportedFlags := library.exportedFlags()
+ for _, dir := range library.exportedDirs() {
+ exportedFlags = append(exportedFlags, "-I"+dir)
+ }
+ for _, dir := range library.exportedSystemDirs() {
+ exportedFlags = append(exportedFlags, "-isystem "+dir)
+ }
if len(exportedFlags) > 0 {
fmt.Fprintln(w, "LOCAL_EXPORT_CFLAGS :=", strings.Join(exportedFlags, " "))
}
- exportedFlagsDeps := library.exportedFlagsDeps()
- if len(exportedFlagsDeps) > 0 {
- fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DEPS :=", strings.Join(exportedFlagsDeps.Strings(), " "))
+ exportedDeps := library.exportedDeps()
+ if len(exportedDeps) > 0 {
+ fmt.Fprintln(w, "LOCAL_EXPORT_C_INCLUDE_DEPS :=", strings.Join(exportedDeps.Strings(), " "))
}
}
diff --git a/cc/binary.go b/cc/binary.go
index 9bb0b16..1757f1c 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -106,13 +106,17 @@
}
-func (binary *binaryDecorator) getStem(ctx BaseModuleContext) string {
+func (binary *binaryDecorator) getStemWithoutSuffix(ctx BaseModuleContext) string {
stem := ctx.baseModuleName()
if String(binary.Properties.Stem) != "" {
stem = String(binary.Properties.Stem)
}
- return stem + String(binary.Properties.Suffix)
+ return stem
+}
+
+func (binary *binaryDecorator) getStem(ctx BaseModuleContext) string {
+ return binary.getStemWithoutSuffix(ctx) + String(binary.Properties.Suffix)
}
func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
@@ -384,11 +388,11 @@
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
- builderFlags, outputFile)
+ builderFlags, outputFile, nil)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
- binary.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, binary.getStem(ctx))
+ binary.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
// Need to determine symlinks early since some targets (ie APEX) need this
// information but will not call 'install'
@@ -398,11 +402,11 @@
}
if Bool(binary.Properties.Symlink_preferred_arch) {
- if String(binary.Properties.Stem) == "" && String(binary.Properties.Suffix) == "" {
- ctx.PropertyErrorf("symlink_preferred_arch", "must also specify stem or suffix")
+ if String(binary.Properties.Suffix) == "" {
+ ctx.PropertyErrorf("symlink_preferred_arch", "must also specify suffix")
}
if ctx.TargetPrimary() {
- binary.symlinks = append(binary.symlinks, ctx.baseModuleName())
+ binary.symlinks = append(binary.symlinks, binary.getStemWithoutSuffix(ctx))
}
}
diff --git a/cc/builder.go b/cc/builder.go
index d2727b3..1e12361 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -22,10 +22,10 @@
"fmt"
"path/filepath"
"runtime"
- "strconv"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/pathtools"
"android/soong/android"
"android/soong/cc/config"
@@ -92,20 +92,6 @@
},
"arCmd", "arFlags")
- darwinAr = pctx.AndroidStaticRule("darwinAr",
- blueprint.RuleParams{
- Command: "rm -f ${out} && ${config.MacArPath} $arFlags $out $in",
- CommandDeps: []string{"${config.MacArPath}"},
- },
- "arFlags")
-
- darwinAppendAr = pctx.AndroidStaticRule("darwinAppendAr",
- blueprint.RuleParams{
- Command: "cp -f ${inAr} ${out}.tmp && ${config.MacArPath} $arFlags ${out}.tmp $in && mv ${out}.tmp ${out}",
- CommandDeps: []string{"${config.MacArPath}", "${inAr}"},
- },
- "arFlags", "inAr")
-
darwinStrip = pctx.AndroidStaticRule("darwinStrip",
blueprint.RuleParams{
Command: "${config.MacStripPath} -u -r -o $out $in",
@@ -227,6 +213,14 @@
blueprint.RuleParams{
Command: "gunzip -c $in > $out",
})
+
+ zip = pctx.AndroidStaticRule("zip",
+ blueprint.RuleParams{
+ Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
+ CommandDeps: []string{"${SoongZipCmd}"},
+ Rspfile: "$out.rsp",
+ RspfileContent: "$in",
+ })
)
func init() {
@@ -239,6 +233,8 @@
// Darwin doesn't have /proc
pctx.StaticVariable("relPwd", "")
}
+
+ pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
}
type builderFlags struct {
@@ -504,11 +500,6 @@
func TransformObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
- if ctx.Darwin() {
- transformDarwinObjToStaticLib(ctx, objFiles, flags, outputFile, deps)
- return
- }
-
arCmd := "${config.ClangBin}/llvm-ar"
arFlags := "crsD"
if !ctx.Darwin() {
@@ -531,87 +522,11 @@
})
}
-// Generate a rule for compiling multiple .o files to a static library (.a) on
-// darwin. The darwin ar tool doesn't support @file for list files, and has a
-// very small command line length limit, so we have to split the ar into multiple
-// steps, each appending to the previous one.
-func transformDarwinObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
- flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
-
- arFlags := "cqs"
-
- if len(objFiles) == 0 {
- dummy := android.PathForModuleOut(ctx, "dummy"+objectExtension)
- dummyAr := android.PathForModuleOut(ctx, "dummy"+staticLibraryExtension)
-
- ctx.Build(pctx, android.BuildParams{
- Rule: emptyFile,
- Description: "empty object file",
- Output: dummy,
- Implicits: deps,
- })
-
- ctx.Build(pctx, android.BuildParams{
- Rule: darwinAr,
- Description: "empty static archive",
- Output: dummyAr,
- Input: dummy,
- Args: map[string]string{
- "arFlags": arFlags,
- },
- })
-
- ctx.Build(pctx, android.BuildParams{
- Rule: darwinAppendAr,
- Description: "static link " + outputFile.Base(),
- Output: outputFile,
- Input: dummy,
- Args: map[string]string{
- "arFlags": "d",
- "inAr": dummyAr.String(),
- },
- })
-
- return
- }
-
- // ARG_MAX on darwin is 262144, use half that to be safe
- objFilesLists, err := splitListForSize(objFiles, 131072)
- if err != nil {
- ctx.ModuleErrorf("%s", err.Error())
- }
-
- var in, out android.WritablePath
- for i, l := range objFilesLists {
- in = out
- out = outputFile
- if i != len(objFilesLists)-1 {
- out = android.PathForModuleOut(ctx, outputFile.Base()+strconv.Itoa(i))
- }
-
- build := android.BuildParams{
- Rule: darwinAr,
- Description: "static link " + out.Base(),
- Output: out,
- Inputs: l,
- Implicits: deps,
- Args: map[string]string{
- "arFlags": arFlags,
- },
- }
- if i != 0 {
- build.Rule = darwinAppendAr
- build.Args["inAr"] = in.String()
- }
- ctx.Build(pctx, build)
- }
-}
-
// Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
// and shared libraries, to a shared library (.so) or dynamic executable
func TransformObjToDynamicBinary(ctx android.ModuleContext,
objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
- crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath) {
+ crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
ldCmd := "${config.ClangBin}/clang++"
@@ -648,7 +563,11 @@
}
for _, lib := range sharedLibs {
- libFlagsList = append(libFlagsList, lib.String())
+ libFile := lib.String()
+ if ctx.Windows() {
+ libFile = pathtools.ReplaceExtension(libFile, "lib")
+ }
+ libFlagsList = append(libFlagsList, libFile)
}
deps = append(deps, staticLibs...)
@@ -659,11 +578,12 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: ld,
- Description: "link " + outputFile.Base(),
- Output: outputFile,
- Inputs: objFiles,
- Implicits: deps,
+ Rule: ld,
+ Description: "link " + outputFile.Base(),
+ Output: outputFile,
+ ImplicitOutputs: implicitOutputs,
+ Inputs: objFiles,
+ Implicits: deps,
Args: map[string]string{
"ldCmd": ldCmd,
"crtBegin": crtBegin.String(),
@@ -877,13 +797,18 @@
})
}
-func TransformCoverageFilesToLib(ctx android.ModuleContext,
- inputs Objects, flags builderFlags, baseName string) android.OptionalPath {
+func TransformCoverageFilesToZip(ctx android.ModuleContext,
+ inputs Objects, baseName string) android.OptionalPath {
if len(inputs.coverageFiles) > 0 {
- outputFile := android.PathForModuleOut(ctx, baseName+".gcnodir")
+ outputFile := android.PathForModuleOut(ctx, baseName+".zip")
- TransformObjToStaticLib(ctx, inputs.coverageFiles, flags, outputFile, nil)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: zip,
+ Description: "zip " + outputFile.Base(),
+ Inputs: inputs.coverageFiles,
+ Output: outputFile,
+ })
return android.OptionalPathForPath(outputFile)
}
diff --git a/cc/cc.go b/cc/cc.go
index e61857d..53ec899 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -119,8 +119,13 @@
GeneratedSources android.Paths
GeneratedHeaders android.Paths
- Flags, ReexportedFlags []string
- ReexportedFlagsDeps android.Paths
+ Flags []string
+ IncludeDirs []string
+ SystemIncludeDirs []string
+ ReexportedDirs []string
+ ReexportedSystemDirs []string
+ ReexportedFlags []string
+ ReexportedDeps android.Paths
// Paths to crt*.o files
CrtBegin, CrtEnd android.OptionalPath
@@ -278,7 +283,7 @@
}
type BaseModuleContext interface {
- android.BaseContext
+ android.BaseModuleContext
ModuleContextIntf
}
@@ -641,7 +646,7 @@
}
type baseModuleContext struct {
- android.BaseContext
+ android.BaseModuleContext
moduleContextImpl
}
@@ -988,6 +993,14 @@
flags.ConlyFlags, _ = filterList(flags.ConlyFlags, config.IllegalFlags)
flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...)
+
+ for _, dir := range deps.IncludeDirs {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+dir)
+ }
+ for _, dir := range deps.SystemIncludeDirs {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-isystem "+dir)
+ }
+
c.flags = flags
// We need access to all the flags seen by a source file.
if c.sabi != nil {
@@ -1040,7 +1053,7 @@
}
}
-func (c *Module) toolchain(ctx android.BaseContext) config.Toolchain {
+func (c *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if c.cachedToolchain == nil {
c.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
}
@@ -1161,7 +1174,7 @@
func (c *Module) beginMutator(actx android.BottomUpMutatorContext) {
ctx := &baseModuleContext{
- BaseContext: actx,
+ BaseModuleContext: actx,
moduleContextImpl: moduleContextImpl{
mod: c,
},
@@ -1578,6 +1591,13 @@
llndkLibraries := llndkLibraries(ctx.Config())
vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
+ reexportExporter := func(exporter exportedFlagsProducer) {
+ depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, exporter.exportedDirs()...)
+ depPaths.ReexportedSystemDirs = append(depPaths.ReexportedSystemDirs, exporter.exportedSystemDirs()...)
+ depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, exporter.exportedFlags()...)
+ depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, exporter.exportedDeps()...)
+ }
+
ctx.VisitDirectDeps(func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
depTag := ctx.OtherModuleDependencyTag(dep)
@@ -1599,14 +1619,13 @@
if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders,
genRule.GeneratedDeps()...)
- flags := includeDirsToFlags(genRule.GeneratedHeaderDirs())
- depPaths.Flags = append(depPaths.Flags, flags)
+ dirs := genRule.GeneratedHeaderDirs().Strings()
+ depPaths.IncludeDirs = append(depPaths.IncludeDirs, dirs...)
if depTag == genHeaderExportDepTag {
- depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags)
- depPaths.ReexportedFlagsDeps = append(depPaths.ReexportedFlagsDeps,
- genRule.GeneratedDeps()...)
+ depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, dirs...)
+ depPaths.ReexportedDeps = append(depPaths.ReexportedDeps, genRule.GeneratedDeps()...)
// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
- c.sabi.Properties.ReexportedIncludeFlags = append(c.sabi.Properties.ReexportedIncludeFlags, flags)
+ c.sabi.Properties.ReexportedIncludes = append(c.sabi.Properties.ReexportedIncludes, dirs...)
}
} else {
@@ -1644,10 +1663,9 @@
if depTag == reuseObjTag {
if l, ok := ccDep.compiler.(libraryInterface); ok {
c.staticVariant = ccDep
- objs, flags, deps := l.reuseObjs()
+ objs, exporter := l.reuseObjs()
depPaths.Objs = depPaths.Objs.Append(objs)
- depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags...)
- depPaths.ReexportedFlagsDeps = append(depPaths.ReexportedFlagsDeps, deps...)
+ reexportExporter(exporter)
return
}
}
@@ -1710,18 +1728,20 @@
}
if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
- flags := i.exportedFlags()
- deps := i.exportedFlagsDeps()
- depPaths.Flags = append(depPaths.Flags, flags...)
- depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, deps...)
+ depPaths.IncludeDirs = append(depPaths.IncludeDirs, i.exportedDirs()...)
+ depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, i.exportedSystemDirs()...)
+ depPaths.GeneratedHeaders = append(depPaths.GeneratedHeaders, i.exportedDeps()...)
+ depPaths.Flags = append(depPaths.Flags, i.exportedFlags()...)
if t.reexportFlags {
- depPaths.ReexportedFlags = append(depPaths.ReexportedFlags, flags...)
- depPaths.ReexportedFlagsDeps = append(depPaths.ReexportedFlagsDeps, deps...)
+ reexportExporter(i)
// Add these re-exported flags to help header-abi-dumper to infer the abi exported by a library.
// Re-exported shared library headers must be included as well since they can help us with type information
// about template instantiations (instantiated from their headers).
- c.sabi.Properties.ReexportedIncludeFlags = append(c.sabi.Properties.ReexportedIncludeFlags, flags...)
+ // -isystem headers are not included since for bionic libraries, abi-filtering is taken care of by version
+ // scripts.
+ c.sabi.Properties.ReexportedIncludes = append(
+ c.sabi.Properties.ReexportedIncludes, i.exportedDirs()...)
}
}
@@ -1883,12 +1903,16 @@
// Dedup exported flags from dependencies
depPaths.Flags = android.FirstUniqueStrings(depPaths.Flags)
+ depPaths.IncludeDirs = android.FirstUniqueStrings(depPaths.IncludeDirs)
+ depPaths.SystemIncludeDirs = android.FirstUniqueStrings(depPaths.SystemIncludeDirs)
depPaths.GeneratedHeaders = android.FirstUniquePaths(depPaths.GeneratedHeaders)
+ depPaths.ReexportedDirs = android.FirstUniqueStrings(depPaths.ReexportedDirs)
+ depPaths.ReexportedSystemDirs = android.FirstUniqueStrings(depPaths.ReexportedSystemDirs)
depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags)
- depPaths.ReexportedFlagsDeps = android.FirstUniquePaths(depPaths.ReexportedFlagsDeps)
+ depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps)
if c.sabi != nil {
- c.sabi.Properties.ReexportedIncludeFlags = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludeFlags)
+ c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes)
}
return depPaths
@@ -2036,9 +2060,6 @@
android.ApexModuleBase
}
-func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-}
-
// cc_defaults provides a set of properties that can be inherited by other cc
// modules. A module can use the properties from a cc_defaults using
// `defaults: ["<:default_module_name>"]`. Properties of both modules are
diff --git a/cc/compiler.go b/cc/compiler.go
index 7667ae7..fd6184b 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -225,11 +225,6 @@
deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
}
- if compiler.hasSrcExt(".sysprop") {
- deps.HeaderLibs = append(deps.HeaderLibs, "libbase_headers")
- deps.SharedLibs = append(deps.SharedLibs, "liblog")
- }
-
if Bool(compiler.Properties.Openmp) {
deps.StaticLibs = append(deps.StaticLibs, "libomp")
}
diff --git a/cc/config/global.go b/cc/config/global.go
index 24075d9..a27246e 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -122,8 +122,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r353983c"
- ClangDefaultShortVersion = "9.0.3"
+ ClangDefaultVersion = "clang-r353983d"
+ ClangDefaultShortVersion = "9.0.4"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/gen.go b/cc/gen.go
index ae761d0..1d30dab 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -16,6 +16,7 @@
import (
"path/filepath"
+ "strings"
"github.com/google/blueprint"
@@ -36,22 +37,13 @@
CommandDeps: []string{"$lexCmd"},
})
- aidl = pctx.AndroidStaticRule("aidl",
- blueprint.RuleParams{
- Command: "$aidlCmd -d${out}.d --ninja $aidlFlags $in $outDir $out",
- CommandDeps: []string{"$aidlCmd"},
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- },
- "aidlFlags", "outDir")
-
sysprop = pctx.AndroidStaticRule("sysprop",
blueprint.RuleParams{
- Command: "$syspropCmd --header-dir=$headerOutDir --system-header-dir=$systemOutDir " +
+ Command: "$syspropCmd --header-dir=$headerOutDir --public-header-dir=$publicOutDir " +
"--source-dir=$srcOutDir --include-name=$includeName $in",
CommandDeps: []string{"$syspropCmd"},
},
- "headerOutDir", "systemOutDir", "srcOutDir", "includeName")
+ "headerOutDir", "publicOutDir", "srcOutDir", "includeName")
windmc = pctx.AndroidStaticRule("windmc",
blueprint.RuleParams{
@@ -114,20 +106,37 @@
return ret
}
-func genAidl(ctx android.ModuleContext, aidlFile android.Path, outFile android.ModuleGenPath, aidlFlags string) android.Paths {
- ctx.Build(pctx, android.BuildParams{
- Rule: aidl,
- Description: "aidl " + aidlFile.Rel(),
- Output: outFile,
- Input: aidlFile,
- Args: map[string]string{
- "aidlFlags": aidlFlags,
- "outDir": android.PathForModuleGen(ctx, "aidl").String(),
- },
- })
+func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path,
+ outFile, depFile android.ModuleGenPath, aidlFlags string) android.Paths {
- // TODO: This should return the generated headers, not the source file.
- return android.Paths{outFile}
+ aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
+ baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
+ shortName := strings.TrimPrefix(baseName, "I")
+
+ outDir := android.PathForModuleGen(ctx, "aidl")
+ headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
+ headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
+ headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
+
+ cmd := rule.Command()
+ cmd.Tool(ctx.Config().HostToolPath(ctx, "aidl-cpp")).
+ FlagWithDepFile("-d", depFile).
+ Flag("--ninja").
+ Flag(aidlFlags).
+ Input(aidlFile).
+ OutputDir().
+ Output(outFile).
+ ImplicitOutputs(android.WritablePaths{
+ headerI,
+ headerBn,
+ headerBp,
+ })
+
+ return android.Paths{
+ headerI,
+ headerBn,
+ headerBp,
+ }
}
func genLex(ctx android.ModuleContext, lexFile android.Path, outFile android.ModuleGenPath) {
@@ -141,7 +150,7 @@
func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Path) {
headerFile := android.PathForModuleGen(ctx, "sysprop", "include", syspropFile.Rel()+".h")
- systemHeaderFile := android.PathForModuleGen(ctx, "sysprop/system", "include", syspropFile.Rel()+".h")
+ publicHeaderFile := android.PathForModuleGen(ctx, "sysprop/public", "include", syspropFile.Rel()+".h")
cppFile := android.PathForModuleGen(ctx, "sysprop", syspropFile.Rel()+".cpp")
ctx.Build(pctx, android.BuildParams{
@@ -152,7 +161,7 @@
Input: syspropFile,
Args: map[string]string{
"headerOutDir": filepath.Dir(headerFile.String()),
- "systemOutDir": filepath.Dir(systemHeaderFile.String()),
+ "publicOutDir": filepath.Dir(publicHeaderFile.String()),
"srcOutDir": filepath.Dir(cppFile.String()),
"includeName": syspropFile.Rel() + ".h",
},
@@ -187,6 +196,8 @@
var deps android.Paths
var rsFiles android.Paths
+ var aidlRule *android.RuleBuilder
+
var yaccRule_ *android.RuleBuilder
yaccRule := func() *android.RuleBuilder {
if yaccRule_ == nil {
@@ -218,9 +229,13 @@
srcFiles[i] = ccFile
deps = append(deps, headerFile)
case ".aidl":
+ if aidlRule == nil {
+ aidlRule = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "aidl"))
+ }
cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
+ depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
srcFiles[i] = cppFile
- deps = append(deps, genAidl(ctx, srcFile, cppFile, buildFlags.aidlFlags)...)
+ deps = append(deps, genAidl(ctx, aidlRule, srcFile, cppFile, depFile, buildFlags.aidlFlags)...)
case ".rs", ".fs":
cppFile := rsGeneratedCppFile(ctx, srcFile)
rsFiles = append(rsFiles, srcFiles[i])
@@ -236,6 +251,10 @@
}
}
+ if aidlRule != nil {
+ aidlRule.Build(pctx, ctx, "aidl", "gen aidl")
+ }
+
if yaccRule_ != nil {
yaccRule_.Build(pctx, ctx, "yacc", "gen yacc")
}
diff --git a/cc/gen_test.go b/cc/gen_test.go
index a0f7308..e4219d9 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -15,6 +15,7 @@
package cc
import (
+ "path/filepath"
"testing"
)
@@ -32,7 +33,7 @@
aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
- if !inList("-I"+aidl.Args["outDir"], libfoo.flags.GlobalFlags) {
+ if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
t.Errorf("missing aidl includes in global flags")
}
})
@@ -55,7 +56,7 @@
aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
- if !inList("-I"+aidl.Args["outDir"], libfoo.flags.GlobalFlags) {
+ if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
t.Errorf("missing aidl includes in global flags")
}
})
diff --git a/cc/genrule.go b/cc/genrule.go
index decf6ea..e594f4b 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -42,5 +42,7 @@
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth)
+ android.InitApexModule(module)
+
return module
}
diff --git a/cc/kernel_headers.go b/cc/kernel_headers.go
index c1da578..fff419e 100644
--- a/cc/kernel_headers.go
+++ b/cc/kernel_headers.go
@@ -25,9 +25,7 @@
func (stub *kernelHeadersDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
if ctx.Device() {
f := &stub.libraryDecorator.flagExporter
- for _, dir := range ctx.DeviceConfig().DeviceKernelHeaderDirs() {
- f.flags = append(f.flags, "-isystem "+dir)
- }
+ f.reexportSystemDirs(ctx.DeviceConfig().DeviceKernelHeaderDirs()...)
}
return stub.libraryDecorator.linkStatic(ctx, flags, deps, objs)
}
diff --git a/cc/library.go b/cc/library.go
index d9c00dc..f98cd36 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -15,6 +15,7 @@
package cc
import (
+ "fmt"
"io"
"path/filepath"
"regexp"
@@ -207,8 +208,10 @@
type flagExporter struct {
Properties FlagExporterProperties
- flags []string
- flagsDeps android.Paths
+ dirs []string
+ systemDirs []string
+ flags []string
+ deps android.Paths
}
func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
@@ -219,32 +222,57 @@
}
}
-func (f *flagExporter) exportIncludes(ctx ModuleContext, inc string) {
- includeDirs := f.exportedIncludes(ctx)
- for _, dir := range includeDirs.Strings() {
- f.flags = append(f.flags, inc+dir)
- }
+func (f *flagExporter) exportIncludes(ctx ModuleContext) {
+ f.dirs = append(f.dirs, f.exportedIncludes(ctx).Strings()...)
}
-func (f *flagExporter) reexportFlags(flags []string) {
+func (f *flagExporter) exportIncludesAsSystem(ctx ModuleContext) {
+ f.systemDirs = append(f.systemDirs, f.exportedIncludes(ctx).Strings()...)
+}
+
+func (f *flagExporter) reexportDirs(dirs ...string) {
+ f.dirs = append(f.dirs, dirs...)
+}
+
+func (f *flagExporter) reexportSystemDirs(dirs ...string) {
+ f.systemDirs = append(f.systemDirs, dirs...)
+}
+
+func (f *flagExporter) reexportFlags(flags ...string) {
+ for _, flag := range flags {
+ if strings.HasPrefix(flag, "-I") || strings.HasPrefix(flag, "-isystem") {
+ panic(fmt.Errorf("Exporting invalid flag %q: "+
+ "use reexportDirs or reexportSystemDirs to export directories", flag))
+ }
+ }
f.flags = append(f.flags, flags...)
}
-func (f *flagExporter) reexportDeps(deps android.Paths) {
- f.flagsDeps = append(f.flagsDeps, deps...)
+func (f *flagExporter) reexportDeps(deps ...android.Path) {
+ f.deps = append(f.deps, deps...)
+}
+
+func (f *flagExporter) exportedDirs() []string {
+ return f.dirs
+}
+
+func (f *flagExporter) exportedSystemDirs() []string {
+ return f.systemDirs
}
func (f *flagExporter) exportedFlags() []string {
return f.flags
}
-func (f *flagExporter) exportedFlagsDeps() android.Paths {
- return f.flagsDeps
+func (f *flagExporter) exportedDeps() android.Paths {
+ return f.deps
}
type exportedFlagsProducer interface {
+ exportedDirs() []string
+ exportedSystemDirs() []string
exportedFlags() []string
- exportedFlagsDeps() android.Paths
+ exportedDeps() android.Paths
}
var _ exportedFlagsProducer = (*flagExporter)(nil)
@@ -256,9 +284,7 @@
MutatedProperties LibraryMutatedProperties
// For reusing static library objects for shared library
- reuseObjects Objects
- reuseExportedFlags []string
- reuseExportedDeps android.Paths
+ reuseObjects Objects
// table-of-contents file to optimize out relinking when possible
tocFile android.OptionalPath
@@ -360,9 +386,10 @@
)
}
} else {
- f = append(f,
- "-shared",
- "-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
+ f = append(f, "-shared")
+ if !ctx.Windows() {
+ f = append(f, "-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
+ }
}
flags.LdFlags = append(f, flags.LdFlags...)
@@ -404,25 +431,6 @@
return flags
}
-func extractExportIncludesFromFlags(flags []string) []string {
- // This method is used in the generation of rules which produce
- // abi-dumps for source files. Exported headers are needed to infer the
- // abi exported by a library and filter out the rest of the abi dumped
- // from a source. We extract the include flags exported by a library.
- // This includes the flags exported which are re-exported from static
- // library dependencies, exported header library dependencies and
- // generated header dependencies. -isystem headers are not included
- // since for bionic libraries, abi-filtering is taken care of by version
- // scripts.
- var exportedIncludes []string
- for _, flag := range flags {
- if strings.HasPrefix(flag, "-I") {
- exportedIncludes = append(exportedIncludes, flag)
- }
- }
- return exportedIncludes
-}
-
func (library *libraryDecorator) shouldCreateVndkSourceAbiDump(ctx ModuleContext) bool {
if library.Properties.Header_abi_checker.Enabled != nil {
return Bool(library.Properties.Header_abi_checker.Enabled)
@@ -455,8 +463,8 @@
for _, dir := range exportIncludeDirs.Strings() {
SourceAbiFlags = append(SourceAbiFlags, "-I"+dir)
}
- for _, reexportedInclude := range extractExportIncludesFromFlags(library.sabi.Properties.ReexportedIncludeFlags) {
- SourceAbiFlags = append(SourceAbiFlags, reexportedInclude)
+ for _, reexportedInclude := range library.sabi.Properties.ReexportedIncludes {
+ SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
}
flags.SAbiFlags = SourceAbiFlags
total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) + len(library.Properties.Shared.Srcs) +
@@ -486,7 +494,7 @@
getWholeStaticMissingDeps() []string
static() bool
objs() Objects
- reuseObjs() (Objects, []string, android.Paths)
+ reuseObjs() (Objects, exportedFlagsProducer)
toc() android.OptionalPath
// Returns true if the build options for the module have selected a static or shared build
@@ -643,7 +651,7 @@
TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
- library.coverageOutputFile = TransformCoverageFilesToLib(ctx, library.objects, builderFlags,
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects,
ctx.ModuleName()+library.MutatedProperties.VariantName)
library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
@@ -696,6 +704,14 @@
outputFile := android.PathForModuleOut(ctx, fileName)
ret := outputFile
+ var implicitOutputs android.WritablePaths
+ if ctx.Windows() {
+ importLibraryPath := android.PathForModuleOut(ctx, pathtools.ReplaceExtension(fileName, "lib"))
+
+ flags.LdFlags = append(flags.LdFlags, "-Wl,--out-implib="+importLibraryPath.String())
+ implicitOutputs = append(implicitOutputs, importLibraryPath)
+ }
+
builderFlags := flagsToBuilderFlags(flags)
// Optimize out relinking against shared libraries whose interface hasn't changed by
@@ -747,7 +763,7 @@
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
- linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile)
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -755,7 +771,7 @@
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
- library.coverageOutputFile = TransformCoverageFilesToLib(ctx, objs, builderFlags, library.getLibName(ctx))
+ library.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName, ret)
return ret
@@ -805,8 +821,8 @@
for _, dir := range exportIncludeDirs.Strings() {
SourceAbiFlags = append(SourceAbiFlags, "-I"+dir)
}
- for _, reexportedInclude := range extractExportIncludesFromFlags(library.sabi.Properties.ReexportedIncludeFlags) {
- SourceAbiFlags = append(SourceAbiFlags, reexportedInclude)
+ for _, reexportedInclude := range library.sabi.Properties.ReexportedIncludes {
+ SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
@@ -833,19 +849,17 @@
out = library.linkShared(ctx, flags, deps, objs)
}
- library.exportIncludes(ctx, "-I")
- library.reexportFlags(deps.ReexportedFlags)
- library.reexportDeps(deps.ReexportedFlagsDeps)
+ library.exportIncludes(ctx)
+ library.reexportDirs(deps.ReexportedDirs...)
+ library.reexportSystemDirs(deps.ReexportedSystemDirs...)
+ library.reexportFlags(deps.ReexportedFlags...)
+ library.reexportDeps(deps.ReexportedDeps...)
if Bool(library.Properties.Aidl.Export_aidl_headers) {
if library.baseCompiler.hasSrcExt(".aidl") {
- flags := []string{
- "-I" + android.PathForModuleGen(ctx, "aidl").String(),
- }
- library.reexportFlags(flags)
- library.reuseExportedFlags = append(library.reuseExportedFlags, flags...)
- library.reexportDeps(library.baseCompiler.pathDeps) // TODO: restrict to aidl deps
- library.reuseExportedDeps = append(library.reuseExportedDeps, library.baseCompiler.pathDeps...)
+ dir := android.PathForModuleGen(ctx, "aidl").String()
+ library.reexportDirs(dir)
+ library.reexportDeps(library.baseCompiler.pathDeps...) // TODO: restrict to aidl deps
}
}
@@ -853,45 +867,34 @@
if library.baseCompiler.hasSrcExt(".proto") {
includes := []string{}
if flags.proto.CanonicalPathFromRoot {
- includes = append(includes, "-I"+flags.proto.SubDir.String())
+ includes = append(includes, flags.proto.SubDir.String())
}
- includes = append(includes, "-I"+flags.proto.Dir.String())
- library.reexportFlags(includes)
- library.reuseExportedFlags = append(library.reuseExportedFlags, includes...)
- library.reexportDeps(library.baseCompiler.pathDeps) // TODO: restrict to proto deps
- library.reuseExportedDeps = append(library.reuseExportedDeps, library.baseCompiler.pathDeps...)
+ includes = append(includes, flags.proto.Dir.String())
+ library.reexportDirs(includes...)
+ library.reexportDeps(library.baseCompiler.pathDeps...) // TODO: restrict to proto deps
}
}
if library.baseCompiler.hasSrcExt(".sysprop") {
- internalFlags := []string{
- "-I" + android.PathForModuleGen(ctx, "sysprop", "include").String(),
- }
- systemFlags := []string{
- "-I" + android.PathForModuleGen(ctx, "sysprop/system", "include").String(),
- }
-
- flags := internalFlags
-
+ dir := android.PathForModuleGen(ctx, "sysprop", "include").String()
if library.Properties.Sysprop.Platform != nil {
isProduct := ctx.ProductSpecific() && !ctx.useVndk()
isVendor := ctx.useVndk()
isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
- useSystem := isProduct || (isOwnerPlatform == isVendor)
+ usePublic := isProduct || (isOwnerPlatform == isVendor)
- if useSystem {
- flags = systemFlags
+ if usePublic {
+ dir = android.PathForModuleGen(ctx, "sysprop/public", "include").String()
}
}
- library.reexportFlags(flags)
- library.reexportDeps(library.baseCompiler.pathDeps)
- library.reuseExportedFlags = append(library.reuseExportedFlags, flags...)
+ library.reexportDirs(dir)
+ library.reexportDeps(library.baseCompiler.pathDeps...)
}
if library.buildStubs() {
- library.reexportFlags([]string{"-D" + versioningMacroName(ctx.ModuleName()) + "=" + library.stubsVersion()})
+ library.reexportFlags("-D" + versioningMacroName(ctx.ModuleName()) + "=" + library.stubsVersion())
}
return out
@@ -913,8 +916,8 @@
return library.objects
}
-func (library *libraryDecorator) reuseObjs() (Objects, []string, android.Paths) {
- return library.reuseObjects, library.reuseExportedFlags, library.reuseExportedDeps
+func (library *libraryDecorator) reuseObjs() (Objects, exportedFlagsProducer) {
+ return library.reuseObjects, &library.flagExporter
}
func (library *libraryDecorator) toc() android.OptionalPath {
diff --git a/cc/linker.go b/cc/linker.go
index fafefdc..dda2fcb 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -295,10 +295,6 @@
if ctx.Darwin() {
return false
}
- // http://b/110800681 - lld cannot link Android's Windows modules yet.
- if ctx.Windows() {
- return false
- }
if linker.Properties.Use_clang_lld != nil {
return Bool(linker.Properties.Use_clang_lld)
}
@@ -352,7 +348,7 @@
// darwin defaults to treating undefined symbols as errors
flags.LdFlags = append(flags.LdFlags, "-Wl,-undefined,dynamic_lookup")
}
- } else if !ctx.Darwin() {
+ } else if !ctx.Darwin() && !ctx.Windows() {
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
}
@@ -389,7 +385,7 @@
flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
- if ctx.Host() {
+ if ctx.Host() && !ctx.Windows() {
rpath_prefix := `\$$ORIGIN/`
if ctx.Darwin() {
rpath_prefix = "@loader_path/"
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 6cdf5c7..8290103 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -134,6 +134,7 @@
if !Bool(stub.Properties.Unversioned) {
linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
}
if len(stub.Properties.Export_preprocessed_headers) > 0 {
@@ -144,17 +145,17 @@
timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
}
- includePrefix := "-I"
if Bool(stub.Properties.Export_headers_as_system) {
- includePrefix = "-isystem "
+ stub.reexportSystemDirs(genHeaderOutDir.String())
+ } else {
+ stub.reexportDirs(genHeaderOutDir.String())
}
- stub.reexportFlags([]string{includePrefix + genHeaderOutDir.String()})
- stub.reexportDeps(timestampFiles)
+ stub.reexportDeps(timestampFiles...)
}
if Bool(stub.Properties.Export_headers_as_system) {
- stub.exportIncludes(ctx, "-isystem ")
+ stub.exportIncludesAsSystem(ctx)
stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index ff990b5..969cb3f 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -121,7 +121,7 @@
}
}
-func normalizeNdkApiLevel(ctx android.BaseContext, apiLevel string,
+func normalizeNdkApiLevel(ctx android.BaseModuleContext, apiLevel string,
arch android.Arch) (string, error) {
if apiLevel == "current" {
@@ -167,7 +167,7 @@
return strconv.Atoi(firstSupportedVersion)
}
-func shouldUseVersionScript(ctx android.BaseContext, stub *stubDecorator) (bool, error) {
+func shouldUseVersionScript(ctx android.BaseModuleContext, stub *stubDecorator) (bool, error) {
// unversioned_until is normally empty, in which case we should use the version script.
if String(stub.properties.Unversioned_until) == "" {
return true, nil
@@ -337,6 +337,7 @@
if useVersionScript {
linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
}
return stub.libraryDecorator.link(ctx, flags, deps, objs)
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index fb16887..4356732 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -151,7 +151,7 @@
ctx.ModuleErrorf("NDK prebuilt libraries must have an ndk_lib prefixed name")
}
- ndk.exportIncludes(ctx, "-isystem ")
+ ndk.exportIncludesAsSystem(ctx)
libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_")
libExt := flags.Toolchain.ShlibSuffix()
diff --git a/cc/pgo.go b/cc/pgo.go
index 7334ea2..4e915ff 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -27,10 +27,9 @@
var (
// Add flags to ignore warnings that profiles are old or missing for
- // some functions, and turn on the experimental new pass manager.
+ // some functions.
profileUseOtherFlags = []string{
"-Wno-backend-plugin",
- "-fexperimental-new-pass-manager",
}
globalPgoProfileProjects = []string{
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index f92c50d..dc6c43a 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -85,9 +85,11 @@
flags Flags, deps PathDeps, objs Objects) android.Path {
// TODO(ccross): verify shared library dependencies
if len(p.properties.Srcs) > 0 {
- p.libraryDecorator.exportIncludes(ctx, "-I")
- p.libraryDecorator.reexportFlags(deps.ReexportedFlags)
- p.libraryDecorator.reexportDeps(deps.ReexportedFlagsDeps)
+ p.libraryDecorator.exportIncludes(ctx)
+ p.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
+ p.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
+ p.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
+ p.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
builderFlags := flagsToBuilderFlags(flags)
diff --git a/cc/rs.go b/cc/rs.go
index 5421b92..fbc6bfb 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -72,11 +72,12 @@
stampFile := android.PathForModuleGen(ctx, "rs", "rs.stamp")
depFiles := make(android.WritablePaths, 0, len(rsFiles))
genFiles := make(android.WritablePaths, 0, 2*len(rsFiles))
+ headers := make(android.Paths, 0, len(rsFiles))
for _, rsFile := range rsFiles {
depFiles = append(depFiles, rsGeneratedDepFile(ctx, rsFile))
- genFiles = append(genFiles,
- rsGeneratedCppFile(ctx, rsFile),
- rsGeneratedHFile(ctx, rsFile))
+ headerFile := rsGeneratedHFile(ctx, rsFile)
+ genFiles = append(genFiles, rsGeneratedCppFile(ctx, rsFile), headerFile)
+ headers = append(headers, headerFile)
}
ctx.Build(pctx, android.BuildParams{
@@ -92,7 +93,7 @@
},
})
- return android.Paths{stampFile}
+ return headers
}
func rsFlags(ctx ModuleContext, flags Flags, properties *BaseCompilerProperties) Flags {
diff --git a/cc/sabi.go b/cc/sabi.go
index 451176f..ae7b31d 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -28,8 +28,8 @@
)
type SAbiProperties struct {
- CreateSAbiDumps bool `blueprint:"mutated"`
- ReexportedIncludeFlags []string
+ CreateSAbiDumps bool `blueprint:"mutated"`
+ ReexportedIncludes []string `blueprint:"mutated"`
}
type sabi struct {
diff --git a/cc/sanitize.go b/cc/sanitize.go
index fdda7be..0af0659 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -40,7 +40,8 @@
hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
"-mllvm", "-hwasan-create-frame-descriptions=0",
"-mllvm", "-hwasan-allow-ifunc",
- "-fsanitize-hwaddress-abi=platform"}
+ "-fsanitize-hwaddress-abi=platform",
+ "-fno-experimental-new-pass-manager"}
cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blacklist.txt"}
diff --git a/cc/test.go b/cc/test.go
index dae2a37..c735fd9 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -64,6 +64,10 @@
// Test options.
Test_options TestOptions
+
+ // Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+ // with root permission.
+ Require_root *bool
}
func init() {
@@ -273,18 +277,19 @@
func (test *testBinary) install(ctx ModuleContext, file android.Path) {
test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
- optionsMap := map[string]string{}
- if Bool(test.testDecorator.Properties.Isolated) {
- optionsMap["not-shardable"] = "true"
+ var configs []tradefed.Config
+ if Bool(test.Properties.Require_root) {
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
}
-
+ if Bool(test.testDecorator.Properties.Isolated) {
+ configs = append(configs, tradefed.Option{"not-shardable", "true"})
+ }
if test.Properties.Test_options.Run_test_as != nil {
- optionsMap["run-test-as"] = String(test.Properties.Test_options.Run_test_as)
+ configs = append(configs, tradefed.Option{"run-test-as", String(test.Properties.Test_options.Run_test_as)})
}
test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
- test.Properties.Test_config_template,
- test.Properties.Test_suites, optionsMap)
+ test.Properties.Test_config_template, test.Properties.Test_suites, configs)
test.binaryDecorator.baseInstaller.dir = "nativetest"
test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -371,6 +376,10 @@
// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
// should be installed with the module.
Test_config_template *string `android:"path,arch_variant"`
+
+ // Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+ // with root permission.
+ Require_root *bool
}
type benchmarkDecorator struct {
@@ -403,8 +412,12 @@
func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
+ var configs []tradefed.Config
+ if Bool(benchmark.Properties.Require_root) {
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+ }
benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
- benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites)
+ benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
diff --git a/cc/util.go b/cc/util.go
index 3862728..2e1bb25 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -29,10 +29,6 @@
return android.JoinWithPrefix(dirs.Strings(), "-I")
}
-func includeFilesToFlags(files android.Paths) string {
- return android.JoinWithPrefix(files.Strings(), "-include ")
-}
-
func ldDirsToFlags(dirs []string) string {
return android.JoinWithPrefix(dirs, "-L")
}
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index 5738d25..f0de267 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -125,6 +125,7 @@
if !Bool(stub.Properties.Unversioned) {
linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+ flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
}
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 330c5dd..1171a65 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -156,10 +156,12 @@
}
func main() {
- writer := terminal.NewWriter(terminal.StdioImpl{})
- defer writer.Finish()
+ stdio := terminal.StdioImpl{}
- log := logger.New(writer)
+ output := terminal.NewStatusOutput(stdio.Stdout(), "",
+ build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
+
+ log := logger.New(output)
defer log.Cleanup()
flag.Parse()
@@ -172,8 +174,7 @@
stat := &status.Status{}
defer stat.Finish()
- stat.AddOutput(terminal.NewStatusOutput(writer, "",
- build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
+ stat.AddOutput(output)
var failures failureCount
stat.AddOutput(&failures)
@@ -188,7 +189,7 @@
Context: ctx,
Logger: log,
Tracer: trace,
- Writer: writer,
+ Writer: output,
Status: stat,
}}
@@ -341,7 +342,7 @@
} else if failures > 1 {
log.Fatalf("%d failures", failures)
} else {
- writer.Print("Success")
+ fmt.Fprintln(output, "Success")
}
}
@@ -386,7 +387,7 @@
Context: mpctx.Context,
Logger: log,
Tracer: mpctx.Tracer,
- Writer: terminal.NewWriter(terminal.NewCustomStdio(nil, f, f)),
+ Writer: f,
Thread: mpctx.Tracer.NewThread(product),
Status: &status.Status{},
}}
@@ -466,3 +467,8 @@
}
func (f *failureCount) Flush() {}
+
+func (f *failureCount) Write(p []byte) (int, error) {
+ // discard writes
+ return len(p), nil
+}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 5f9bd01..f5276c3 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -109,10 +109,10 @@
os.Exit(1)
}
- writer := terminal.NewWriter(c.stdio())
- defer writer.Finish()
+ output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"),
+ build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
- log := logger.New(writer)
+ log := logger.New(output)
defer log.Cleanup()
ctx, cancel := context.WithCancel(context.Background())
@@ -125,8 +125,7 @@
stat := &status.Status{}
defer stat.Finish()
- stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS"),
- build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
+ stat.AddOutput(output)
stat.AddOutput(trace.StatusTracer())
build.SetupSignals(log, cancel, func() {
@@ -140,7 +139,7 @@
Logger: log,
Metrics: met,
Tracer: trace,
- Writer: writer,
+ Writer: output,
Status: stat,
}}
@@ -312,13 +311,13 @@
func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
if config.IsVerbose() {
writer := ctx.Writer
- writer.Print("! The argument `showcommands` is no longer supported.")
- writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:")
- writer.Print("!")
- writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir))
- writer.Print("!")
- writer.Print("! Older versions are saved in verbose.log.#.gz files")
- writer.Print("")
+ fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
+ fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
+ fmt.Fprintln(writer, "!")
+ fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
+ fmt.Fprintln(writer, "!")
+ fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
+ fmt.Fprintln(writer, "")
time.Sleep(5 * time.Second)
}
diff --git a/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go
index ea755f5..a6023d3 100644
--- a/cmd/zipsync/zipsync.go
+++ b/cmd/zipsync/zipsync.go
@@ -30,6 +30,7 @@
outputDir = flag.String("d", "", "output dir")
outputFile = flag.String("l", "", "output list file")
filter = flag.String("f", "", "optional filter pattern")
+ zipPrefix = flag.String("zip-prefix", "", "optional prefix within the zip file to extract, stripping the prefix")
)
func must(err error) {
@@ -77,6 +78,10 @@
var files []string
seen := make(map[string]string)
+ if *zipPrefix != "" {
+ *zipPrefix = filepath.Clean(*zipPrefix) + "/"
+ }
+
for _, input := range inputs {
reader, err := zip.OpenReader(input)
if err != nil {
@@ -85,23 +90,30 @@
defer reader.Close()
for _, f := range reader.File {
+ name := f.Name
+ if *zipPrefix != "" {
+ if !strings.HasPrefix(name, *zipPrefix) {
+ continue
+ }
+ name = strings.TrimPrefix(name, *zipPrefix)
+ }
if *filter != "" {
- if match, err := filepath.Match(*filter, filepath.Base(f.Name)); err != nil {
+ if match, err := filepath.Match(*filter, filepath.Base(name)); err != nil {
log.Fatal(err)
} else if !match {
continue
}
}
- if filepath.IsAbs(f.Name) {
- log.Fatalf("%q in %q is an absolute path", f.Name, input)
+ if filepath.IsAbs(name) {
+ log.Fatalf("%q in %q is an absolute path", name, input)
}
- if prev, exists := seen[f.Name]; exists {
- log.Fatalf("%q found in both %q and %q", f.Name, prev, input)
+ if prev, exists := seen[name]; exists {
+ log.Fatalf("%q found in both %q and %q", name, prev, input)
}
- seen[f.Name] = input
+ seen[name] = input
- filename := filepath.Join(*outputDir, f.Name)
+ filename := filepath.Join(*outputDir, name)
if f.FileInfo().IsDir() {
must(os.MkdirAll(filename, f.FileInfo().Mode()))
} else {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 87e6747..b0657ff 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -101,6 +101,7 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
// For other packages to make their own genrules with extra
// properties
@@ -582,9 +583,6 @@
android.DefaultsModuleBase
}
-func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-}
-
func defaultsFactory() android.Module {
return DefaultsFactory()
}
diff --git a/java/aapt2.go b/java/aapt2.go
index bcc8e97..a815160 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -94,32 +94,20 @@
return ret
}
-func aapt2CompileDirs(ctx android.ModuleContext, flata android.WritablePath, dirs android.Paths, deps android.Paths) {
- ctx.Build(pctx, android.BuildParams{
- Rule: aapt2CompileRule,
- Description: "aapt2 compile dirs",
- Implicits: deps,
- Output: flata,
- Args: map[string]string{
- "outDir": flata.String(),
- // Always set --pseudo-localize, it will be stripped out later for release
- // builds that don't want it.
- "cFlags": "--pseudo-localize " + android.JoinWithPrefix(dirs.Strings(), "--dir "),
- },
- })
-}
-
var aapt2CompileZipRule = pctx.AndroidStaticRule("aapt2CompileZip",
blueprint.RuleParams{
- Command: `${config.ZipSyncCmd} -d $resZipDir $in && ` +
+ Command: `${config.ZipSyncCmd} -d $resZipDir $zipSyncFlags $in && ` +
`${config.Aapt2Cmd} compile -o $out $cFlags --legacy --dir $resZipDir`,
CommandDeps: []string{
"${config.Aapt2Cmd}",
"${config.ZipSyncCmd}",
},
- }, "cFlags", "resZipDir")
+ }, "cFlags", "resZipDir", "zipSyncFlags")
-func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path) {
+func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string) {
+ if zipPrefix != "" {
+ zipPrefix = "--zip-prefix " + zipPrefix
+ }
ctx.Build(pctx, android.BuildParams{
Rule: aapt2CompileZipRule,
Description: "aapt2 compile zip",
@@ -128,8 +116,9 @@
Args: map[string]string{
// Always set --pseudo-localize, it will be stripped out later for release
// builds that don't want it.
- "cFlags": "--pseudo-localize",
- "resZipDir": android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
+ "cFlags": "--pseudo-localize",
+ "resZipDir": android.PathForModuleOut(ctx, "aapt2", "reszip", flata.Base()).String(),
+ "zipSyncFlags": zipPrefix,
},
})
}
diff --git a/java/aar.go b/java/aar.go
index 1b84a47..47f6e5f 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -188,8 +188,7 @@
return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, resourceZips
}
-func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkContext sdkContext) {
- sdkDep := decodeSdkDep(ctx, sdkContext)
+func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkDep sdkDep) {
if sdkDep.frameworkResModule != "" {
ctx.AddVariationDependencies(nil, frameworkResTag, sdkDep.frameworkResModule)
}
@@ -245,7 +244,7 @@
for i, zip := range resZips {
flata := android.PathForModuleOut(ctx, fmt.Sprintf("reszip.%d.flata", i))
- aapt2CompileZip(ctx, flata, zip)
+ aapt2CompileZip(ctx, flata, zip, "")
compiledResDirs = append(compiledResDirs, android.Paths{flata})
}
@@ -401,8 +400,9 @@
func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
a.Module.deps(ctx)
- if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
- a.aapt.deps(ctx, sdkContext(a))
+ sdkDep := decodeSdkDep(ctx, sdkContext(a))
+ if sdkDep.hasFrameworkLibs() {
+ a.aapt.deps(ctx, sdkDep)
}
}
@@ -513,6 +513,10 @@
return a.sdkVersion()
}
+func (a *AARImport) noFrameworkLibs() bool {
+ return false
+}
+
var _ AndroidLibraryDependency = (*AARImport)(nil)
func (a *AARImport) ExportPackage() android.Path {
@@ -556,13 +560,13 @@
}
// Unzip an AAR into its constituent files and directories. Any files in Outputs that don't exist in the AAR will be
-// touched to create an empty file, and any directories in $expectedDirs will be created.
+// touched to create an empty file. The res directory is not extracted, as it will be extracted in its own rule.
var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
blueprint.RuleParams{
- Command: `rm -rf $outDir && mkdir -p $outDir $expectedDirs && ` +
- `unzip -qo -d $outDir $in && touch $out`,
+ Command: `rm -rf $outDir && mkdir -p $outDir && ` +
+ `unzip -qo -d $outDir $in && rm -rf $outDir/res && touch $out`,
},
- "expectedDirs", "outDir")
+ "outDir")
func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(a.properties.Aars) != 1 {
@@ -580,7 +584,6 @@
}
extractedAARDir := android.PathForModuleOut(ctx, "aar")
- extractedResDir := extractedAARDir.Join(ctx, "res")
a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
@@ -591,16 +594,13 @@
Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
Description: "unzip AAR",
Args: map[string]string{
- "expectedDirs": extractedResDir.String(),
- "outDir": extractedAARDir.String(),
+ "outDir": extractedAARDir.String(),
},
})
compiledResDir := android.PathForModuleOut(ctx, "flat-res")
- aaptCompileDeps := android.Paths{a.classpathFile}
- aaptCompileDirs := android.Paths{extractedResDir}
flata := compiledResDir.Join(ctx, "gen_res.flata")
- aapt2CompileDirs(ctx, flata, aaptCompileDirs, aaptCompileDeps)
+ aapt2CompileZip(ctx, flata, aar, "res")
a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk")
srcJar := android.PathForModuleGen(ctx, "R.jar")
diff --git a/java/app.go b/java/app.go
index 3c8f847..cab97de 100644
--- a/java/app.go
+++ b/java/app.go
@@ -159,8 +159,9 @@
ctx.PropertyErrorf("stl", "sdk_version must be set in order to use c++_shared")
}
- if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) {
- a.aapt.deps(ctx, sdkContext(a))
+ sdkDep := decodeSdkDep(ctx, sdkContext(a))
+ if sdkDep.hasFrameworkLibs() {
+ a.aapt.deps(ctx, sdkDep)
}
embedJni := a.shouldEmbedJnis(ctx)
@@ -180,7 +181,7 @@
}
}
- a.usesLibrary.deps(ctx, Bool(a.properties.No_framework_libs))
+ a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
}
func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -482,7 +483,7 @@
return jniLibs, certificates
}
-func (a *AndroidApp) getCertString(ctx android.BaseContext) string {
+func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string {
certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
if overridden {
return ":" + certificate
@@ -783,7 +784,7 @@
ctx.AddDependency(ctx.Module(), certificateTag, cert)
}
- a.usesLibrary.deps(ctx, false)
+ a.usesLibrary.deps(ctx, true)
}
func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -937,17 +938,22 @@
usesLibraryProperties UsesLibraryProperties
}
-func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, noFrameworkLibs bool) {
- ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
- ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
- if !noFrameworkLibs {
- // dexpreopt/dexpreopt.go needs the paths to the dex jars of these libraries in case construct_context.sh needs
- // to pass them to dex2oat. Add them as a dependency so we can determine the path to the dex jar of each
- // library to dexpreopt.
- ctx.AddVariationDependencies(nil, usesLibTag,
- "org.apache.http.legacy",
- "android.hidl.base-V1.0-java",
- "android.hidl.manager-V1.0-java")
+func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
+ if !ctx.Config().UnbundledBuild() {
+ ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
+ ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
+ // Only add these extra dependencies if the module depends on framework libs. This avoids
+ // creating a cyclic dependency:
+ // e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
+ if hasFrameworkLibs {
+ // dexpreopt/dexpreopt.go needs the paths to the dex jars of these libraries in case construct_context.sh needs
+ // to pass them to dex2oat. Add them as a dependency so we can determine the path to the dex jar of each
+ // library to dexpreopt.
+ ctx.AddVariationDependencies(nil, usesLibTag,
+ "org.apache.http.legacy",
+ "android.hidl.base-V1.0-java",
+ "android.hidl.manager-V1.0-java")
+ }
}
}
diff --git a/java/app_builder.go b/java/app_builder.go
index fa77bbf..348c8b4 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -31,7 +31,7 @@
var (
Signapk = pctx.AndroidStaticRule("signapk",
blueprint.RuleParams{
- Command: `${config.JavaCmd} -Djava.library.path=$$(dirname $signapkJniLibrary) ` +
+ Command: `${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname $signapkJniLibrary) ` +
`-jar $signapkCmd $flags $certificates $in $out`,
CommandDeps: []string{"$signapkCmd", "$signapkJniLibrary"},
},
diff --git a/java/app_test.go b/java/app_test.go
index bb39c16..27802cd 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -546,7 +546,7 @@
}
}
-func TestJNIABI(t *testing.T) {
+func TestJNIABI_no_framework_libs_true(t *testing.T) {
ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
@@ -619,7 +619,80 @@
}
}
-func TestJNIPackaging(t *testing.T) {
+func TestJNIABI(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_test {
+ name: "test",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_first",
+ sdk_version: "core_platform",
+ compile_multilib: "first",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_both",
+ sdk_version: "core_platform",
+ compile_multilib: "both",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_32",
+ sdk_version: "core_platform",
+ compile_multilib: "32",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_64",
+ sdk_version: "core_platform",
+ compile_multilib: "64",
+ jni_libs: ["libjni"],
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ abis []string
+ }{
+ {"test", []string{"arm64-v8a"}},
+ {"test_first", []string{"arm64-v8a"}},
+ {"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
+ {"test_32", []string{"armeabi-v7a"}},
+ {"test_64", []string{"arm64-v8a"}},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.Output("jnilibs.zip")
+ var abis []string
+ args := strings.Fields(jniLibZip.Args["jarArgs"])
+ for i := 0; i < len(args); i++ {
+ if args[i] == "-P" {
+ abis = append(abis, filepath.Base(args[i+1]))
+ i++
+ }
+ }
+ if !reflect.DeepEqual(abis, test.abis) {
+ t.Errorf("want abis %v, got %v", test.abis, abis)
+ }
+ })
+ }
+}
+
+func TestJNIPackaging_no_framework_libs_true(t *testing.T) {
ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
@@ -700,7 +773,89 @@
}
})
}
+}
+func TestJNIPackaging(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+ cc_library {
+ name: "libjni",
+ system_shared_libs: [],
+ stl: "none",
+ }
+
+ android_app {
+ name: "app",
+ jni_libs: ["libjni"],
+ }
+
+ android_app {
+ name: "app_noembed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_app {
+ name: "app_embed",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: true,
+ }
+
+ android_test {
+ name: "test",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_noembed",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_test_helper_app {
+ name: "test_helper",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni"],
+ }
+
+ android_test_helper_app {
+ name: "test_helper_noembed",
+ sdk_version: "core_platform",
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ packaged bool
+ compressed bool
+ }{
+ {"app", false, false},
+ {"app_noembed", false, false},
+ {"app_embed", true, false},
+ {"test", true, false},
+ {"test_noembed", true, true},
+ {"test_helper", true, false},
+ {"test_helper_noembed", true, true},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ app := ctx.ModuleForTests(test.name, "android_common")
+ jniLibZip := app.MaybeOutput("jnilibs.zip")
+ if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
+ t.Errorf("expected jni packaged %v, got %v", w, g)
+ }
+
+ if jniLibZip.Rule != nil {
+ if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
+ t.Errorf("expected jni compressed %v, got %v", w, g)
+ }
+ }
+ })
+ }
}
func TestCertificates(t *testing.T) {
diff --git a/java/builder.go b/java/builder.go
index d257d1d..e1a912b 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -43,7 +43,8 @@
Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
- `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
+ `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ` +
+ `${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
`-source $javaVersion -target $javaVersion ` +
`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
@@ -64,7 +65,7 @@
turbine = pctx.AndroidStaticRule("turbine",
blueprint.RuleParams{
Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `${config.JavaCmd} -jar ${config.TurbineJar} --output $out.tmp ` +
+ `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
`--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` +
`--javacopts ${config.CommonJdkFlags} ` +
`$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
@@ -108,7 +109,7 @@
jarjar = pctx.AndroidStaticRule("jarjar",
blueprint.RuleParams{
- Command: "${config.JavaCmd} -jar ${config.JarjarCmd} process $rulesFile $in $out",
+ Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JarjarCmd} process $rulesFile $in $out",
CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"},
},
"rulesFile")
@@ -124,7 +125,7 @@
jetifier = pctx.AndroidStaticRule("jetifier",
blueprint.RuleParams{
- Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in",
+ Command: "${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JetifierJar} -l error -o $out -i $in",
CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"},
},
)
diff --git a/java/config/config.go b/java/config/config.go
index f9552d5..6ade649 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -69,6 +69,8 @@
// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
`-XDstringConcat=inline`,
}, " "))
+ pctx.StaticVariable("JavaVmFlags", "-XX:OnError=\"cat hs_err_pid%p.log\" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads")
+ pctx.StaticVariable("JavacVmFlags", "-J-XX:OnError=\"cat hs_err_pid%p.log\" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads")
pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 7cea042..fd8e3db 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -32,6 +32,7 @@
pctx.SourcePathVariable("KotlinScriptRuntimeJar", "external/kotlinc/lib/kotlin-script-runtime.jar")
pctx.SourcePathVariable("KotlinTrove4jJar", "external/kotlinc/lib/trove4j.jar")
pctx.SourcePathVariable("KotlinKaptJar", "external/kotlinc/lib/kotlin-annotation-processing.jar")
+ pctx.SourcePathVariable("KotlinAnnotationJar", "external/kotlinc/lib/annotations-13.0.jar")
pctx.SourcePathVariable("KotlinStdlibJar", KotlinStdlibJar)
// These flags silence "Illegal reflective access" warnings when running kotlinc in OpenJDK9
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 9c78511..ead298a 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -39,8 +39,8 @@
ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
ctx.Strict("ANDROID_JAVA9_HOME", "prebuilts/jdk/jdk9/${hostPrebuiltTag}")
ctx.Strict("ANDROID_JAVA_TOOLCHAIN", "${JavaToolchain}")
- ctx.Strict("JAVA", "${JavaCmd}")
- ctx.Strict("JAVAC", "${JavacCmd}")
+ ctx.Strict("JAVA", "${JavaCmd} ${JavaVmFlags}")
+ ctx.Strict("JAVAC", "${JavacCmd} ${JavacVmFlags}")
ctx.Strict("JAR", "${JarCmd}")
ctx.Strict("JAR_ARGS", "${JarArgsCmd}")
ctx.Strict("JAVADOC", "${JavadocCmd}")
@@ -58,8 +58,8 @@
ctx.Strict("ERROR_PRONE_CHECKS", "${ErrorProneChecks}")
}
- ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
- ctx.Strict("HOST_JAVAC", "${JavacCmd} ${CommonJdkFlags}")
+ ctx.Strict("TARGET_JAVAC", "${JavacCmd} ${JavacVmFlags} ${CommonJdkFlags}")
+ ctx.Strict("HOST_JAVAC", "${JavacCmd} ${JavacVmFlags} ${CommonJdkFlags}")
ctx.Strict("JLINK", "${JlinkCmd}")
ctx.Strict("JMOD", "${JmodCmd}")
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 0ec7799..a8cf1c0 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -73,7 +73,7 @@
Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
`mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
+ `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
`$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
`$opts && ` +
`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
@@ -95,7 +95,7 @@
blueprint.RuleParams{
Command: `( rm -rf "$srcJarDir" && mkdir -p "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
+ `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.MetalavaJar} -encoding UTF-8 -source $javaVersion @$out.rsp @$srcJarDir/list ` +
`$bootclasspathArgs $classpathArgs $sourcepathArgs --no-banner --color --quiet --format=v2 ` +
`$opts && touch $out && rm -rf "$srcJarDir") || ` +
`( echo -e "$msg" ; exit 38 )`,
@@ -120,7 +120,7 @@
Command: `rm -rf "$outDir" "$srcJarDir" "$stubsDir" && ` +
`mkdir -p "$outDir" "$srcJarDir" "$stubsDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
- `${config.JavaCmd} -jar ${config.DokkaJar} $srcJarDir ` +
+ `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.DokkaJar} $srcJarDir ` +
`$classpathArgs -format dac -dacRoot /reference/kotlin -output $outDir $opts && ` +
`${config.SoongZipCmd} -write_if_changed -d -o $docZip -C $outDir -D $outDir && ` +
`${config.SoongZipCmd} -write_if_changed -jar -o $out -C $stubsDir -D $stubsDir && ` +
@@ -171,10 +171,6 @@
// list of java libraries that will be in the classpath.
Libs []string `android:"arch_variant"`
- // don't build against the default libraries (bootclasspath, ext, and framework for device
- // targets)
- No_standard_libs *bool
-
// don't build against the framework libraries (ext, and framework for device targets)
No_framework_libs *bool
@@ -538,16 +534,20 @@
return j.sdkVersion()
}
+func (j *Javadoc) noFrameworkLibs() bool {
+ return Bool(j.properties.No_framework_libs)
+}
+
func (j *Javadoc) addDeps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
- if !Bool(j.properties.No_standard_libs) {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.hasStandardLibs() {
if sdkDep.useDefaultLibs {
ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
if ctx.Config().TargetOpenJDK9() {
ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
}
- if !Bool(j.properties.No_framework_libs) {
+ if sdkDep.hasFrameworkLibs() {
ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
}
} else if sdkDep.useModule {
@@ -1751,7 +1751,7 @@
jdiff := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "jdiff.jar")
jdiffImplicits = append(jdiffImplicits, android.Paths{jdiff, d.apiXmlFile, d.lastReleasedApiXmlFile}...)
- opts := " -encoding UTF-8 -source 1.8 -J-Xmx1600m -XDignore.symbol.file " +
+ opts := " -source 1.8 -J-Xmx1600m -XDignore.symbol.file " +
"-doclet jdiff.JDiff -docletpath " + jdiff.String() + " -quiet " +
"-newapi " + strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext()) +
" -newapidir " + filepath.Dir(d.apiXmlFile.String()) +
@@ -1808,9 +1808,6 @@
android.DefaultsModuleBase
}
-func (*DocDefaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-}
-
func DocDefaultsFactory() android.Module {
module := &DocDefaults{}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index b1ddab4..cf9f492 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -15,11 +15,14 @@
package java
import (
+ "fmt"
+
"android/soong/android"
)
func init() {
android.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+ android.RegisterModuleType("hiddenapi_flags", hiddenAPIFlagsFactory)
}
type hiddenAPISingletonPathsStruct struct {
@@ -307,3 +310,48 @@
Text("fi").
Text(")")
}
+
+type hiddenAPIFlagsProperties struct {
+ // name of the file into which the flags will be copied.
+ Filename *string
+}
+
+type hiddenAPIFlags struct {
+ android.ModuleBase
+
+ properties hiddenAPIFlagsProperties
+
+ outputFilePath android.OutputPath
+}
+
+func (h *hiddenAPIFlags) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ filename := String(h.properties.Filename)
+
+ inputPath := hiddenAPISingletonPaths(ctx).flags
+ h.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+
+ // This ensures that outputFilePath has the correct name for others to
+ // use, as the source file may have a different name.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: h.outputFilePath,
+ Input: inputPath,
+ })
+}
+
+func (h *hiddenAPIFlags) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "":
+ return android.Paths{h.outputFilePath}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+// hiddenapi-flags provides access to the hiddenapi-flags.csv file generated during the build.
+func hiddenAPIFlagsFactory() android.Module {
+ module := &hiddenAPIFlags{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ return module
+}
diff --git a/java/jacoco.go b/java/jacoco.go
index 8b6d4ac..bce9822 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -31,7 +31,7 @@
jacoco = pctx.AndroidStaticRule("jacoco", blueprint.RuleParams{
Command: `rm -rf $tmpDir && mkdir -p $tmpDir && ` +
`${config.Zip2ZipCmd} -i $in -o $strippedJar $stripSpec && ` +
- `${config.JavaCmd} -jar ${config.JacocoCLIJar} ` +
+ `${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.JacocoCLIJar} ` +
` instrument --quiet --dest $tmpDir $strippedJar && ` +
`${config.Ziptime} $tmpJar && ` +
`${config.MergeZipsCmd} --ignore-duplicates -j $out $tmpJar $in`,
diff --git a/java/java.go b/java/java.go
index 5544f57..4b38451 100644
--- a/java/java.go
+++ b/java/java.go
@@ -82,10 +82,6 @@
// list of files that should be excluded from java_resources and java_resource_dirs
Exclude_java_resources []string `android:"path,arch_variant"`
- // don't build against the default libraries (bootclasspath, ext, and framework for device
- // targets)
- No_standard_libs *bool
-
// don't build against the framework libraries (ext, and framework for device targets)
No_framework_libs *bool
@@ -380,8 +376,8 @@
}
type SdkLibraryDependency interface {
- SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths
- SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths
+ SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
+ SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths
}
type SrcDependency interface {
@@ -440,6 +436,16 @@
jars android.Paths
aidl android.OptionalPath
+
+ noStandardLibs, noFrameworksLibs bool
+}
+
+func (s sdkDep) hasStandardLibs() bool {
+ return !s.noStandardLibs
+}
+
+func (s sdkDep) hasFrameworkLibs() bool {
+ return !s.noStandardLibs && !s.noFrameworksLibs
}
type jniLib struct {
@@ -448,11 +454,11 @@
target android.Target
}
-func (j *Module) shouldInstrument(ctx android.BaseContext) bool {
+func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
return j.properties.Instrument && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
}
-func (j *Module) shouldInstrumentStatic(ctx android.BaseContext) bool {
+func (j *Module) shouldInstrumentStatic(ctx android.BaseModuleContext) bool {
return j.shouldInstrument(ctx) &&
(ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_STATIC") ||
ctx.Config().UnbundledBuild())
@@ -476,14 +482,18 @@
return j.sdkVersion()
}
+func (j *Module) noFrameworkLibs() bool {
+ return Bool(j.properties.No_framework_libs)
+}
+
func (j *Module) deps(ctx android.BottomUpMutatorContext) {
if ctx.Device() {
- if !Bool(j.properties.No_standard_libs) {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.hasStandardLibs() {
if sdkDep.useDefaultLibs {
ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
- if !Bool(j.properties.No_framework_libs) {
+ if sdkDep.hasFrameworkLibs() {
ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
}
} else if sdkDep.useModule {
@@ -495,8 +505,8 @@
}
}
} else if j.deviceProperties.System_modules == nil {
- ctx.PropertyErrorf("no_standard_libs",
- "system_modules is required to be set when no_standard_libs is true, did you mean no_framework_libs?")
+ ctx.PropertyErrorf("sdk_version",
+ `system_modules is required to be set when sdk_version is "none", did you mean no_framework_libs?`)
} else if *j.deviceProperties.System_modules != "none" {
ctx.AddVariationDependencies(nil, systemModulesTag, *j.deviceProperties.System_modules)
}
@@ -664,7 +674,7 @@
return javaSdk, true
case ver == "current":
return javaSdk, false
- case ver == "":
+ case ver == "" || ver == "none" || ver == "core_platform":
return javaPlatform, false
default:
if _, err := strconv.Atoi(ver); err != nil {
@@ -842,7 +852,8 @@
var ret string
v := sdkContext.sdkVersion()
// For PDK builds, use the latest SDK version instead of "current"
- if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
+ if ctx.Config().IsPdkBuild() &&
+ (v == "" || v == "none" || v == "core_platform" || v == "current") {
sdkVersions := ctx.Config().Get(sdkVersionsKey).([]int)
latestSdkVersion := 0
if len(sdkVersions) > 0 {
@@ -861,7 +872,11 @@
ret = "1.7"
} else if ctx.Device() && sdk <= 29 || !ctx.Config().TargetOpenJDK9() {
ret = "1.8"
- } else if ctx.Device() && sdkContext.sdkVersion() != "" && sdk == android.FutureApiLevel {
+ } else if ctx.Device() &&
+ sdkContext.sdkVersion() != "" &&
+ sdkContext.sdkVersion() != "none" &&
+ sdkContext.sdkVersion() != "core_platform" &&
+ sdk == android.FutureApiLevel {
// TODO(ccross): once we generate stubs we should be able to use 1.9 for sdk_version: "current"
ret = "1.8"
} else {
@@ -913,7 +928,7 @@
flags.processor = strings.Join(deps.processorClasses, ",")
if len(flags.bootClasspath) == 0 && ctx.Host() && flags.javaVersion != "1.9" &&
- !Bool(j.properties.No_standard_libs) &&
+ decodeSdkDep(ctx, sdkContext(j)).hasStandardLibs() &&
inList(flags.javaVersion, []string{"1.6", "1.7", "1.8"}) {
// Give host-side tools a version of OpenJDK's standard libraries
// close to what they're targeting. As of Dec 2017, AOSP is only
@@ -2131,9 +2146,6 @@
android.DefaultsModuleBase
}
-func (*Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-}
-
// java_defaults provides a set of properties that can be inherited by other java or android modules.
//
// A module can use the properties from a java_defaults module using `defaults: ["defaults_module_name"]`. Each
diff --git a/java/java_test.go b/java/java_test.go
index 4d161c5..22dec07 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -212,7 +212,7 @@
setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp", "prebuilts/sdk/Android.bp"})
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
@@ -842,6 +842,19 @@
}
}
+func TestJavaLibrary(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, "", map[string][]byte{
+ "libcore/Android.bp": []byte(`
+ java_library {
+ name: "core",
+ sdk_version: "none",
+ system_modules: "none",
+ }`),
+ })
+ run(t, ctx, config)
+}
+
func TestJavaSdkLibrary(t *testing.T) {
ctx := testJava(t, `
droiddoc_template {
@@ -1000,7 +1013,7 @@
java_library {
name: "bar",
srcs: ["b.java"],
- no_standard_libs: true,
+ sdk_version: "none",
system_modules: "none",
patch_module: "java.base",
}
diff --git a/java/kotlin.go b/java/kotlin.go
index 33167ba..8306907 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -44,6 +44,7 @@
"${config.KotlinScriptRuntimeJar}",
"${config.KotlinStdlibJar}",
"${config.KotlinTrove4jJar}",
+ "${config.KotlinAnnotationJar}",
"${config.GenKotlinBuildFileCmd}",
"${config.SoongZipCmd}",
"${config.ZipSyncCmd}",
diff --git a/java/robolectric.go b/java/robolectric.go
index b87ee0d..1de56a5 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -17,6 +17,7 @@
import (
"fmt"
"io"
+ "strconv"
"strings"
"android/soong/android"
@@ -40,6 +41,9 @@
Test_options struct {
// Timeout in seconds when running the tests.
Timeout *int64
+
+ // Number of shards to use when running the tests.
+ Shards *int64
}
}
@@ -48,7 +52,8 @@
robolectricProperties robolectricProperties
- libs []string
+ libs []string
+ tests []string
}
func (r *robolectricTest) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -69,6 +74,39 @@
for _, dep := range ctx.GetDirectDepsWithTag(libTag) {
r.libs = append(r.libs, ctx.OtherModuleName(dep))
}
+
+ // TODO: this could all be removed if tradefed was used as the test runner, it will find everything
+ // annotated as a test and run it.
+ for _, src := range r.compiledJavaSrcs {
+ s := src.Rel()
+ if !strings.HasSuffix(s, "Test.java") {
+ continue
+ } else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
+ continue
+ } else if strings.HasPrefix(s, "src/") {
+ s = strings.TrimPrefix(s, "src/")
+ }
+ r.tests = append(r.tests, s)
+ }
+}
+
+func shardTests(paths []string, shards int) [][]string {
+ if shards > len(paths) {
+ shards = len(paths)
+ }
+ if shards == 0 {
+ return nil
+ }
+ ret := make([][]string, 0, shards)
+ shardSize := (len(paths) + shards - 1) / shards
+ for len(paths) > shardSize {
+ ret = append(ret, paths[0:shardSize])
+ paths = paths[shardSize:]
+ }
+ if len(paths) > 0 {
+ ret = append(ret, paths)
+ }
+ return ret
}
func (r *robolectricTest) AndroidMk() android.AndroidMkData {
@@ -77,24 +115,50 @@
data.Custom = func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
android.WriteAndroidMkData(w, data)
- fmt.Fprintln(w, "")
- fmt.Fprintln(w, "include $(CLEAR_VARS)")
- fmt.Fprintln(w, "LOCAL_MODULE := Run"+name)
- fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", name)
- fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
- fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
- if t := r.robolectricProperties.Test_options.Timeout; t != nil {
- fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
+ if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
+ shards := shardTests(r.tests, int(*s))
+ for i, shard := range shards {
+ r.writeTestRunner(w, name, "Run"+name+strconv.Itoa(i), shard)
+ }
+
+ // TODO: add rules to dist the outputs of the individual tests, or combine them together?
+ fmt.Fprintln(w, "")
+ fmt.Fprintln(w, ".PHONY:", "Run"+name)
+ fmt.Fprintln(w, "Run"+name, ": \\")
+ for i := range shards {
+ fmt.Fprintln(w, " ", "Run"+name+strconv.Itoa(i), "\\")
+ }
+ fmt.Fprintln(w, "")
+ } else {
+ r.writeTestRunner(w, name, "Run"+name, r.tests)
}
- fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
}
return data
}
+func (r *robolectricTest) writeTestRunner(w io.Writer, module, name string, tests []string) {
+ fmt.Fprintln(w, "")
+ fmt.Fprintln(w, "include $(CLEAR_VARS)")
+ fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES :=", module)
+ fmt.Fprintln(w, "LOCAL_JAVA_LIBRARIES += ", strings.Join(r.libs, " "))
+ fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
+ fmt.Fprintln(w, "LOCAL_ROBOTEST_FILES :=", strings.Join(tests, " "))
+ if t := r.robolectricProperties.Test_options.Timeout; t != nil {
+ fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
+ }
+ fmt.Fprintln(w, "-include external/robolectric-shadows/run_robotests.mk")
+
+}
+
// An android_robolectric_test module compiles tests against the Robolectric framework that can run on the local host
// instead of on a device. It also generates a rule with the name of the module prefixed with "Run" that can be
// used to run the tests. Running the tests with build rule will eventually be deprecated and replaced with atest.
+//
+// The test runner considers any file listed in srcs whose name ends with Test.java to be a test class, unless
+// it is named BaseRobolectricTest.java. The path to the each source file must exactly match the package
+// name, or match the package name when the prefix "src/" is removed.
func RobolectricTestFactory() android.Module {
module := &robolectricTest{}
diff --git a/java/robolectric_test.go b/java/robolectric_test.go
new file mode 100644
index 0000000..e89c6e7
--- /dev/null
+++ b/java/robolectric_test.go
@@ -0,0 +1,88 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "reflect"
+ "testing"
+)
+
+func Test_shardTests(t *testing.T) {
+ type args struct {
+ paths []string
+ shards int
+ }
+ tests := []struct {
+ name string
+ args args
+ want [][]string
+ }{
+ {
+ name: "empty",
+ args: args{
+ paths: nil,
+ shards: 1,
+ },
+ want: [][]string(nil),
+ },
+ {
+ name: "too many shards",
+ args: args{
+ paths: []string{"a", "b"},
+ shards: 3,
+ },
+ want: [][]string{{"a"}, {"b"}},
+ },
+ {
+ name: "single shard",
+ args: args{
+ paths: []string{"a", "b"},
+ shards: 1,
+ },
+ want: [][]string{{"a", "b"}},
+ },
+ {
+ name: "shard per input",
+ args: args{
+ paths: []string{"a", "b", "c"},
+ shards: 3,
+ },
+ want: [][]string{{"a"}, {"b"}, {"c"}},
+ },
+ {
+ name: "balanced shards",
+ args: args{
+ paths: []string{"a", "b", "c", "d"},
+ shards: 2,
+ },
+ want: [][]string{{"a", "b"}, {"c", "d"}},
+ },
+ {
+ name: "unbalanced shards",
+ args: args{
+ paths: []string{"a", "b", "c"},
+ shards: 2,
+ },
+ want: [][]string{{"a", "b"}, {"c"}},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := shardTests(tt.args.paths, tt.args.shards); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("shardTests() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/java/sdk.go b/java/sdk.go
index 506edfb..6ffe399 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -38,17 +38,20 @@
var apiFingerprintPathKey = android.NewOnceKey("apiFingerprintPathKey")
type sdkContext interface {
- // sdkVersion eturns the sdk_version property of the current module, or an empty string if it is not set.
+ // sdkVersion returns the sdk_version property of the current module, or an empty string if it is not set.
sdkVersion() string
// minSdkVersion returns the min_sdk_version property of the current module, or sdkVersion() if it is not set.
minSdkVersion() string
// targetSdkVersion returns the target_sdk_version property of the current module, or sdkVersion() if it is not set.
targetSdkVersion() string
+
+ // Temporarily provide access to the no_frameworks_libs property (where present).
+ noFrameworkLibs() bool
}
-func sdkVersionOrDefault(ctx android.BaseContext, v string) string {
+func sdkVersionOrDefault(ctx android.BaseModuleContext, v string) string {
switch v {
- case "", "current", "system_current", "test_current", "core_current":
+ case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
return ctx.Config().DefaultAppTargetSdk()
default:
return v
@@ -57,9 +60,9 @@
// Returns a sdk version as a number. For modules targeting an unreleased SDK (meaning it does not yet have a number)
// it returns android.FutureApiLevel (10000).
-func sdkVersionToNumber(ctx android.BaseContext, v string) (int, error) {
+func sdkVersionToNumber(ctx android.BaseModuleContext, v string) (int, error) {
switch v {
- case "", "current", "test_current", "system_current", "core_current":
+ case "", "none", "current", "test_current", "system_current", "core_current", "core_platform":
return ctx.Config().DefaultAppTargetSdkInt(), nil
default:
n := android.GetNumericSdkVersion(v)
@@ -71,7 +74,7 @@
}
}
-func sdkVersionToNumberAsString(ctx android.BaseContext, v string) (string, error) {
+func sdkVersionToNumberAsString(ctx android.BaseModuleContext, v string) (string, error) {
n, err := sdkVersionToNumber(ctx, v)
if err != nil {
return "", err
@@ -79,7 +82,7 @@
return strconv.Itoa(n), nil
}
-func decodeSdkDep(ctx android.BaseContext, sdkContext sdkContext) sdkDep {
+func decodeSdkDep(ctx android.BaseModuleContext, sdkContext sdkContext) sdkDep {
v := sdkContext.sdkVersion()
// For PDK builds, use the latest SDK version instead of "current"
if ctx.Config().IsPdkBuild() && (v == "" || v == "current") {
@@ -138,6 +141,9 @@
useFiles: true,
jars: android.Paths{jarPath.Path(), lambdaStubsPath},
aidl: android.OptionalPathForPath(aidlPath.Path()),
+
+ // Pass value straight through for now to match previous behavior.
+ noFrameworksLibs: sdkContext.noFrameworkLibs(),
}
}
@@ -148,6 +154,9 @@
systemModules: m + "_system_modules",
frameworkResModule: r,
aidl: android.OptionalPathForPath(aidl),
+
+ // Pass value straight through for now to match previous behavior.
+ noFrameworksLibs: sdkContext.noFrameworkLibs(),
}
if m == "core.current.stubs" {
@@ -173,7 +182,8 @@
}
}
- if ctx.Config().UnbundledBuildUsePrebuiltSdks() && v != "" {
+ if ctx.Config().UnbundledBuildUsePrebuiltSdks() &&
+ v != "" && v != "none" && v != "core_platform" {
return toPrebuilt(v)
}
@@ -182,6 +192,19 @@
return sdkDep{
useDefaultLibs: true,
frameworkResModule: "framework-res",
+
+ // Pass value straight through for now to match previous behavior.
+ noFrameworksLibs: sdkContext.noFrameworkLibs(),
+ }
+ case "none":
+ return sdkDep{
+ noStandardLibs: true,
+ }
+ case "core_platform":
+ return sdkDep{
+ useDefaultLibs: true,
+ frameworkResModule: "framework-res",
+ noFrameworksLibs: true,
}
case "current":
return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx))
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 5b65c0c..b4a3f29 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -159,7 +159,8 @@
ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
- if !Bool(module.properties.No_standard_libs) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
+ if sdkDep.hasStandardLibs() {
ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
@@ -384,7 +385,6 @@
Device_specific *bool
Product_specific *bool
Compile_dex *bool
- No_standard_libs *bool
System_modules *string
Java_version *string
Product_variables struct {
@@ -401,17 +401,22 @@
}
}{}
+ sdkVersion := module.sdkVersion(apiScope)
+ sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+ if !sdkDep.hasStandardLibs() {
+ sdkVersion = "none"
+ }
+
props.Name = proptools.StringPtr(module.stubsName(apiScope))
// sources are generated from the droiddoc
props.Srcs = []string{":" + module.docsName(apiScope)}
- props.Sdk_version = proptools.StringPtr(module.sdkVersion(apiScope))
+ props.Sdk_version = proptools.StringPtr(sdkVersion)
props.Libs = module.sdkLibraryProperties.Stub_only_libs
// Unbundled apps will use the prebult one from /prebuilts/sdk
if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
}
props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
- props.No_standard_libs = module.Library.Module.properties.No_standard_libs
props.System_modules = module.Library.Module.deviceProperties.System_modules
props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
@@ -441,13 +446,13 @@
Srcs_lib *string
Srcs_lib_whitelist_dirs []string
Srcs_lib_whitelist_pkgs []string
+ Sdk_version *string
Libs []string
Arg_files []string
Args *string
Api_tag_name *string
Api_filename *string
Removed_api_filename *string
- No_standard_libs *bool
Java_version *string
Merge_annotations_dirs []string
Merge_inclusion_annotations_dirs []string
@@ -462,9 +467,16 @@
}
}{}
+ sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+ sdkVersion := ""
+ if !sdkDep.hasStandardLibs() {
+ sdkVersion = "none"
+ }
+
props.Name = proptools.StringPtr(module.docsName(apiScope))
props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
props.Srcs = append(props.Srcs, module.sdkLibraryProperties.Api_srcs...)
+ props.Sdk_version = proptools.StringPtr(sdkVersion)
props.Installable = proptools.BoolPtr(false)
// A droiddoc module has only one Libs property and doesn't distinguish between
// shared libs and static libs. So we need to add both of these libs to Libs property.
@@ -472,7 +484,6 @@
props.Libs = append(props.Libs, module.Library.Module.properties.Static_libs...)
props.Aidl.Include_dirs = module.Library.Module.deviceProperties.Aidl.Include_dirs
props.Aidl.Local_include_dirs = module.Library.Module.deviceProperties.Aidl.Local_include_dirs
- props.No_standard_libs = module.Library.Module.properties.No_standard_libs
props.Java_version = module.Library.Module.properties.Java_version
props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
@@ -591,9 +602,9 @@
mctx.CreateModule(android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory), &etcProps)
}
-func (module *SdkLibrary) PrebuiltJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) PrebuiltJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
var api, v string
- if sdkVersion == "" {
+ if sdkVersion == "" || sdkVersion == "none" {
api = "system"
v = "current"
} else if strings.Contains(sdkVersion, "_") {
@@ -615,7 +626,7 @@
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
// This module is just a wrapper for the stubs.
if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
return module.PrebuiltJars(ctx, sdkVersion)
@@ -631,7 +642,7 @@
}
// to satisfy SdkLibraryDependency interface
-func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+func (module *SdkLibrary) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
// This module is just a wrapper for the stubs.
if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
return module.PrebuiltJars(ctx, sdkVersion)
@@ -701,7 +712,8 @@
module.createStubsLibrary(mctx, apiScopePublic)
module.createDocs(mctx, apiScopePublic)
- if !Bool(module.properties.No_standard_libs) {
+ sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+ if sdkDep.hasStandardLibs() {
// for system API stubs
module.createStubsLibrary(mctx, apiScopeSystem)
module.createDocs(mctx, apiScopeSystem)
@@ -840,13 +852,13 @@
}
// to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
// This module is just a wrapper for the prebuilt stubs.
return module.stubsPath
}
// to satisfy SdkLibraryDependency interface
-func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseContext, sdkVersion string) android.Paths {
+func (module *sdkLibraryImport) SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion string) android.Paths {
// This module is just a wrapper for the stubs.
return module.stubsPath
}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index cc4da2e..915333e 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -47,6 +47,22 @@
aidl: "-Iframework/aidl",
},
{
+ name: "no_framework_libs:true",
+ properties: `no_framework_libs:true`,
+ bootclasspath: config.DefaultBootclasspathLibraries,
+ system: config.DefaultSystemModules,
+ classpath: []string{},
+ aidl: "",
+ },
+ {
+ name: `sdk_version:"core_platform"`,
+ properties: `sdk_version:"core_platform"`,
+ bootclasspath: config.DefaultBootclasspathLibraries,
+ system: config.DefaultSystemModules,
+ classpath: []string{},
+ aidl: "",
+ },
+ {
name: "blank sdk version",
properties: `sdk_version: "",`,
bootclasspath: config.DefaultBootclasspathLibraries,
@@ -106,7 +122,7 @@
{
name: "nostdlib",
- properties: `no_standard_libs: true, system_modules: "none"`,
+ properties: `sdk_version: "none", system_modules: "none"`,
system: "none",
bootclasspath: []string{`""`},
classpath: []string{},
@@ -114,7 +130,7 @@
{
name: "nostdlib system_modules",
- properties: `no_standard_libs: true, system_modules: "core-platform-api-stubs-system-modules"`,
+ properties: `sdk_version: "none", system_modules: "core-platform-api-stubs-system-modules"`,
system: "core-platform-api-stubs-system-modules",
bootclasspath: []string{`""`},
classpath: []string{},
@@ -129,13 +145,6 @@
classpath: []string{},
},
{
- name: "host nostdlib",
- moduleType: "java_library_host",
- host: android.Host,
- properties: `no_standard_libs: true`,
- classpath: []string{},
- },
- {
name: "host supported default",
host: android.Host,
@@ -146,7 +155,7 @@
{
name: "host supported nostdlib",
host: android.Host,
- properties: `host_supported: true, no_standard_libs: true, system_modules: "none"`,
+ properties: `host_supported: true, sdk_version: "none", system_modules: "none"`,
classpath: []string{},
},
{
diff --git a/java/system_modules.go b/java/system_modules.go
index 2ec3dfb..5a86f3c 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -107,12 +107,6 @@
type SystemModulesProperties struct {
// List of java library modules that should be included in the system modules
Libs []string
-
- // List of prebuilt jars that should be included in the system modules
- Jars []string
-
- // Sdk version that should be included in the system modules
- Sdk_version *string
}
func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -123,8 +117,6 @@
jars = append(jars, dep.HeaderJars()...)
})
- jars = append(jars, android.PathsForModuleSrc(ctx, system.properties.Jars)...)
-
system.outputFile = TransformJarsToSystemModules(ctx, "java.base", jars)
}
diff --git a/java/testing.go b/java/testing.go
index e1b06a0..5d116a7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -54,8 +54,7 @@
java_library {
name: "%s",
srcs: ["a.java"],
- no_standard_libs: true,
- sdk_version: "core_current",
+ sdk_version: "none",
system_modules: "core-platform-api-stubs-system-modules",
}
`, extra)
@@ -65,8 +64,7 @@
java_library {
name: "framework",
srcs: ["a.java"],
- no_standard_libs: true,
- sdk_version: "core_current",
+ sdk_version: "none",
system_modules: "core-platform-api-stubs-system-modules",
aidl: {
export_include_dirs: ["framework/aidl"],
@@ -75,14 +73,13 @@
android_app {
name: "framework-res",
- no_framework_libs: true,
+ sdk_version: "core_platform",
}
java_library {
name: "android.hidl.base-V1.0-java",
srcs: ["a.java"],
- no_standard_libs: true,
- sdk_version: "core_current",
+ sdk_version: "none",
system_modules: "core-platform-api-stubs-system-modules",
installable: true,
}
@@ -90,8 +87,7 @@
java_library {
name: "android.hidl.manager-V1.0-java",
srcs: ["a.java"],
- no_standard_libs: true,
- sdk_version: "core_current",
+ sdk_version: "none",
system_modules: "core-platform-api-stubs-system-modules",
installable: true,
}
@@ -99,8 +95,7 @@
java_library {
name: "org.apache.http.legacy",
srcs: ["a.java"],
- no_standard_libs: true,
- sdk_version: "core_current",
+ sdk_version: "none",
system_modules: "core-platform-api-stubs-system-modules",
installable: true,
}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 3f2709e..86061c6 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -123,6 +123,8 @@
Sysprop struct {
Platform *bool
}
+ Header_libs []string
+ Shared_libs []string
}{}
ccProps.Name = proptools.StringPtr(m.CcModuleName())
@@ -130,6 +132,8 @@
ccProps.Device_specific = proptools.BoolPtr(deviceSpecific)
ccProps.Product_specific = proptools.BoolPtr(productSpecific)
ccProps.Sysprop.Platform = proptools.BoolPtr(owner == "Platform")
+ ccProps.Header_libs = []string{"libbase_headers"}
+ ccProps.Shared_libs = []string{"liblog"}
ctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &m.commonProperties, &ccProps)
}
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index b470ba5..0566036 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -313,13 +313,13 @@
vendorVariant := "android_arm64_armv8-a_vendor_static"
platformInternalPath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/include"
- platformSystemCorePath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
- platformSystemVendorPath := "libsysprop-platform/android_arm64_armv8-a_vendor_static/gen/sysprop/system/include"
+ platformPublicCorePath := "libsysprop-platform/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
+ platformPublicVendorPath := "libsysprop-platform/android_arm64_armv8-a_vendor_static/gen/sysprop/public/include"
- platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
+ platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
vendorInternalPath := "libsysprop-vendor/android_arm64_armv8-a_vendor_static/gen/sysprop/include"
- vendorSystemPath := "libsysprop-vendor/android_arm64_armv8-a_core_static/gen/sysprop/system/include"
+ vendorPublicPath := "libsysprop-vendor/android_arm64_armv8-a_core_static/gen/sysprop/public/include"
platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant)
platformFlags := platformClient.Rule("cc").Args["cFlags"]
@@ -342,20 +342,20 @@
productClient := ctx.ModuleForTests("cc-client-product", coreVariant)
productFlags := productClient.Rule("cc").Args["cFlags"]
- // Product should use platform's and vendor's system headers
+ // Product should use platform's and vendor's public headers
if !strings.Contains(productFlags, platformOnProductPath) ||
- !strings.Contains(productFlags, vendorSystemPath) {
+ !strings.Contains(productFlags, vendorPublicPath) {
t.Errorf("flags for product must contain %#v and %#v, but was %#v.",
- platformSystemCorePath, vendorSystemPath, productFlags)
+ platformPublicCorePath, vendorPublicPath, productFlags)
}
vendorClient := ctx.ModuleForTests("cc-client-vendor", vendorVariant)
vendorFlags := vendorClient.Rule("cc").Args["cFlags"]
- // Vendor should use platform's system header and vendor's internal header
- if !strings.Contains(vendorFlags, platformSystemVendorPath) ||
+ // Vendor should use platform's public header and vendor's internal header
+ if !strings.Contains(vendorFlags, platformPublicVendorPath) ||
!strings.Contains(vendorFlags, vendorInternalPath) {
t.Errorf("flags for vendor must contain %#v and %#v, but was %#v.",
- platformSystemVendorPath, vendorInternalPath, vendorFlags)
+ platformPublicVendorPath, vendorInternalPath, vendorFlags)
}
}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index da5dabe..952b022 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -16,7 +16,6 @@
import (
"fmt"
- "sort"
"strings"
"github.com/google/blueprint"
@@ -39,9 +38,9 @@
}
var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
- Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_OPTIONS}&'${extraOptions}'&g' $template > $out",
+ Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g' $template > $out",
CommandDeps: []string{"$template"},
-}, "name", "template", "extraOptions")
+}, "name", "template", "extraConfigs")
func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
if p := getTestConfig(ctx, prop); p != nil {
@@ -57,17 +56,38 @@
}
}
-func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, optionsMap map[string]string) {
- // If no test option found, delete {EXTRA_OPTIONS} line.
- var options []string
- for optionName, value := range optionsMap {
- if value != "" {
- options = append(options, fmt.Sprintf(`<option name="%s" value="%s" />`, optionName, value))
- }
+type Config interface {
+ Config() string
+}
+
+type Option struct {
+ Name string
+ Value string
+}
+
+var _ Config = Option{}
+
+func (o Option) Config() string {
+ return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
+}
+
+type Preparer struct {
+ Class string
+}
+
+var _ Config = Preparer{}
+
+func (p Preparer) Config() string {
+ return fmt.Sprintf(`<target_preparer class="%s" />`, p.Class)
+}
+
+func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
+ var configStrings []string
+ for _, config := range configs {
+ configStrings = append(configStrings, config.Config())
}
- sort.Strings(options)
- extraOptions := strings.Join(options, "\n ")
- extraOptions = proptools.NinjaAndShellEscape(extraOptions)
+ extraConfigs := strings.Join(configStrings, "\n ")
+ extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
ctx.Build(pctx, android.BuildParams{
Rule: autogenTestConfig,
@@ -76,26 +96,23 @@
Args: map[string]string{
"name": ctx.ModuleName(),
"template": template,
- "extraOptions": extraOptions,
+ "extraConfigs": extraConfigs,
},
})
}
func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string,
- optionsMap map[string]string) android.Path {
+ testConfigTemplateProp *string, testSuites []string, config []Config) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
if autogenPath != nil {
templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), optionsMap)
+ autogenTemplate(ctx, autogenPath, templatePath.String(), config)
} else {
if ctx.Device() {
- autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}",
- optionsMap)
+ autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config)
} else {
- autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}",
- optionsMap)
+ autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config)
}
}
return autogenPath
@@ -104,14 +121,14 @@
}
func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
- testConfigTemplateProp *string, testSuites []string) android.Path {
+ testConfigTemplateProp *string, testSuites []string, configs []Config) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
if autogenPath != nil {
templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
if templatePath.Valid() {
- autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+ autogenTemplate(ctx, autogenPath, templatePath.String(), configs)
} else {
- autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", nil)
+ autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs)
}
return autogenPath
}
diff --git a/ui/build/config.go b/ui/build/config.go
index c298f00..6df9529 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -61,6 +61,28 @@
const srcDirFileCheck = "build/soong/root.bp"
+type BuildAction uint
+
+const (
+ // Builds all of the modules and their dependencies of a specified directory, relative to the root
+ // directory of the source tree.
+ BUILD_MODULES_IN_A_DIRECTORY BuildAction = iota
+
+ // Builds all of the modules and their dependencies of a list of specified directories. All specified
+ // directories are relative to the root directory of the source tree.
+ BUILD_MODULES_IN_DIRECTORIES
+)
+
+// checkTopDir validates that the current directory is at the root directory of the source tree.
+func checkTopDir(ctx Context) {
+ if _, err := os.Stat(srcDirFileCheck); err != nil {
+ if os.IsNotExist(err) {
+ ctx.Fatalf("Current working directory must be the source tree. %q not found.", srcDirFileCheck)
+ }
+ ctx.Fatalln("Error verifying tree state:", err)
+ }
+}
+
func NewConfig(ctx Context, args ...string) Config {
ret := &configImpl{
environ: OsEnvironment(),
@@ -154,12 +176,7 @@
ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir()))
// Precondition: the current directory is the top of the source tree
- if _, err := os.Stat(srcDirFileCheck); err != nil {
- if os.IsNotExist(err) {
- log.Fatalf("Current working directory must be the source tree. %q not found", srcDirFileCheck)
- }
- log.Fatalln("Error verifying tree state:", err)
- }
+ checkTopDir(ctx)
if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') {
log.Println("You are building in a directory whose absolute path contains a space character:")
@@ -229,6 +246,203 @@
return Config{ret}
}
+// NewBuildActionConfig returns a build configuration based on the build action. The arguments are
+// processed based on the build action and extracts any arguments that belongs to the build action.
+func NewBuildActionConfig(action BuildAction, dir string, buildDependencies bool, ctx Context, args ...string) Config {
+ return NewConfig(ctx, getConfigArgs(action, dir, buildDependencies, ctx, args)...)
+}
+
+// getConfigArgs processes the command arguments based on the build action and creates a set of new
+// arguments to be accepted by Config.
+func getConfigArgs(action BuildAction, dir string, buildDependencies bool, ctx Context, args []string) []string {
+ // The next block of code verifies that the current directory is the root directory of the source
+ // tree. It then finds the relative path of dir based on the root directory of the source tree
+ // and verify that dir is inside of the source tree.
+ checkTopDir(ctx)
+ topDir, err := os.Getwd()
+ if err != nil {
+ ctx.Fatalf("Error retrieving top directory: %v", err)
+ }
+ dir, err = filepath.Abs(dir)
+ if err != nil {
+ ctx.Fatalf("Unable to find absolute path %s: %v", dir, err)
+ }
+ relDir, err := filepath.Rel(topDir, dir)
+ if err != nil {
+ ctx.Fatalf("Unable to find relative path %s of %s: %v", relDir, topDir, err)
+ }
+ // If there are ".." in the path, it's not in the source tree.
+ if strings.Contains(relDir, "..") {
+ ctx.Fatalf("Directory %s is not under the source tree %s", dir, topDir)
+ }
+
+ configArgs := args[:]
+
+ // If the arguments contains GET-INSTALL-PATH, change the target name prefix from MODULES-IN- to
+ // GET-INSTALL-PATH-IN- to extract the installation path instead of building the modules.
+ targetNamePrefix := "MODULES-IN-"
+ if inList("GET-INSTALL-PATH", configArgs) {
+ targetNamePrefix = "GET-INSTALL-PATH-IN-"
+ configArgs = removeFromList("GET-INSTALL-PATH", configArgs)
+ }
+
+ var buildFiles []string
+ var targets []string
+
+ switch action {
+ case BUILD_MODULES_IN_A_DIRECTORY:
+ // If dir is the root source tree, all the modules are built of the source tree are built so
+ // no need to find the build file.
+ if topDir == dir {
+ break
+ }
+ // Find the build file from the directory where the build action was triggered by traversing up
+ // the source tree. If a blank build filename is returned, simply use the directory where the build
+ // action was invoked.
+ buildFile := findBuildFile(ctx, relDir)
+ if buildFile == "" {
+ buildFile = filepath.Join(relDir, "Android.mk")
+ }
+ buildFiles = []string{buildFile}
+ targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
+ case BUILD_MODULES_IN_DIRECTORIES:
+ newConfigArgs, dirs := splitArgs(configArgs)
+ configArgs = newConfigArgs
+ targets, buildFiles = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
+ }
+
+ // This is to support building modules without building their dependencies. Soon, this will be
+ // deprecated.
+ if !buildDependencies && len(buildFiles) > 0 {
+ if err := os.Setenv("ONE_SHOT_MAKEFILE", strings.Join(buildFiles, " ")); err != nil {
+ ctx.Fatalf("Unable to set ONE_SHOT_MAKEFILE environment variable: %v", err)
+ }
+ }
+
+ // Tidy only override all other specified targets.
+ tidyOnly := os.Getenv("WITH_TIDY_ONLY")
+ if tidyOnly == "true" || tidyOnly == "1" {
+ configArgs = append(configArgs, "tidy_only")
+ } else {
+ configArgs = append(configArgs, targets...)
+ }
+
+ return configArgs
+}
+
+// convertToTarget replaces "/" to "-" in dir and pre-append the targetNamePrefix to the target name.
+func convertToTarget(dir string, targetNamePrefix string) string {
+ return targetNamePrefix + strings.ReplaceAll(dir, "/", "-")
+}
+
+// findBuildFile finds a build file (makefile or blueprint file) by looking at dir first. If not
+// found, go up one level and repeat again until one is found and the path of that build file
+// relative to the root directory of the source tree is returned. The returned filename of build
+// file is "Android.mk". If one was not found, a blank string is returned.
+func findBuildFile(ctx Context, dir string) string {
+ // If the string is empty, assume it is top directory of the source tree.
+ if dir == "" {
+ return ""
+ }
+
+ for ; dir != "."; dir = filepath.Dir(dir) {
+ for _, buildFile := range []string{"Android.bp", "Android.mk"} {
+ _, err := os.Stat(filepath.Join(dir, buildFile))
+ if err == nil {
+ // Returning the filename Android.mk as it might be used for ONE_SHOT_MAKEFILE variable.
+ return filepath.Join(dir, "Android.mk")
+ }
+ if !os.IsNotExist(err) {
+ ctx.Fatalf("Error retrieving the build file stats: %v", err)
+ }
+ }
+ }
+
+ return ""
+}
+
+// splitArgs iterates over the arguments list and splits into two lists: arguments and directories.
+func splitArgs(args []string) (newArgs []string, dirs []string) {
+ specialArgs := map[string]bool{
+ "showcommands": true,
+ "snod": true,
+ "dist": true,
+ "checkbuild": true,
+ }
+
+ newArgs = []string{}
+ dirs = []string{}
+
+ for _, arg := range args {
+ // It's a dash argument if it starts with "-" or it's a key=value pair, it's not a directory.
+ if strings.IndexRune(arg, '-') == 0 || strings.IndexRune(arg, '=') != -1 {
+ newArgs = append(newArgs, arg)
+ continue
+ }
+
+ if _, ok := specialArgs[arg]; ok {
+ newArgs = append(newArgs, arg)
+ continue
+ }
+
+ dirs = append(dirs, arg)
+ }
+
+ return newArgs, dirs
+}
+
+// getTargetsFromDirs iterates over the dirs list and creates a list of targets to build. If a
+// directory from the dirs list does not exist, a fatal error is raised. relDir is related to the
+// source root tree where the build action command was invoked. Each directory is validated if the
+// build file can be found and follows the format "dir1:target1,target2,...". Target is optional.
+func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string, buildFiles []string) {
+ for _, dir := range dirs {
+ // The directory may have specified specific modules to build. ":" is the separator to separate
+ // the directory and the list of modules.
+ s := strings.Split(dir, ":")
+ l := len(s)
+ if l > 2 { // more than one ":" was specified.
+ ctx.Fatalf("%s not in proper directory:target1,target2,... format (\":\" was specified more than once)", dir)
+ }
+
+ dir = filepath.Join(relDir, s[0])
+ if _, err := os.Stat(dir); err != nil {
+ ctx.Fatalf("couldn't find directory %s", dir)
+ }
+
+ // Verify that if there are any targets specified after ":". Each target is separated by ",".
+ var newTargets []string
+ if l == 2 && s[1] != "" {
+ newTargets = strings.Split(s[1], ",")
+ if inList("", newTargets) {
+ ctx.Fatalf("%s not in proper directory:target1,target2,... format", dir)
+ }
+ }
+
+ buildFile := findBuildFile(ctx, dir)
+ if buildFile == "" {
+ ctx.Fatalf("Build file not found for %s directory", dir)
+ }
+ buildFileDir := filepath.Dir(buildFile)
+
+ // If there are specified targets, find the build file in the directory. If dir does not
+ // contain the build file, bail out as it is required for one shot build. If there are no
+ // target specified, build all the modules in dir (or the closest one in the dir path).
+ if len(newTargets) > 0 {
+ if buildFileDir != dir {
+ ctx.Fatalf("Couldn't locate a build file from %s directory", dir)
+ }
+ } else {
+ newTargets = []string{convertToTarget(buildFileDir, targetNamePrefix)}
+ }
+
+ buildFiles = append(buildFiles, buildFile)
+ targets = append(targets, newTargets...)
+ }
+
+ return targets, buildFiles
+}
+
func (c *configImpl) parseArgs(ctx Context, args []string) {
for i := 0; i < len(args); i++ {
arg := strings.TrimSpace(args[i])
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 242e3af..1ef5456 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -17,19 +17,22 @@
import (
"bytes"
"context"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"reflect"
"strings"
"testing"
"android/soong/ui/logger"
- "android/soong/ui/terminal"
)
func testContext() Context {
return Context{&ContextImpl{
Context: context.Background(),
Logger: logger.New(&bytes.Buffer{}),
- Writer: terminal.NewWriter(terminal.NewCustomStdio(&bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{})),
+ Writer: &bytes.Buffer{},
}}
}
@@ -173,3 +176,877 @@
})
}
}
+
+func TestConfigCheckTopDir(t *testing.T) {
+ ctx := testContext()
+ buildRootDir := filepath.Dir(srcDirFileCheck)
+ expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck)
+
+ tests := []struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // ********* Action *********
+ // If set to true, the build root file is created.
+ rootBuildFile bool
+
+ // The current path where Soong is being executed.
+ path string
+
+ // ********* Validation *********
+ // Expecting error and validate the error string against expectedErrStr.
+ wantErr bool
+ }{{
+ description: "current directory is the root source tree",
+ rootBuildFile: true,
+ path: ".",
+ wantErr: false,
+ }, {
+ description: "one level deep in the source tree",
+ rootBuildFile: true,
+ path: "1",
+ wantErr: true,
+ }, {
+ description: "very deep in the source tree",
+ rootBuildFile: true,
+ path: "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7",
+ wantErr: true,
+ }, {
+ description: "outside of source tree",
+ rootBuildFile: false,
+ path: "1/2/3/4/5",
+ wantErr: true,
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ if !tt.wantErr {
+ t.Fatalf("Got unexpected error: %v", err)
+ }
+ if expectedErrStr != err.Error() {
+ t.Fatalf("expected %s, got %s", expectedErrStr, err.Error())
+ }
+ })
+
+ // Create the root source tree.
+ rootDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(rootDir)
+
+ // Create the build root file. This is to test if topDir returns an error if the build root
+ // file does not exist.
+ if tt.rootBuildFile {
+ dir := filepath.Join(rootDir, buildRootDir)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Errorf("failed to create %s directory: %v", dir, err)
+ }
+ f := filepath.Join(rootDir, srcDirFileCheck)
+ if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil {
+ t.Errorf("failed to create file %s: %v", f, err)
+ }
+ }
+
+ // Next block of code is to set the current directory.
+ dir := rootDir
+ if tt.path != "" {
+ dir = filepath.Join(dir, tt.path)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Errorf("failed to create %s directory: %v", dir, err)
+ }
+ }
+ curDir, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("failed to get the current directory: %v", err)
+ }
+ defer func() { os.Chdir(curDir) }()
+
+ if err := os.Chdir(dir); err != nil {
+ t.Fatalf("failed to change directory to %s: %v", dir, err)
+ }
+
+ checkTopDir(ctx)
+ })
+ }
+}
+
+func TestConfigConvertToTarget(t *testing.T) {
+ tests := []struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // ********* Action *********
+ // The current directory where Soong is being executed.
+ dir string
+
+ // The current prefix string to be pre-appended to the target.
+ prefix string
+
+ // ********* Validation *********
+ // The expected target to be invoked in ninja.
+ expectedTarget string
+ }{{
+ description: "one level directory in source tree",
+ dir: "test1",
+ prefix: "MODULES-IN-",
+ expectedTarget: "MODULES-IN-test1",
+ }, {
+ description: "multiple level directories in source tree",
+ dir: "test1/test2/test3/test4",
+ prefix: "GET-INSTALL-PATH-IN-",
+ expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4",
+ }}
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ target := convertToTarget(tt.dir, tt.prefix)
+ if target != tt.expectedTarget {
+ t.Errorf("expected %s, got %s for target", tt.expectedTarget, target)
+ }
+ })
+ }
+}
+
+func setTop(t *testing.T, dir string) func() {
+ curDir, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("failed to get current directory: %v", err)
+ }
+ if err := os.Chdir(dir); err != nil {
+ t.Fatalf("failed to change directory to top dir %s: %v", dir, err)
+ }
+ return func() { os.Chdir(curDir) }
+}
+
+func createBuildFiles(t *testing.T, topDir string, buildFiles []string) {
+ for _, buildFile := range buildFiles {
+ buildFile = filepath.Join(topDir, buildFile)
+ if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil {
+ t.Errorf("failed to create file %s: %v", buildFile, err)
+ }
+ }
+}
+
+func createDirectories(t *testing.T, topDir string, dirs []string) {
+ for _, dir := range dirs {
+ dir = filepath.Join(topDir, dir)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Errorf("failed to create %s directory: %v", dir, err)
+ }
+ }
+}
+
+func TestConfigGetTargets(t *testing.T) {
+ ctx := testContext()
+ tests := []struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // Directories that exist in the source tree.
+ dirsInTrees []string
+
+ // Build files that exists in the source tree.
+ buildFiles []string
+
+ // ********* Action *********
+ // Directories passed in to soong_ui.
+ dirs []string
+
+ // Current directory that the user executed the build action command.
+ curDir string
+
+ // ********* Validation *********
+ // Expected targets from the function.
+ expectedTargets []string
+
+ // Expected build from the build system.
+ expectedBuildFiles []string
+
+ // Expecting error from running test case.
+ errStr string
+ }{{
+ description: "one target dir specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2-3"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ }, {
+ description: "one target dir specified, build file does not exist",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{},
+ dirs: []string{"1/2/3"},
+ curDir: "0",
+ errStr: "Build file not found for 0/1/2/3 directory",
+ }, {
+ description: "one target dir specified, invalid targets specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{},
+ dirs: []string{"1/2/3:t1:t2"},
+ curDir: "0",
+ errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
+ }, {
+ description: "one target dir specified, no targets specified but has colon",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2-3"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ }, {
+ description: "one target dir specified, two targets specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:t1,t2"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ }, {
+ description: "one target dir specified, no targets and has a comma",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:,"},
+ curDir: "0",
+ errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
+ }, {
+ description: "one target dir specified, improper targets defined",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:,t1"},
+ curDir: "0",
+ errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
+ }, {
+ description: "one target dir specified, blank target",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:t1,"},
+ curDir: "0",
+ errStr: "0/1/2/3 not in proper directory:target1,target2,... format",
+ }, {
+ description: "one target dir specified, many targets specified",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.bp"},
+ dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+ }, {
+ description: "one target dir specified, one target specified, build file does not exist",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{},
+ dirs: []string{"1/2/3:t1"},
+ curDir: "0",
+ errStr: "Build file not found for 0/1/2/3 directory",
+ }, {
+ description: "one target dir specified, one target specified, build file not in target dir",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ dirs: []string{"1/2/3:t1"},
+ curDir: "0",
+ errStr: "Couldn't locate a build file from 0/1/2/3 directory",
+ }, {
+ description: "one target dir specified, build file not in target dir",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ dirs: []string{"1/2/3"},
+ curDir: "0",
+ expectedTargets: []string{"MODULES-IN-0-1-2"},
+ expectedBuildFiles: []string{"0/1/2/Android.mk"},
+ }, {
+ description: "multiple targets dir specified, targets specified",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ }, {
+ description: "multiple targets dir specified, one directory has targets specified",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"1/2/3:t1,t2", "3/4"},
+ curDir: "0",
+ expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ }, {
+ description: "two dirs specified, only one dir exist",
+ dirsInTrees: []string{"0/1/2/3"},
+ buildFiles: []string{"0/1/2/3/Android.mk"},
+ dirs: []string{"1/2/3:t1", "3/4"},
+ curDir: "0",
+ errStr: "couldn't find directory 0/3/4",
+ }, {
+ description: "multiple targets dirs specified at root source tree",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{"0/1/2/3:t1,t2", "0/3/4"},
+ curDir: ".",
+ expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
+ expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+ }, {
+ description: "no directories specified",
+ dirsInTrees: []string{"0/1/2/3", "0/3/4"},
+ buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+ dirs: []string{},
+ curDir: ".",
+ }}
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ if tt.errStr == "" {
+ t.Fatalf("Got unexpected error: %v", err)
+ }
+ if tt.errStr != err.Error() {
+ t.Errorf("expected %s, got %s", tt.errStr, err.Error())
+ }
+ })
+
+ // Create the root source tree.
+ topDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(topDir)
+
+ createDirectories(t, topDir, tt.dirsInTrees)
+ createBuildFiles(t, topDir, tt.buildFiles)
+ r := setTop(t, topDir)
+ defer r()
+
+ targets, buildFiles := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
+ if !reflect.DeepEqual(targets, tt.expectedTargets) {
+ t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
+ }
+ if !reflect.DeepEqual(buildFiles, tt.expectedBuildFiles) {
+ t.Errorf("expected %v, got %v for build files", tt.expectedBuildFiles, buildFiles)
+ }
+
+ // If the execution reached here and there was an expected error code, the unit test case failed.
+ if tt.errStr != "" {
+ t.Errorf("expecting error %s", tt.errStr)
+ }
+ })
+ }
+}
+
+func TestConfigFindBuildFile(t *testing.T) {
+ ctx := testContext()
+
+ tests := []struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // Array of build files to create in dir.
+ buildFiles []string
+
+ // ********* Action *********
+ // Directory to create, also the base directory is where findBuildFile is invoked.
+ dir string
+
+ // ********* Validation *********
+ // Expected build file path to find.
+ expectedBuildFile string
+ }{{
+ description: "build file exists at leaf directory",
+ buildFiles: []string{"1/2/3/Android.bp"},
+ dir: "1/2/3",
+ expectedBuildFile: "1/2/3/Android.mk",
+ }, {
+ description: "build file exists in all directory paths",
+ buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"},
+ dir: "1/2/3",
+ expectedBuildFile: "1/2/3/Android.mk",
+ }, {
+ description: "build file does not exist in all directory paths",
+ buildFiles: []string{},
+ dir: "1/2/3",
+ expectedBuildFile: "",
+ }, {
+ description: "build file exists only at top directory",
+ buildFiles: []string{"Android.bp"},
+ dir: "1/2/3",
+ expectedBuildFile: "",
+ }, {
+ description: "build file exist in a subdirectory",
+ buildFiles: []string{"1/2/Android.bp"},
+ dir: "1/2/3",
+ expectedBuildFile: "1/2/Android.mk",
+ }, {
+ description: "build file exists in a subdirectory",
+ buildFiles: []string{"1/Android.mk"},
+ dir: "1/2/3",
+ expectedBuildFile: "1/Android.mk",
+ }, {
+ description: "top directory",
+ buildFiles: []string{"Android.bp"},
+ dir: ".",
+ expectedBuildFile: "",
+ }}
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ defer logger.Recover(func(err error) {
+ t.Fatalf("Got unexpected error: %v", err)
+ })
+
+ topDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(topDir)
+
+ if tt.dir != "" {
+ createDirectories(t, topDir, []string{tt.dir})
+ }
+
+ createBuildFiles(t, topDir, tt.buildFiles)
+
+ curDir, err := os.Getwd()
+ if err != nil {
+ t.Fatalf("Could not get working directory: %v", err)
+ }
+ defer func() { os.Chdir(curDir) }()
+ if err := os.Chdir(topDir); err != nil {
+ t.Fatalf("Could not change top dir to %s: %v", topDir, err)
+ }
+
+ buildFile := findBuildFile(ctx, tt.dir)
+ if buildFile != tt.expectedBuildFile {
+ t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile)
+ }
+ })
+ }
+}
+
+func TestConfigSplitArgs(t *testing.T) {
+ tests := []struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // ********* Action *********
+ // Arguments passed in to soong_ui.
+ args []string
+
+ // ********* Validation *********
+ // Expected newArgs list after extracting the directories.
+ expectedNewArgs []string
+
+ // Expected directories
+ expectedDirs []string
+ }{{
+ description: "flags but no directories specified",
+ args: []string{"showcommands", "-j", "-k"},
+ expectedNewArgs: []string{"showcommands", "-j", "-k"},
+ expectedDirs: []string{},
+ }, {
+ description: "flags and one directory specified",
+ args: []string{"snod", "-j", "dir:target1,target2"},
+ expectedNewArgs: []string{"snod", "-j"},
+ expectedDirs: []string{"dir:target1,target2"},
+ }, {
+ description: "flags and directories specified",
+ args: []string{"dist", "-k", "dir1", "dir2:target1,target2"},
+ expectedNewArgs: []string{"dist", "-k"},
+ expectedDirs: []string{"dir1", "dir2:target1,target2"},
+ }, {
+ description: "only directories specified",
+ args: []string{"dir1", "dir2", "dir3:target1,target2"},
+ expectedNewArgs: []string{},
+ expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"},
+ }}
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ args, dirs := splitArgs(tt.args)
+ if !reflect.DeepEqual(tt.expectedNewArgs, args) {
+ t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args)
+ }
+ if !reflect.DeepEqual(tt.expectedDirs, dirs) {
+ t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs)
+ }
+ })
+ }
+}
+
+type envVar struct {
+ name string
+ value string
+}
+
+type buildActionTestCase struct {
+ // ********* Setup *********
+ // Test description.
+ description string
+
+ // Directories that exist in the source tree.
+ dirsInTrees []string
+
+ // Build files that exists in the source tree.
+ buildFiles []string
+
+ // ********* Action *********
+ // Arguments passed in to soong_ui.
+ args []string
+
+ // Directory where the build action was invoked.
+ curDir string
+
+ // WITH_TIDY_ONLY environment variable specified.
+ tidyOnly string
+
+ // ********* Validation *********
+ // Expected arguments to be in Config instance.
+ expectedArgs []string
+
+ // Expected environment variables to be set.
+ expectedEnvVars []envVar
+}
+
+func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, buildDependencies bool) {
+ ctx := testContext()
+
+ // Environment variables to set it to blank on every test case run.
+ resetEnvVars := []string{
+ "ONE_SHOT_MAKEFILE",
+ "WITH_TIDY_ONLY",
+ }
+
+ for _, name := range resetEnvVars {
+ if err := os.Unsetenv(name); err != nil {
+ t.Fatalf("failed to unset environment variable %s: %v", name, err)
+ }
+ }
+ if tt.tidyOnly != "" {
+ if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil {
+ t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err)
+ }
+ }
+
+ // Create the root source tree.
+ topDir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(topDir)
+
+ createDirectories(t, topDir, tt.dirsInTrees)
+ createBuildFiles(t, topDir, tt.buildFiles)
+
+ r := setTop(t, topDir)
+ defer r()
+
+ // The next block is to create the root build file.
+ rootBuildFileDir := filepath.Dir(srcDirFileCheck)
+ if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil {
+ t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err)
+ }
+
+ if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil {
+ t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
+ }
+
+ args := getConfigArgs(action, tt.curDir, buildDependencies, ctx, tt.args)
+ if !reflect.DeepEqual(tt.expectedArgs, args) {
+ t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
+ }
+
+ for _, env := range tt.expectedEnvVars {
+ if val := os.Getenv(env.name); val != env.value {
+ t.Errorf("expecting %s, got %s for environment variable %s", env.value, val, env.name)
+ }
+ }
+}
+
+// TODO: Remove this test case once mm shell build command has been deprecated.
+func TestGetConfigArgsBuildModulesInDirecotoryNoDeps(t *testing.T) {
+ tests := []buildActionTestCase{{
+ description: "normal execution in a directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ args: []string{"-j", "-k", "showcommands", "fake-module"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k", "showcommands", "fake-module", "MODULES-IN-0-1-2"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/Android.mk"}},
+ }, {
+ description: "makefile in parent directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/Android.mk"}},
+ }, {
+ description: "build file not found",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/Android.mk"}},
+ }, {
+ description: "build action executed at root directory",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ args: []string{},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: ""}},
+ }, {
+ description: "GET-INSTALL-PATH specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/Android.mk"}},
+ }, {
+ description: "tidy only environment variable specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "true",
+ expectedArgs: []string{"tidy_only"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/Android.mk"}},
+ }}
+ for _, tt := range tests {
+ t.Run("build action BUILD_MODULES_IN_DIR without their dependencies, "+tt.description, func(t *testing.T) {
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, false)
+ })
+ }
+}
+
+func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
+ tests := []buildActionTestCase{{
+ description: "normal execution in a directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/2/Android.mk"},
+ args: []string{"fake-module"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "build file in parent directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1"},
+ expectedEnvVars: []envVar{},
+ },
+ {
+ description: "build file in parent directory, multiple module names passed in",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"fake-module1", "fake-module2", "fake-module3"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "build file in 2nd level parent directory",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/Android.bp"},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "build action executed at root directory",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ args: []string{},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "build file not found - no error is expected to return",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{},
+ args: []string{},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "GET-INSTALL-PATH specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "tidy only environment variable specified,",
+ dirsInTrees: []string{"0/1/2"},
+ buildFiles: []string{"0/1/Android.mk"},
+ args: []string{"GET-INSTALL-PATH"},
+ curDir: "0/1/2",
+ tidyOnly: "true",
+ expectedArgs: []string{"tidy_only"},
+ expectedEnvVars: []envVar{},
+ }, {
+ description: "normal execution in root directory with args",
+ dirsInTrees: []string{},
+ buildFiles: []string{},
+ args: []string{"-j", "-k", "fake_module"},
+ curDir: "",
+ tidyOnly: "",
+ expectedArgs: []string{"-j", "-k", "fake_module"},
+ expectedEnvVars: []envVar{},
+ }}
+ for _, tt := range tests {
+ t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, true)
+ })
+ }
+}
+
+// TODO: Remove this test case once mmm shell build command has been deprecated.
+func TestGetConfigArgsBuildModulesInDirectoriesNoDeps(t *testing.T) {
+ tests := []buildActionTestCase{{
+ description: "normal execution in a directory",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"3.1/:t1,t2", "3.2/:t3,t4", "3.3/:t5,t6"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"t1", "t2", "t3", "t4", "t5", "t6"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
+ }, {
+ description: "GET-INSTALL-PATH specified",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "t6"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
+ }, {
+ description: "tidy only environment variable specified",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
+ curDir: "0/1/2",
+ tidyOnly: "1",
+ expectedArgs: []string{"tidy_only"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
+ }, {
+ description: "normal execution from top dir directory",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"0/1/2/3.1", "0/1/2/3.2/:t3,t4", "0/1/2/3.3/:t5,t6"},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "t3", "t4", "t5", "t6"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
+ }}
+ for _, tt := range tests {
+ t.Run("build action BUILD_MODULES_IN_DIRS_NO_DEPS, "+tt.description, func(t *testing.T) {
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, false)
+ })
+ }
+}
+
+func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) {
+ tests := []buildActionTestCase{{
+ description: "normal execution in a directory",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"3.1/", "3.2/", "3.3/"},
+ curDir: "0/1/2",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: ""}},
+ }, {
+ description: "GET-INSTALL-PATH specified",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"},
+ args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"},
+ curDir: "0/1",
+ tidyOnly: "",
+ expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: ""}},
+ }, {
+ description: "tidy only environment variable specified",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
+ args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"},
+ curDir: "0/1/2",
+ tidyOnly: "1",
+ expectedArgs: []string{"tidy_only"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: ""}},
+ }, {
+ description: "normal execution from top dir directory",
+ dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
+ buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"},
+ args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
+ curDir: ".",
+ tidyOnly: "",
+ expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
+ expectedEnvVars: []envVar{
+ envVar{
+ name: "ONE_SHOT_MAKEFILE",
+ value: ""}},
+ }}
+ for _, tt := range tests {
+ t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
+ testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, true)
+ })
+ }
+}
diff --git a/ui/build/context.go b/ui/build/context.go
index 249e898..7ff98ef 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -16,12 +16,12 @@
import (
"context"
+ "io"
"android/soong/ui/logger"
"android/soong/ui/metrics"
"android/soong/ui/metrics/metrics_proto"
"android/soong/ui/status"
- "android/soong/ui/terminal"
"android/soong/ui/tracer"
)
@@ -35,7 +35,7 @@
Metrics *metrics.Metrics
- Writer terminal.Writer
+ Writer io.Writer
Status *status.Status
Thread tracer.Thread
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 4335667..266130f 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -249,7 +249,7 @@
env := config.Environment()
// Print the banner like make does
if !env.IsEnvTrue("ANDROID_QUIET_BUILD") {
- ctx.Writer.Print(Banner(make_vars))
+ fmt.Fprintln(ctx.Writer, Banner(make_vars))
}
// Populate the environment
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index d92649f..c9946e2 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -79,7 +79,6 @@
// We need bzip2 here even though we provide a bzip2 binary because
// GNU tar seems to avoid calling ours.
"bzip2": Allowed,
- "date": Allowed,
"dd": Allowed,
"diff": Allowed,
"egrep": Allowed,
@@ -131,6 +130,7 @@
"cp": LinuxOnlyPrebuilt,
"comm": LinuxOnlyPrebuilt,
"cut": LinuxOnlyPrebuilt,
+ "date": LinuxOnlyPrebuilt,
"dirname": LinuxOnlyPrebuilt,
"du": LinuxOnlyPrebuilt,
"echo": LinuxOnlyPrebuilt,
diff --git a/ui/build/util.go b/ui/build/util.go
index 0676a86..75e6753 100644
--- a/ui/build/util.go
+++ b/ui/build/util.go
@@ -44,6 +44,17 @@
return indexList(s, list) != -1
}
+// removeFromlist removes all occurrences of the string in list.
+func removeFromList(s string, list []string) []string {
+ filteredList := make([]string, 0, len(list))
+ for _, ls := range list {
+ if s != ls {
+ filteredList = append(filteredList, ls)
+ }
+ }
+ return filteredList
+}
+
// ensureDirectoriesExist is a shortcut to os.MkdirAll, sending errors to the ctx logger.
func ensureDirectoriesExist(ctx Context, dirs ...string) {
for _, dir := range dirs {
diff --git a/ui/status/log.go b/ui/status/log.go
index 921aa44..7badac7 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -71,6 +71,11 @@
fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message)
}
+func (v *verboseLog) Write(p []byte) (int, error) {
+ fmt.Fprint(v.w, string(p))
+ return len(p), nil
+}
+
type errorLog struct {
w io.WriteCloser
@@ -134,3 +139,8 @@
fmt.Fprintf(e.w, "error: %s\n", message)
}
+
+func (e *errorLog) Write(p []byte) (int, error) {
+ fmt.Fprint(e.w, string(p))
+ return len(p), nil
+}
diff --git a/ui/status/status.go b/ui/status/status.go
index 46ec72e..3d8cd7a 100644
--- a/ui/status/status.go
+++ b/ui/status/status.go
@@ -173,6 +173,9 @@
// Flush is called when your outputs should be flushed / closed. No
// output is expected after this call.
Flush()
+
+ // Write lets StatusOutput implement io.Writer
+ Write(p []byte) (n int, err error)
}
// Status is the multiplexer / accumulator between ToolStatus instances (via
diff --git a/ui/status/status_test.go b/ui/status/status_test.go
index e62785f..9494582 100644
--- a/ui/status/status_test.go
+++ b/ui/status/status_test.go
@@ -27,6 +27,11 @@
func (c counterOutput) Message(level MsgLevel, msg string) {}
func (c counterOutput) Flush() {}
+func (c counterOutput) Write(p []byte) (int, error) {
+ // Discard writes
+ return len(p), nil
+}
+
func (c counterOutput) Expect(t *testing.T, counts Counts) {
if Counts(c) == counts {
return
diff --git a/ui/terminal/Android.bp b/ui/terminal/Android.bp
index 7104a50..b533b0d 100644
--- a/ui/terminal/Android.bp
+++ b/ui/terminal/Android.bp
@@ -17,11 +17,15 @@
pkgPath: "android/soong/ui/terminal",
deps: ["soong-ui-status"],
srcs: [
+ "dumb_status.go",
+ "format.go",
+ "smart_status.go",
"status.go",
- "writer.go",
+ "stdio.go",
"util.go",
],
testSrcs: [
+ "status_test.go",
"util_test.go",
],
darwin: {
diff --git a/ui/terminal/dumb_status.go b/ui/terminal/dumb_status.go
new file mode 100644
index 0000000..201770f
--- /dev/null
+++ b/ui/terminal/dumb_status.go
@@ -0,0 +1,71 @@
+// Copyright 2019 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 terminal
+
+import (
+ "fmt"
+ "io"
+
+ "android/soong/ui/status"
+)
+
+type dumbStatusOutput struct {
+ writer io.Writer
+ formatter formatter
+}
+
+// NewDumbStatusOutput returns a StatusOutput that represents the
+// current build status similarly to Ninja's built-in terminal
+// output.
+func NewDumbStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
+ return &dumbStatusOutput{
+ writer: w,
+ formatter: formatter,
+ }
+}
+
+func (s *dumbStatusOutput) Message(level status.MsgLevel, message string) {
+ if level >= status.StatusLvl {
+ fmt.Fprintln(s.writer, s.formatter.message(level, message))
+ }
+}
+
+func (s *dumbStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+}
+
+func (s *dumbStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+ str := result.Description
+ if str == "" {
+ str = result.Command
+ }
+
+ progress := s.formatter.progress(counts) + str
+
+ output := s.formatter.result(result)
+ output = string(stripAnsiEscapes([]byte(output)))
+
+ if output != "" {
+ fmt.Fprint(s.writer, progress, "\n", output)
+ } else {
+ fmt.Fprintln(s.writer, progress)
+ }
+}
+
+func (s *dumbStatusOutput) Flush() {}
+
+func (s *dumbStatusOutput) Write(p []byte) (int, error) {
+ fmt.Fprint(s.writer, string(p))
+ return len(p), nil
+}
diff --git a/ui/terminal/format.go b/ui/terminal/format.go
new file mode 100644
index 0000000..4205bdc
--- /dev/null
+++ b/ui/terminal/format.go
@@ -0,0 +1,123 @@
+// Copyright 2019 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 terminal
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "android/soong/ui/status"
+)
+
+type formatter struct {
+ format string
+ quiet bool
+ start time.Time
+}
+
+// newFormatter returns a formatter for formatting output to
+// the terminal in a format similar to Ninja.
+// format takes nearly all the same options as NINJA_STATUS.
+// %c is currently unsupported.
+func newFormatter(format string, quiet bool) formatter {
+ return formatter{
+ format: format,
+ quiet: quiet,
+ start: time.Now(),
+ }
+}
+
+func (s formatter) message(level status.MsgLevel, message string) string {
+ if level >= status.ErrorLvl {
+ return fmt.Sprintf("FAILED: %s", message)
+ } else if level > status.StatusLvl {
+ return fmt.Sprintf("%s%s", level.Prefix(), message)
+ } else if level == status.StatusLvl {
+ return message
+ }
+ return ""
+}
+
+func (s formatter) progress(counts status.Counts) string {
+ if s.format == "" {
+ return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
+ }
+
+ buf := &strings.Builder{}
+ for i := 0; i < len(s.format); i++ {
+ c := s.format[i]
+ if c != '%' {
+ buf.WriteByte(c)
+ continue
+ }
+
+ i = i + 1
+ if i == len(s.format) {
+ buf.WriteByte(c)
+ break
+ }
+
+ c = s.format[i]
+ switch c {
+ case '%':
+ buf.WriteByte(c)
+ case 's':
+ fmt.Fprintf(buf, "%d", counts.StartedActions)
+ case 't':
+ fmt.Fprintf(buf, "%d", counts.TotalActions)
+ case 'r':
+ fmt.Fprintf(buf, "%d", counts.RunningActions)
+ case 'u':
+ fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
+ case 'f':
+ fmt.Fprintf(buf, "%d", counts.FinishedActions)
+ case 'o':
+ fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
+ case 'c':
+ // TODO: implement?
+ buf.WriteRune('?')
+ case 'p':
+ fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
+ case 'e':
+ fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
+ default:
+ buf.WriteString("unknown placeholder '")
+ buf.WriteByte(c)
+ buf.WriteString("'")
+ }
+ }
+ return buf.String()
+}
+
+func (s formatter) result(result status.ActionResult) string {
+ var ret string
+ if result.Error != nil {
+ targets := strings.Join(result.Outputs, " ")
+ if s.quiet || result.Command == "" {
+ ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output)
+ } else {
+ ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output)
+ }
+ } else if result.Output != "" {
+ ret = result.Output
+ }
+
+ if len(ret) > 0 && ret[len(ret)-1] != '\n' {
+ ret += "\n"
+ }
+
+ return ret
+}
diff --git a/ui/terminal/smart_status.go b/ui/terminal/smart_status.go
new file mode 100644
index 0000000..8fa9eff
--- /dev/null
+++ b/ui/terminal/smart_status.go
@@ -0,0 +1,158 @@
+// Copyright 2019 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 terminal
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+
+ "android/soong/ui/status"
+)
+
+type smartStatusOutput struct {
+ writer io.Writer
+ formatter formatter
+
+ lock sync.Mutex
+
+ haveBlankLine bool
+}
+
+// NewSmartStatusOutput returns a StatusOutput that represents the
+// current build status similarly to Ninja's built-in terminal
+// output.
+func NewSmartStatusOutput(w io.Writer, formatter formatter) status.StatusOutput {
+ return &smartStatusOutput{
+ writer: w,
+ formatter: formatter,
+
+ haveBlankLine: true,
+ }
+}
+
+func (s *smartStatusOutput) Message(level status.MsgLevel, message string) {
+ if level < status.StatusLvl {
+ return
+ }
+
+ str := s.formatter.message(level, message)
+
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if level > status.StatusLvl {
+ s.print(str)
+ } else {
+ s.statusLine(str)
+ }
+}
+
+func (s *smartStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+ str := action.Description
+ if str == "" {
+ str = action.Command
+ }
+
+ progress := s.formatter.progress(counts)
+
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.statusLine(progress + str)
+}
+
+func (s *smartStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+ str := result.Description
+ if str == "" {
+ str = result.Command
+ }
+
+ progress := s.formatter.progress(counts) + str
+
+ output := s.formatter.result(result)
+
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if output != "" {
+ s.statusLine(progress)
+ s.requestLine()
+ s.print(output)
+ } else {
+ s.statusLine(progress)
+ }
+}
+
+func (s *smartStatusOutput) Flush() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.requestLine()
+}
+
+func (s *smartStatusOutput) Write(p []byte) (int, error) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.print(string(p))
+ return len(p), nil
+}
+
+func (s *smartStatusOutput) requestLine() {
+ if !s.haveBlankLine {
+ fmt.Fprintln(s.writer)
+ s.haveBlankLine = true
+ }
+}
+
+func (s *smartStatusOutput) print(str string) {
+ if !s.haveBlankLine {
+ fmt.Fprint(s.writer, "\r", "\x1b[K")
+ s.haveBlankLine = true
+ }
+ fmt.Fprint(s.writer, str)
+ if len(str) == 0 || str[len(str)-1] != '\n' {
+ fmt.Fprint(s.writer, "\n")
+ }
+}
+
+func (s *smartStatusOutput) statusLine(str string) {
+ idx := strings.IndexRune(str, '\n')
+ if idx != -1 {
+ str = str[0:idx]
+ }
+
+ // Limit line width to the terminal width, otherwise we'll wrap onto
+ // another line and we won't delete the previous line.
+ //
+ // Run this on every line in case the window has been resized while
+ // we're printing. This could be optimized to only re-run when we get
+ // SIGWINCH if it ever becomes too time consuming.
+ if max, ok := termWidth(s.writer); ok {
+ if len(str) > max {
+ // TODO: Just do a max. Ninja elides the middle, but that's
+ // more complicated and these lines aren't that important.
+ str = str[:max]
+ }
+ }
+
+ // Move to the beginning on the line, turn on bold, print the output,
+ // turn off bold, then clear the rest of the line.
+ start := "\r\x1b[1m"
+ end := "\x1b[0m\x1b[K"
+ fmt.Fprint(s.writer, start, str, end)
+ s.haveBlankLine = false
+}
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 2445c5b..69a2a09 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -15,131 +15,23 @@
package terminal
import (
- "fmt"
- "strings"
- "time"
+ "io"
"android/soong/ui/status"
)
-type statusOutput struct {
- writer Writer
- format string
-
- start time.Time
- quiet bool
-}
-
// NewStatusOutput returns a StatusOutput that represents the
// current build status similarly to Ninja's built-in terminal
// output.
//
// statusFormat takes nearly all the same options as NINJA_STATUS.
// %c is currently unsupported.
-func NewStatusOutput(w Writer, statusFormat string, quietBuild bool) status.StatusOutput {
- return &statusOutput{
- writer: w,
- format: statusFormat,
+func NewStatusOutput(w io.Writer, statusFormat string, quietBuild bool) status.StatusOutput {
+ formatter := newFormatter(statusFormat, quietBuild)
- start: time.Now(),
- quiet: quietBuild,
- }
-}
-
-func (s *statusOutput) Message(level status.MsgLevel, message string) {
- if level >= status.ErrorLvl {
- s.writer.Print(fmt.Sprintf("FAILED: %s", message))
- } else if level > status.StatusLvl {
- s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
- } else if level == status.StatusLvl {
- s.writer.StatusLine(message)
- }
-}
-
-func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
- if !s.writer.isSmartTerminal() {
- return
- }
-
- str := action.Description
- if str == "" {
- str = action.Command
- }
-
- s.writer.StatusLine(s.progress(counts) + str)
-}
-
-func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
- str := result.Description
- if str == "" {
- str = result.Command
- }
-
- progress := s.progress(counts) + str
-
- if result.Error != nil {
- targets := strings.Join(result.Outputs, " ")
- if s.quiet || result.Command == "" {
- s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s", targets, result.Output))
- } else {
- s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output))
- }
- } else if result.Output != "" {
- s.writer.StatusAndMessage(progress, result.Output)
+ if isSmartTerminal(w) {
+ return NewSmartStatusOutput(w, formatter)
} else {
- s.writer.StatusLine(progress)
+ return NewDumbStatusOutput(w, formatter)
}
}
-
-func (s *statusOutput) Flush() {}
-
-func (s *statusOutput) progress(counts status.Counts) string {
- if s.format == "" {
- return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
- }
-
- buf := &strings.Builder{}
- for i := 0; i < len(s.format); i++ {
- c := s.format[i]
- if c != '%' {
- buf.WriteByte(c)
- continue
- }
-
- i = i + 1
- if i == len(s.format) {
- buf.WriteByte(c)
- break
- }
-
- c = s.format[i]
- switch c {
- case '%':
- buf.WriteByte(c)
- case 's':
- fmt.Fprintf(buf, "%d", counts.StartedActions)
- case 't':
- fmt.Fprintf(buf, "%d", counts.TotalActions)
- case 'r':
- fmt.Fprintf(buf, "%d", counts.RunningActions)
- case 'u':
- fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
- case 'f':
- fmt.Fprintf(buf, "%d", counts.FinishedActions)
- case 'o':
- fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
- case 'c':
- // TODO: implement?
- buf.WriteRune('?')
- case 'p':
- fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
- case 'e':
- fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
- default:
- buf.WriteString("unknown placeholder '")
- buf.WriteByte(c)
- buf.WriteString("'")
- }
- }
- return buf.String()
-}
diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go
new file mode 100644
index 0000000..a87a7f0
--- /dev/null
+++ b/ui/terminal/status_test.go
@@ -0,0 +1,272 @@
+// Copyright 2018 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 terminal
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "android/soong/ui/status"
+)
+
+func TestStatusOutput(t *testing.T) {
+ tests := []struct {
+ name string
+ calls func(stat status.StatusOutput)
+ smart string
+ dumb string
+ }{
+ {
+ name: "two actions",
+ calls: twoActions,
+ smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+ dumb: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
+ },
+ {
+ name: "two parallel actions",
+ calls: twoParallelActions,
+ smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+ dumb: "[ 50% 1/2] action1\n[100% 2/2] action2\n",
+ },
+ {
+ name: "action with output",
+ calls: actionsWithOutput,
+ smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+ dumb: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
+ },
+ {
+ name: "action with output without newline",
+ calls: actionsWithOutputWithoutNewline,
+ smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+ dumb: "[ 33% 1/3] action1\n[ 66% 2/3] action2\noutput1\noutput2\n[100% 3/3] action3\n",
+ },
+ {
+ name: "action with error",
+ calls: actionsWithError,
+ smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n",
+ dumb: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n",
+ },
+ {
+ name: "action with empty description",
+ calls: actionWithEmptyDescription,
+ smart: "\r\x1b[1m[ 0% 0/1] command1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] command1\x1b[0m\x1b[K\n",
+ dumb: "[100% 1/1] command1\n",
+ },
+ {
+ name: "messages",
+ calls: actionsWithMessages,
+ smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n",
+ dumb: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n",
+ },
+ {
+ name: "action with long description",
+ calls: actionWithLongDescription,
+ smart: "\r\x1b[1m[ 0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very long descrip\x1b[0m\x1b[K\n",
+ dumb: "[ 50% 1/2] action with very long description to test eliding\n",
+ },
+ {
+ name: "action with output with ansi codes",
+ calls: actionWithOuptutWithAnsiCodes,
+ smart: "\r\x1b[1m[ 0% 0/1] action1\x1b[0m\x1b[K\r\x1b[1m[100% 1/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n",
+ dumb: "[100% 1/1] action1\ncolor\n",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Run("smart", func(t *testing.T) {
+ smart := &fakeSmartTerminal{termWidth: 40}
+ stat := NewStatusOutput(smart, "", false)
+ tt.calls(stat)
+ stat.Flush()
+
+ if g, w := smart.String(), tt.smart; g != w {
+ t.Errorf("want:\n%q\ngot:\n%q", w, g)
+ }
+ })
+
+ t.Run("dumb", func(t *testing.T) {
+ dumb := &bytes.Buffer{}
+ stat := NewStatusOutput(dumb, "", false)
+ tt.calls(stat)
+ stat.Flush()
+
+ if g, w := dumb.String(), tt.dumb; g != w {
+ t.Errorf("want:\n%q\ngot:\n%q", w, g)
+ }
+ })
+ })
+ }
+}
+
+type runner struct {
+ counts status.Counts
+ stat status.StatusOutput
+}
+
+func newRunner(stat status.StatusOutput, totalActions int) *runner {
+ return &runner{
+ counts: status.Counts{TotalActions: totalActions},
+ stat: stat,
+ }
+}
+
+func (r *runner) startAction(action *status.Action) {
+ r.counts.StartedActions++
+ r.counts.RunningActions++
+ r.stat.StartAction(action, r.counts)
+}
+
+func (r *runner) finishAction(result status.ActionResult) {
+ r.counts.FinishedActions++
+ r.counts.RunningActions--
+ r.stat.FinishAction(result, r.counts)
+}
+
+func (r *runner) finishAndStartAction(result status.ActionResult, action *status.Action) {
+ r.counts.FinishedActions++
+ r.stat.FinishAction(result, r.counts)
+
+ r.counts.StartedActions++
+ r.stat.StartAction(action, r.counts)
+}
+
+var (
+ action1 = &status.Action{Description: "action1"}
+ result1 = status.ActionResult{Action: action1}
+ action2 = &status.Action{Description: "action2"}
+ result2 = status.ActionResult{Action: action2}
+ action3 = &status.Action{Description: "action3"}
+ result3 = status.ActionResult{Action: action3}
+)
+
+func twoActions(stat status.StatusOutput) {
+ runner := newRunner(stat, 2)
+ runner.startAction(action1)
+ runner.finishAction(result1)
+ runner.startAction(action2)
+ runner.finishAction(result2)
+}
+
+func twoParallelActions(stat status.StatusOutput) {
+ runner := newRunner(stat, 2)
+ runner.startAction(action1)
+ runner.startAction(action2)
+ runner.finishAction(result1)
+ runner.finishAction(result2)
+}
+
+func actionsWithOutput(stat status.StatusOutput) {
+ result2WithOutput := status.ActionResult{Action: action2, Output: "output1\noutput2\n"}
+
+ runner := newRunner(stat, 3)
+ runner.startAction(action1)
+ runner.finishAction(result1)
+ runner.startAction(action2)
+ runner.finishAction(result2WithOutput)
+ runner.startAction(action3)
+ runner.finishAction(result3)
+}
+
+func actionsWithOutputWithoutNewline(stat status.StatusOutput) {
+ result2WithOutputWithoutNewline := status.ActionResult{Action: action2, Output: "output1\noutput2"}
+
+ runner := newRunner(stat, 3)
+ runner.startAction(action1)
+ runner.finishAction(result1)
+ runner.startAction(action2)
+ runner.finishAction(result2WithOutputWithoutNewline)
+ runner.startAction(action3)
+ runner.finishAction(result3)
+}
+
+func actionsWithError(stat status.StatusOutput) {
+ action2WithError := &status.Action{Description: "action2", Outputs: []string{"f1", "f2"}, Command: "touch f1 f2"}
+ result2WithError := status.ActionResult{Action: action2WithError, Output: "error1\nerror2\n", Error: fmt.Errorf("error1")}
+
+ runner := newRunner(stat, 3)
+ runner.startAction(action1)
+ runner.finishAction(result1)
+ runner.startAction(action2WithError)
+ runner.finishAction(result2WithError)
+ runner.startAction(action3)
+ runner.finishAction(result3)
+}
+
+func actionWithEmptyDescription(stat status.StatusOutput) {
+ action1 := &status.Action{Command: "command1"}
+ result1 := status.ActionResult{Action: action1}
+
+ runner := newRunner(stat, 1)
+ runner.startAction(action1)
+ runner.finishAction(result1)
+}
+
+func actionsWithMessages(stat status.StatusOutput) {
+ runner := newRunner(stat, 2)
+
+ runner.startAction(action1)
+ runner.finishAction(result1)
+
+ stat.Message(status.VerboseLvl, "verbose")
+ stat.Message(status.StatusLvl, "status")
+ stat.Message(status.PrintLvl, "print")
+ stat.Message(status.ErrorLvl, "error")
+
+ runner.startAction(action2)
+ runner.finishAction(result2)
+}
+
+func actionWithLongDescription(stat status.StatusOutput) {
+ action1 := &status.Action{Description: "action with very long description to test eliding"}
+ result1 := status.ActionResult{Action: action1}
+
+ runner := newRunner(stat, 2)
+
+ runner.startAction(action1)
+
+ runner.finishAction(result1)
+}
+
+func actionWithOuptutWithAnsiCodes(stat status.StatusOutput) {
+ result1WithOutputWithAnsiCodes := status.ActionResult{Action: action1, Output: "\x1b[31mcolor\x1b[0m"}
+
+ runner := newRunner(stat, 1)
+ runner.startAction(action1)
+ runner.finishAction(result1WithOutputWithAnsiCodes)
+}
+
+func TestSmartStatusOutputWidthChange(t *testing.T) {
+ smart := &fakeSmartTerminal{termWidth: 40}
+ stat := NewStatusOutput(smart, "", false)
+
+ runner := newRunner(stat, 2)
+
+ action := &status.Action{Description: "action with very long description to test eliding"}
+ result := status.ActionResult{Action: action}
+
+ runner.startAction(action)
+ smart.termWidth = 30
+ runner.finishAction(result)
+
+ stat.Flush()
+
+ w := "\r\x1b[1m[ 0% 0/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action with very lo\x1b[0m\x1b[K\n"
+
+ if g := smart.String(); g != w {
+ t.Errorf("want:\n%q\ngot:\n%q", w, g)
+ }
+}
diff --git a/ui/terminal/stdio.go b/ui/terminal/stdio.go
new file mode 100644
index 0000000..dec2963
--- /dev/null
+++ b/ui/terminal/stdio.go
@@ -0,0 +1,55 @@
+// Copyright 2018 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 terminal provides a set of interfaces that can be used to interact
+// with the terminal (including falling back when the terminal is detected to
+// be a redirect or other dumb terminal)
+package terminal
+
+import (
+ "io"
+ "os"
+)
+
+// StdioInterface represents a set of stdin/stdout/stderr Reader/Writers
+type StdioInterface interface {
+ Stdin() io.Reader
+ Stdout() io.Writer
+ Stderr() io.Writer
+}
+
+// StdioImpl uses the OS stdin/stdout/stderr to implement StdioInterface
+type StdioImpl struct{}
+
+func (StdioImpl) Stdin() io.Reader { return os.Stdin }
+func (StdioImpl) Stdout() io.Writer { return os.Stdout }
+func (StdioImpl) Stderr() io.Writer { return os.Stderr }
+
+var _ StdioInterface = StdioImpl{}
+
+type customStdio struct {
+ stdin io.Reader
+ stdout io.Writer
+ stderr io.Writer
+}
+
+func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
+ return customStdio{stdin, stdout, stderr}
+}
+
+func (c customStdio) Stdin() io.Reader { return c.stdin }
+func (c customStdio) Stdout() io.Writer { return c.stdout }
+func (c customStdio) Stderr() io.Writer { return c.stderr }
+
+var _ StdioInterface = customStdio{}
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
index a85a517..3a11b79 100644
--- a/ui/terminal/util.go
+++ b/ui/terminal/util.go
@@ -22,13 +22,15 @@
"unsafe"
)
-func isTerminal(w io.Writer) bool {
+func isSmartTerminal(w io.Writer) bool {
if f, ok := w.(*os.File); ok {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
ioctlGetTermios, uintptr(unsafe.Pointer(&termios)),
0, 0, 0)
return err == 0
+ } else if _, ok := w.(*fakeSmartTerminal); ok {
+ return true
}
return false
}
@@ -43,6 +45,8 @@
syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
0, 0, 0)
return int(winsize.ws_column), err == 0
+ } else if f, ok := w.(*fakeSmartTerminal); ok {
+ return f.termWidth, true
}
return 0, false
}
@@ -99,3 +103,8 @@
return input
}
+
+type fakeSmartTerminal struct {
+ bytes.Buffer
+ termWidth int
+}
diff --git a/ui/terminal/writer.go b/ui/terminal/writer.go
deleted file mode 100644
index ebe4b2a..0000000
--- a/ui/terminal/writer.go
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2018 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 terminal provides a set of interfaces that can be used to interact
-// with the terminal (including falling back when the terminal is detected to
-// be a redirect or other dumb terminal)
-package terminal
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
- "sync"
-)
-
-// Writer provides an interface to write temporary and permanent messages to
-// the terminal.
-//
-// The terminal is considered to be a dumb terminal if TERM==dumb, or if a
-// terminal isn't detected on stdout/stderr (generally because it's a pipe or
-// file). Dumb terminals will strip out all ANSI escape sequences, including
-// colors.
-type Writer interface {
- // Print prints the string to the terminal, overwriting any current
- // status being displayed.
- //
- // On a dumb terminal, the status messages will be kept.
- Print(str string)
-
- // Status prints the first line of the string to the terminal,
- // overwriting any previous status line. Strings longer than the width
- // of the terminal will be cut off.
- //
- // On a dumb terminal, previous status messages will remain, and the
- // entire first line of the string will be printed.
- StatusLine(str string)
-
- // StatusAndMessage prints the first line of status to the terminal,
- // similarly to StatusLine(), then prints the full msg below that. The
- // status line is retained.
- //
- // There is guaranteed to be no other output in between the status and
- // message.
- StatusAndMessage(status, msg string)
-
- // Finish ensures that the output ends with a newline (preserving any
- // current status line that is current displayed).
- //
- // This does nothing on dumb terminals.
- Finish()
-
- // Write implements the io.Writer interface. This is primarily so that
- // the logger can use this interface to print to stderr without
- // breaking the other semantics of this interface.
- //
- // Try to use any of the other functions if possible.
- Write(p []byte) (n int, err error)
-
- isSmartTerminal() bool
-}
-
-// NewWriter creates a new Writer based on the stdio and the TERM
-// environment variable.
-func NewWriter(stdio StdioInterface) Writer {
- w := &writerImpl{
- stdio: stdio,
-
- haveBlankLine: true,
- }
-
- if term, ok := os.LookupEnv("TERM"); ok && term != "dumb" {
- w.smartTerminal = isTerminal(stdio.Stdout())
- }
- w.stripEscapes = !w.smartTerminal
-
- return w
-}
-
-type writerImpl struct {
- stdio StdioInterface
-
- haveBlankLine bool
-
- // Protecting the above, we assume that smartTerminal and stripEscapes
- // does not change after initial setup.
- lock sync.Mutex
-
- smartTerminal bool
- stripEscapes bool
-}
-
-func (w *writerImpl) isSmartTerminal() bool {
- return w.smartTerminal
-}
-
-func (w *writerImpl) requestLine() {
- if !w.haveBlankLine {
- fmt.Fprintln(w.stdio.Stdout())
- w.haveBlankLine = true
- }
-}
-
-func (w *writerImpl) Print(str string) {
- if w.stripEscapes {
- str = string(stripAnsiEscapes([]byte(str)))
- }
-
- w.lock.Lock()
- defer w.lock.Unlock()
- w.print(str)
-}
-
-func (w *writerImpl) print(str string) {
- if !w.haveBlankLine {
- fmt.Fprint(w.stdio.Stdout(), "\r", "\x1b[K")
- w.haveBlankLine = true
- }
- fmt.Fprint(w.stdio.Stdout(), str)
- if len(str) == 0 || str[len(str)-1] != '\n' {
- fmt.Fprint(w.stdio.Stdout(), "\n")
- }
-}
-
-func (w *writerImpl) StatusLine(str string) {
- w.lock.Lock()
- defer w.lock.Unlock()
-
- w.statusLine(str)
-}
-
-func (w *writerImpl) statusLine(str string) {
- if !w.smartTerminal {
- fmt.Fprintln(w.stdio.Stdout(), str)
- return
- }
-
- idx := strings.IndexRune(str, '\n')
- if idx != -1 {
- str = str[0:idx]
- }
-
- // Limit line width to the terminal width, otherwise we'll wrap onto
- // another line and we won't delete the previous line.
- //
- // Run this on every line in case the window has been resized while
- // we're printing. This could be optimized to only re-run when we get
- // SIGWINCH if it ever becomes too time consuming.
- if max, ok := termWidth(w.stdio.Stdout()); ok {
- if len(str) > max {
- // TODO: Just do a max. Ninja elides the middle, but that's
- // more complicated and these lines aren't that important.
- str = str[:max]
- }
- }
-
- // Move to the beginning on the line, print the output, then clear
- // the rest of the line.
- fmt.Fprint(w.stdio.Stdout(), "\r", str, "\x1b[K")
- w.haveBlankLine = false
-}
-
-func (w *writerImpl) StatusAndMessage(status, msg string) {
- if w.stripEscapes {
- msg = string(stripAnsiEscapes([]byte(msg)))
- }
-
- w.lock.Lock()
- defer w.lock.Unlock()
-
- w.statusLine(status)
- w.requestLine()
- w.print(msg)
-}
-
-func (w *writerImpl) Finish() {
- w.lock.Lock()
- defer w.lock.Unlock()
-
- w.requestLine()
-}
-
-func (w *writerImpl) Write(p []byte) (n int, err error) {
- w.Print(string(p))
- return len(p), nil
-}
-
-// StdioInterface represents a set of stdin/stdout/stderr Reader/Writers
-type StdioInterface interface {
- Stdin() io.Reader
- Stdout() io.Writer
- Stderr() io.Writer
-}
-
-// StdioImpl uses the OS stdin/stdout/stderr to implement StdioInterface
-type StdioImpl struct{}
-
-func (StdioImpl) Stdin() io.Reader { return os.Stdin }
-func (StdioImpl) Stdout() io.Writer { return os.Stdout }
-func (StdioImpl) Stderr() io.Writer { return os.Stderr }
-
-var _ StdioInterface = StdioImpl{}
-
-type customStdio struct {
- stdin io.Reader
- stdout io.Writer
- stderr io.Writer
-}
-
-func NewCustomStdio(stdin io.Reader, stdout, stderr io.Writer) StdioInterface {
- return customStdio{stdin, stdout, stderr}
-}
-
-func (c customStdio) Stdin() io.Reader { return c.stdin }
-func (c customStdio) Stdout() io.Writer { return c.stdout }
-func (c customStdio) Stderr() io.Writer { return c.stderr }
-
-var _ StdioInterface = customStdio{}
diff --git a/ui/tracer/status.go b/ui/tracer/status.go
index af50e2d..c831255 100644
--- a/ui/tracer/status.go
+++ b/ui/tracer/status.go
@@ -85,3 +85,8 @@
func (s *statusOutput) Flush() {}
func (s *statusOutput) Message(level status.MsgLevel, message string) {}
+
+func (s *statusOutput) Write(p []byte) (int, error) {
+ // Discard writes
+ return len(p), nil
+}