Merge "Blacklist more libraries from no-vendor-variant VNDK"
diff --git a/Android.bp b/Android.bp
index 7465341..a4e6e7d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -193,6 +193,7 @@
"cc/gen_test.go",
"cc/genrule_test.go",
"cc/library_test.go",
+ "cc/proto_test.go",
"cc/test_data_test.go",
"cc/util_test.go",
],
diff --git a/android/config.go b/android/config.go
index fff77ca..84dd66f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -200,12 +200,14 @@
func TestConfig(buildDir string, env map[string]string) Config {
config := &config{
productVariables: productVariables{
- DeviceName: stringPtr("test_device"),
- Platform_sdk_version: intPtr(26),
- AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
- AAPTPreferredConfig: stringPtr("xhdpi"),
- AAPTCharacteristics: stringPtr("nosdcard"),
- AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
+ DeviceName: stringPtr("test_device"),
+ Platform_sdk_version: intPtr(26),
+ DeviceSystemSdkVersions: []string{"14", "15"},
+ Platform_systemsdk_versions: []string{"25", "26"},
+ AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
+ AAPTPreferredConfig: stringPtr("xhdpi"),
+ AAPTCharacteristics: stringPtr("nosdcard"),
+ AAPTPrebuiltDPI: []string{"xhdpi", "xxhdpi"},
},
buildDir: buildDir,
@@ -499,6 +501,22 @@
return String(c.productVariables.Platform_sdk_codename)
}
+func (c *config) PlatformSecurityPatch() string {
+ return String(c.productVariables.Platform_security_patch)
+}
+
+func (c *config) PlatformPreviewSdkVersion() string {
+ return String(c.productVariables.Platform_preview_sdk_version)
+}
+
+func (c *config) PlatformMinSupportedTargetSdkVersion() string {
+ return String(c.productVariables.Platform_min_supported_target_sdk_version)
+}
+
+func (c *config) PlatformBaseOS() string {
+ return String(c.productVariables.Platform_base_os)
+}
+
func (c *config) MinSupportedSdkVersion() int {
return 16
}
diff --git a/android/defs.go b/android/defs.go
index cd8b4e3..4890c66 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -20,7 +20,7 @@
)
var (
- pctx = NewPackageContext("android/soong/common")
+ pctx = NewPackageContext("android/soong/android")
cpPreserveSymlinks = pctx.VariableConfigMethod("cpPreserveSymlinks",
Config.CpPreserveSymlinksFlags)
diff --git a/android/module.go b/android/module.go
index abf2cae..201c27a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1425,6 +1425,10 @@
Srcs() Paths
}
+type HostToolProvider interface {
+ HostToolPath() OptionalPath
+}
+
// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
diff --git a/android/namespace.go b/android/namespace.go
index dca2b8c..50bdcba 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -355,15 +355,19 @@
var _ blueprint.Namespace = (*Namespace)(nil)
+type namespaceProperties struct {
+ // a list of namespaces that contain modules that will be referenced
+ // by modules in this namespace.
+ Imports []string `android:"path"`
+}
+
type NamespaceModule struct {
ModuleBase
namespace *Namespace
resolver *NameResolver
- properties struct {
- Imports []string
- }
+ properties namespaceProperties
}
func (n *NamespaceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -376,6 +380,16 @@
return *n.nameProperties.Name
}
+// soong_namespace provides a scope to modules in an Android.bp file to prevent
+// module name conflicts with other defined modules in different Android.bp
+// files. Once soong_namespace has been defined in an Android.bp file, the
+// namespacing is applied to all modules that follow the soong_namespace in
+// the current Android.bp file, as well as modules defined in Android.bp files
+// in subdirectories. An Android.bp file in a subdirectory can define its own
+// soong_namespace which is applied to all its modules and as well as modules
+// defined in subdirectories Android.bp files. Modules in a soong_namespace are
+// visible to Make by listing the namespace path in PRODUCT_SOONG_NAMESPACES
+// make variable in a makefile.
func NamespaceFactory() Module {
module := &NamespaceModule{}
diff --git a/android/override_module.go b/android/override_module.go
index 02db359..119bca1 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -134,10 +134,15 @@
// Overrides a base module with the given OverrideModule.
func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
+ // Adds the base module to the overrides property, if exists, of the overriding module. See the
+ // comment on OverridableModuleBase.overridesProperty for details.
+ if b.overridesProperty != nil {
+ *b.overridesProperty = append(*b.overridesProperty, b.Name())
+ }
for _, p := range b.overridableProperties {
for _, op := range o.getOverridingProperties() {
if proptools.TypeEqual(p, op) {
- err := proptools.PrependProperties(p, op, nil)
+ err := proptools.AppendProperties(p, op, nil)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -148,13 +153,6 @@
}
}
}
- // Adds the base module to the overrides property, if exists, of the overriding module. See the
- // comment on OverridableModuleBase.overridesProperty for details.
- if b.overridesProperty != nil {
- *b.overridesProperty = append(*b.overridesProperty, b.Name())
- }
- // The base module name property has to be updated separately for Name() to work as intended.
- b.module.base().nameProperties.Name = proptools.StringPtr(o.Name())
}
// Mutators for override/overridable modules. All the fun happens in these functions. It is critical
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 5bd0e2d..df25a89 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -18,6 +18,7 @@
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
// This file implements common functionality for handling modules that may exist as prebuilts,
@@ -42,6 +43,7 @@
properties PrebuiltProperties
module Module
srcs *[]string
+ src *string
}
func (p *Prebuilt) Name(name string) string {
@@ -49,19 +51,27 @@
}
func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
- if len(*p.srcs) == 0 {
- ctx.PropertyErrorf("srcs", "missing prebuilt source file")
- return nil
- }
+ if p.srcs != nil {
+ if len(*p.srcs) == 0 {
+ ctx.PropertyErrorf("srcs", "missing prebuilt source file")
+ return nil
+ }
- if len(*p.srcs) > 1 {
- ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
- return nil
- }
+ if len(*p.srcs) > 1 {
+ ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
+ return nil
+ }
- // Return the singleton source after expanding any filegroup in the
- // sources.
- return PathForModuleSrc(ctx, (*p.srcs)[0])
+ // Return the singleton source after expanding any filegroup in the
+ // sources.
+ return PathForModuleSrc(ctx, (*p.srcs)[0])
+ } else {
+ if proptools.String(p.src) == "" {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
+ return nil
+ }
+ return PathForModuleSrc(ctx, *p.src)
+ }
}
func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
@@ -70,13 +80,19 @@
p.srcs = srcs
}
+func InitSingleSourcePrebuiltModule(module PrebuiltInterface, src *string) {
+ p := module.Prebuilt()
+ module.AddProperties(&p.properties)
+ p.src = src
+}
+
type PrebuiltInterface interface {
Module
Prebuilt() *Prebuilt
}
func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
- ctx.BottomUp("prebuilts", prebuiltMutator).Parallel()
+ ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
}
func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
@@ -84,9 +100,9 @@
ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
}
-// prebuiltMutator ensures that there is always a module with an undecorated name, and marks
+// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
// prebuilt modules that have both a prebuilt and a source module.
-func prebuiltMutator(ctx BottomUpMutatorContext) {
+func PrebuiltMutator(ctx BottomUpMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
name := m.base().BaseModuleName()
@@ -104,7 +120,7 @@
func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
p := m.Prebuilt()
- if p.srcs == nil {
+ if p.srcs == nil && p.src == nil {
panic(fmt.Errorf("prebuilt module did not have InitPrebuiltModule called on it"))
}
if !p.properties.SourceExists {
@@ -143,7 +159,11 @@
// usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt
// will be used if it is marked "prefer" or if the source module is disabled.
func (p *Prebuilt) usePrebuilt(ctx TopDownMutatorContext, source Module) bool {
- if len(*p.srcs) == 0 {
+ if p.srcs != nil && len(*p.srcs) == 0 {
+ return false
+ }
+
+ if p.src != nil && *p.src == "" {
return false
}
diff --git a/android/proto.go b/android/proto.go
index 801837e..5247c68 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -14,6 +14,13 @@
package android
+import (
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
// TODO(ccross): protos are often used to communicate between multiple modules. If the only
// way to convert a proto to source is to reference it as a source file, and external modules cannot
// reference source files in other modules, then every module that owns a proto file will need to
@@ -22,36 +29,72 @@
// and then external modules could depend on the proto module but use their own settings to
// generate the source.
-func ProtoFlags(ctx ModuleContext, p *ProtoProperties) []string {
- protoFlags := []string{}
+type ProtoFlags struct {
+ Flags []string
+ CanonicalPathFromRoot bool
+ Dir ModuleGenPath
+ SubDir ModuleGenPath
+ OutTypeFlag string
+ OutParams []string
+ Deps Paths
+}
+
+type protoDependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+var ProtoPluginDepTag = protoDependencyTag{name: "plugin"}
+
+func ProtoDeps(ctx BottomUpMutatorContext, p *ProtoProperties) {
+ if String(p.Proto.Plugin) != "" && String(p.Proto.Type) != "" {
+ ctx.ModuleErrorf("only one of proto.type and proto.plugin can be specified.")
+ }
+
+ if plugin := String(p.Proto.Plugin); plugin != "" {
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Config().BuildOsVariant},
+ }, ProtoPluginDepTag, "protoc-gen-"+plugin)
+ }
+}
+
+func GetProtoFlags(ctx ModuleContext, p *ProtoProperties) ProtoFlags {
+ var flags []string
+ var deps Paths
if len(p.Proto.Local_include_dirs) > 0 {
localProtoIncludeDirs := PathsForModuleSrc(ctx, p.Proto.Local_include_dirs)
- protoFlags = append(protoFlags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
+ flags = append(flags, JoinWithPrefix(localProtoIncludeDirs.Strings(), "-I"))
}
if len(p.Proto.Include_dirs) > 0 {
rootProtoIncludeDirs := PathsForSource(ctx, p.Proto.Include_dirs)
- protoFlags = append(protoFlags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
+ flags = append(flags, JoinWithPrefix(rootProtoIncludeDirs.Strings(), "-I"))
}
- return protoFlags
-}
+ ctx.VisitDirectDepsWithTag(ProtoPluginDepTag, func(dep Module) {
+ if hostTool, ok := dep.(HostToolProvider); !ok || !hostTool.HostToolPath().Valid() {
+ ctx.PropertyErrorf("proto.plugin", "module %q is not a host tool provider",
+ ctx.OtherModuleName(dep))
+ } else {
+ plugin := String(p.Proto.Plugin)
+ deps = append(deps, hostTool.HostToolPath().Path())
+ flags = append(flags, "--plugin=protoc-gen-"+plugin+"="+hostTool.HostToolPath().String())
+ }
+ })
-func ProtoCanonicalPathFromRoot(ctx ModuleContext, p *ProtoProperties) bool {
- if p.Proto.Canonical_path_from_root == nil {
- return true
+ var protoOutFlag string
+ if plugin := String(p.Proto.Plugin); plugin != "" {
+ protoOutFlag = "--" + plugin + "_out"
}
- return *p.Proto.Canonical_path_from_root
-}
-// ProtoDir returns the module's "gen/proto" directory
-func ProtoDir(ctx ModuleContext) ModuleGenPath {
- return PathForModuleGen(ctx, "proto")
-}
-
-// ProtoSubDir returns the module's "gen/proto/path/to/module" directory
-func ProtoSubDir(ctx ModuleContext) ModuleGenPath {
- return PathForModuleGen(ctx, "proto", ctx.ModuleDir())
+ return ProtoFlags{
+ Flags: flags,
+ Deps: deps,
+ OutTypeFlag: protoOutFlag,
+ CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, true),
+ Dir: PathForModuleGen(ctx, "proto"),
+ SubDir: PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
+ }
}
type ProtoProperties struct {
@@ -59,6 +102,9 @@
// Proto generator type. C++: full or lite. Java: micro, nano, stream, or lite.
Type *string `android:"arch_variant"`
+ // Proto plugin to use as the generator. Must be a cc_binary_host module.
+ Plugin *string `android:"arch_variant"`
+
// list of directories that will be added to the protoc include paths.
Include_dirs []string
@@ -76,3 +122,28 @@
Canonical_path_from_root *bool
} `android:"arch_variant"`
}
+
+func ProtoRule(ctx ModuleContext, rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
+ outDir WritablePath, depFile WritablePath, outputs WritablePaths) {
+
+ var protoBase string
+ if flags.CanonicalPathFromRoot {
+ protoBase = "."
+ } else {
+ rel := protoFile.Rel()
+ protoBase = strings.TrimSuffix(protoFile.String(), rel)
+ }
+
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "aprotoc")).
+ FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
+ FlagWithDepFile("--dependency_out=", depFile).
+ FlagWithArg("-I ", protoBase).
+ Flags(flags.Flags).
+ Input(protoFile).
+ Implicits(deps).
+ ImplicitOutputs(outputs)
+
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).Flag(depFile.String())
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index a2a5366..2d0fac1 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -171,6 +171,20 @@
return outputList
}
+// DepFiles returns the list of paths that were passed to the RuleBuilderCommand methods that take depfile paths, such
+// as RuleBuilderCommand.DepFile or RuleBuilderCommand.FlagWithDepFile.
+func (r *RuleBuilder) DepFiles() WritablePaths {
+ var depFiles WritablePaths
+
+ for _, c := range r.commands {
+ for _, depFile := range c.depFiles {
+ depFiles = append(depFiles, depFile)
+ }
+ }
+
+ return depFiles
+}
+
// Installs returns the list of tuples passed to Install.
func (r *RuleBuilder) Installs() RuleBuilderInstalls {
return append(RuleBuilderInstalls(nil), r.installs...)
@@ -222,9 +236,17 @@
var _ BuilderContext = ModuleContext(nil)
var _ BuilderContext = SingletonContext(nil)
+func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
+ return (&RuleBuilderCommand{}).
+ Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
+ Flags(depFiles.Strings())
+}
+
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs.
func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+ name = ninjaNameEscape(name)
+
if len(r.missingDeps) > 0 {
ctx.Build(pctx, BuildParams{
Rule: ErrorRule,
@@ -237,16 +259,45 @@
return
}
- if len(r.Commands()) > 0 {
+ tools := r.Tools()
+ commands := r.Commands()
+
+ var depFile WritablePath
+ var depFormat blueprint.Deps
+ if depFiles := r.DepFiles(); len(depFiles) > 0 {
+ depFile = depFiles[0]
+ depFormat = blueprint.DepsGCC
+ if len(depFiles) > 1 {
+ // Add a command locally that merges all depfiles together into the first depfile.
+ cmd := r.depFileMergerCmd(ctx, depFiles)
+ commands = append(commands, string(cmd.buf))
+ tools = append(tools, cmd.tools...)
+ }
+ }
+
+ // Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
+ // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
+ // doesn't matter.
+ var output WritablePath
+ var implicitOutputs WritablePaths
+ if outputs := r.Outputs(); len(outputs) > 0 {
+ output = outputs[0]
+ implicitOutputs = outputs[1:]
+ }
+
+ if len(commands) > 0 {
ctx.Build(pctx, BuildParams{
Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: strings.Join(proptools.NinjaEscapeList(r.Commands()), " && "),
- CommandDeps: r.Tools().Strings(),
+ Command: strings.Join(proptools.NinjaEscapeList(commands), " && "),
+ CommandDeps: tools.Strings(),
Restat: r.restat,
}),
- Implicits: r.Inputs(),
- Outputs: r.Outputs(),
- Description: desc,
+ Implicits: r.Inputs(),
+ Output: output,
+ ImplicitOutputs: implicitOutputs,
+ Depfile: depFile,
+ Deps: depFormat,
+ Description: desc,
})
}
}
@@ -256,10 +307,11 @@
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
- buf []byte
- inputs Paths
- outputs WritablePaths
- tools Paths
+ buf []byte
+ inputs Paths
+ outputs WritablePaths
+ depFiles WritablePaths
+ tools Paths
}
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
@@ -284,6 +336,15 @@
return c.Text(flag)
}
+// Flags adds the specified raw text to the command line. The text should not contain input or output paths or the
+// rule will not have them listed in its dependencies or outputs.
+func (c *RuleBuilderCommand) Flags(flags []string) *RuleBuilderCommand {
+ for _, flag := range flags {
+ c.Text(flag)
+ }
+ return c
+}
+
// FlagWithArg adds the specified flag and argument text to the command line, with no separator between them. The flag
// and argument should not contain input or output paths or the rule will not have them listed in its dependencies or
// outputs.
@@ -360,6 +421,14 @@
return c
}
+// 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.
+func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c.Text(path.String())
+}
+
// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
// the command line.
func (c *RuleBuilderCommand) ImplicitOutput(path WritablePath) *RuleBuilderCommand {
@@ -374,6 +443,15 @@
return c
}
+// ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
+// 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.
+func (c *RuleBuilderCommand) ImplicitDepFile(path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c
+}
+
// FlagWithInput adds the specified flag and input path to the command line, with no separator between them. The path
// will also be added to the dependencies returned by RuleBuilder.Inputs.
func (c *RuleBuilderCommand) FlagWithInput(flag string, path Path) *RuleBuilderCommand {
@@ -406,7 +484,35 @@
return c.Text(flag + path.String())
}
+// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
+// will also be added to the outputs returned by RuleBuilder.Outputs.
+func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
+ c.depFiles = append(c.depFiles, path)
+ return c.Text(flag + path.String())
+}
+
// String returns the command line.
func (c *RuleBuilderCommand) String() string {
return string(c.buf)
}
+
+func ninjaNameEscape(s string) string {
+ b := []byte(s)
+ escaped := false
+ for i, c := range b {
+ valid := (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_') ||
+ (c == '-') ||
+ (c == '.')
+ if !valid {
+ b[i] = '_'
+ escaped = true
+ }
+ }
+ if escaped {
+ s = string(b)
+ }
+ return s
+}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 01d23e5..7bad025 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -171,6 +171,14 @@
// ls -l
}
+func ExampleRuleBuilderCommand_Flags() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
+ // Output:
+ // ls -l -a
+}
+
func ExampleRuleBuilderCommand_FlagWithArg() {
ctx := pathContext()
fmt.Println(NewRuleBuilder().Command().
@@ -229,23 +237,27 @@
rule := NewRuleBuilder()
fs := map[string][]byte{
- "input": nil,
- "Implicit": nil,
- "Input": nil,
- "Tool": nil,
- "input2": nil,
- "tool2": nil,
- "input3": nil,
+ "dep_fixer": nil,
+ "input": nil,
+ "Implicit": nil,
+ "Input": nil,
+ "Tool": nil,
+ "input2": nil,
+ "tool2": nil,
+ "input3": nil,
}
ctx := PathContextForTesting(TestConfig("out", nil), fs)
cmd := rule.Command().
+ DepFile(PathForOutput(ctx, "DepFile")).
Flag("Flag").
FlagWithArg("FlagWithArg=", "arg").
+ FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")).
FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")).
FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")).
Implicit(PathForSource(ctx, "Implicit")).
+ ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")).
ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")).
Input(PathForSource(ctx, "Input")).
Output(PathForOutput(ctx, "Output")).
@@ -254,6 +266,7 @@
rule.Command().
Text("command2").
+ DepFile(PathForOutput(ctx, "depfile2")).
Input(PathForSource(ctx, "input2")).
Output(PathForOutput(ctx, "output2")).
Tool(PathForSource(ctx, "tool2"))
@@ -271,25 +284,37 @@
Output(PathForOutput(ctx, "output3"))
wantCommands := []string{
- "Flag FlagWithArg=arg FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
- "command2 input2 out/output2 tool2",
+ "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
+ "command2 out/depfile2 input2 out/output2 tool2",
"command3 input3 out/output2 out/output3",
}
+
+ wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2"
+
wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"})
+ wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
- if !reflect.DeepEqual(rule.Commands(), wantCommands) {
- t.Errorf("\nwant rule.Commands() = %#v\n got %#v", wantCommands, rule.Commands())
+ if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+ t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
}
- if !reflect.DeepEqual(rule.Inputs(), wantInputs) {
- t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", wantInputs, rule.Inputs())
+
+ if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
}
- if !reflect.DeepEqual(rule.Outputs(), wantOutputs) {
- t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", wantOutputs, rule.Outputs())
+
+ if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
}
- if !reflect.DeepEqual(rule.Tools(), wantTools) {
- t.Errorf("\nwant rule.Tools() = %#v\n got %#v", wantTools, rule.Tools())
+ if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
}
}
@@ -375,8 +400,8 @@
t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
}
- if len(params.Outputs) != 1 || params.Outputs[0].String() != wantOutput {
- t.Errorf("want Outputs = [%q], got %q", wantOutput, params.Outputs.Strings())
+ if params.Output.String() != wantOutput {
+ t.Errorf("want Output = %q, got %q", wantOutput, params.Output)
}
if !params.RuleParams.Restat {
diff --git a/android/testing.go b/android/testing.go
index 7f443a3..0ec5af5 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -196,6 +196,7 @@
var searchedOutputs []string
for _, p := range provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
@@ -222,6 +223,7 @@
var outputFullPaths []string
for _, p := range provider.BuildParamsForTests() {
outputs := append(WritablePaths(nil), p.Outputs...)
+ outputs = append(outputs, p.ImplicitOutputs...)
if p.Output != nil {
outputs = append(outputs, p.Output)
}
diff --git a/android/variable.go b/android/variable.go
index aa8c804..21af31f 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -43,7 +43,8 @@
} `android:"arch_variant"`
Malloc_not_svelte struct {
- Cflags []string `android:"arch_variant"`
+ Cflags []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant"`
} `android:"arch_variant"`
Safestack struct {
@@ -136,14 +137,18 @@
BuildNumberFromFile *string `json:",omitempty"`
DateFromFile *string `json:",omitempty"`
- Platform_version_name *string `json:",omitempty"`
- Platform_sdk_version *int `json:",omitempty"`
- Platform_sdk_codename *string `json:",omitempty"`
- Platform_sdk_final *bool `json:",omitempty"`
- Platform_version_active_codenames []string `json:",omitempty"`
- Platform_version_future_codenames []string `json:",omitempty"`
- Platform_vndk_version *string `json:",omitempty"`
- Platform_systemsdk_versions []string `json:",omitempty"`
+ Platform_version_name *string `json:",omitempty"`
+ Platform_sdk_version *int `json:",omitempty"`
+ Platform_sdk_codename *string `json:",omitempty"`
+ Platform_sdk_final *bool `json:",omitempty"`
+ Platform_version_active_codenames []string `json:",omitempty"`
+ Platform_version_future_codenames []string `json:",omitempty"`
+ Platform_vndk_version *string `json:",omitempty"`
+ Platform_systemsdk_versions []string `json:",omitempty"`
+ Platform_security_patch *string `json:",omitempty"`
+ Platform_preview_sdk_version *string `json:",omitempty"`
+ Platform_min_supported_target_sdk_version *string `json:",omitempty"`
+ Platform_base_os *string `json:",omitempty"`
DeviceName *string `json:",omitempty"`
DeviceArch *string `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index c1f52a6..3327a56 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -119,7 +119,7 @@
)
func init() {
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
@@ -149,6 +149,7 @@
android.RegisterModuleType("apex", apexBundleFactory)
android.RegisterModuleType("apex_test", testApexBundleFactory)
android.RegisterModuleType("apex_defaults", defaultsFactory)
+ android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
@@ -395,9 +396,8 @@
outputFiles map[apexPackaging]android.WritablePath
installDir android.OutputPath
- public_key_file android.Path
- private_key_file android.Path
- bundle_public_key bool
+ public_key_file android.Path
+ private_key_file android.Path
container_certificate_file android.Path
container_private_key_file android.Path
@@ -745,10 +745,6 @@
if key, ok := child.(*apexKey); ok {
a.private_key_file = key.private_key_file
a.public_key_file = key.public_key_file
- // If the key is not installed, bundled it with the APEX.
- // Note: this bundled key is valid only for non-production builds
- // (eng/userdebug).
- a.bundle_public_key = !key.installable() && ctx.Config().Debuggable()
return false
} else {
ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
@@ -967,11 +963,8 @@
optFlags := []string{}
// Additional implicit inputs.
- implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, a.private_key_file)
- if a.bundle_public_key {
- implicitInputs = append(implicitInputs, a.public_key_file)
- optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
- }
+ implicitInputs = append(implicitInputs, cannedFsConfig, fileContexts, a.private_key_file, a.public_key_file)
+ optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
if overridden {
@@ -1056,7 +1049,7 @@
func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
if a.installable() {
- // For flattened APEX, do nothing but make sure that apex_manifest.json file is also copied along
+ // For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
// with other ordinary files.
manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
@@ -1069,6 +1062,15 @@
})
a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
+ // rename to apex_pubkey
+ copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: a.public_key_file,
+ Output: copiedPubkey,
+ })
+ a.filesInfo = append(a.filesInfo, apexFile{copiedPubkey, ctx.ModuleName() + ".apex_pubkey", ".", etc, nil, nil})
+
if ctx.Config().FlattenApex() {
for _, fi := range a.filesInfo {
dir := filepath.Join("apex", ctx.ModuleName(), fi.installDir)
@@ -1214,7 +1216,6 @@
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
- fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key))
if a.installable() && a.mergedNoticeFile != nil {
fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNoticeFile.String())
}
@@ -1283,3 +1284,103 @@
android.InitDefaultsModule(module)
return module
}
+
+//
+// Prebuilt APEX
+//
+type Prebuilt struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties PrebuiltProperties
+
+ inputApex android.Path
+ installDir android.OutputPath
+}
+
+type PrebuiltProperties struct {
+ // the path to the prebuilt .apex file to import.
+ Source string `blueprint:"mutated"`
+
+ Src *string
+ Arch struct {
+ Arm struct {
+ Src *string
+ }
+ Arm64 struct {
+ Src *string
+ }
+ X86 struct {
+ Src *string
+ }
+ X86_64 struct {
+ Src *string
+ }
+ }
+}
+
+func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // This is called before prebuilt_select and prebuilt_postdeps mutators
+ // The mutators requires that src to be set correctly for each arch so that
+ // arch variants are disabled when src is not provided for the arch.
+ if len(ctx.MultiTargets()) != 1 {
+ ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ return
+ }
+ var src string
+ switch ctx.MultiTargets()[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.properties.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.properties.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.properties.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.properties.Arch.X86_64.Src)
+ default:
+ ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+ return
+ }
+ if src == "" {
+ src = String(p.properties.Src)
+ }
+ p.properties.Source = src
+}
+
+func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // TODO(jungjw): Check the key validity.
+ p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+ p.installDir = android.PathForModuleInstall(ctx, "apex")
+ ctx.InstallFile(p.installDir, ctx.ModuleName()+imageApexSuffix, p.inputApex)
+}
+
+func (p *Prebuilt) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *Prebuilt) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *Prebuilt) AndroidMk() android.AndroidMkData {
+ return android.AndroidMkData{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.inputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", p.BaseModuleName()+imageApexSuffix)
+ },
+ },
+ }
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func PrebuiltFactory() android.Module {
+ module := &Prebuilt{}
+ module.AddProperties(&module.properties)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties.Source)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8a2e55a..6d101d8 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -36,11 +36,14 @@
ctx.RegisterModuleType("apex_test", android.ModuleFactoryAdaptor(testApexBundleFactory))
ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))
ctx.RegisterModuleType("apex_defaults", android.ModuleFactoryAdaptor(defaultsFactory))
+ ctx.RegisterModuleType("prebuilt_apex", android.ModuleFactoryAdaptor(PrebuiltFactory))
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_deps", apexDepsMutator)
ctx.BottomUp("apex", apexMutator)
+ ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
+ ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
})
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
@@ -54,6 +57,9 @@
ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
ctx.RegisterModuleType("android_app_certificate", android.ModuleFactoryAdaptor(java.AndroidAppCertificateFactory))
ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
+ })
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("image", cc.ImageMutator).Parallel()
ctx.BottomUp("link", cc.LinkageMutator).Parallel()
@@ -163,6 +169,8 @@
"custom_notice": nil,
"testkey2.avbpubkey": nil,
"testkey2.pem": nil,
+ "myapex-arm64.apex": nil,
+ "myapex-arm.apex": nil,
})
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -289,6 +297,10 @@
`)
apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
+
+ optFlags := apexRule.Args["opt_flags"]
+ ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
+
copyCmds := apexRule.Args["copy_commands"]
// Ensure that main rule creates an output
@@ -1187,14 +1199,6 @@
if actual != expected {
t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
}
-
- apex_key := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
- expected = "target/product/test_device/product/etc/security/apex"
- actual = apex_key.installDir.RelPathString()
- if actual != expected {
- t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
- }
-
}
func TestApexKeyFromOtherModule(t *testing.T) {
@@ -1229,3 +1233,26 @@
t.Errorf("wrong private key path. expected %q. actual %q", expected_privkey, actual_privkey)
}
}
+
+func TestPrebuilt(t *testing.T) {
+ ctx := testApex(t, `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ }
+ `)
+
+ prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+
+ expectedInput := "myapex-arm64.apex"
+ if prebuilt.inputApex.String() != expectedInput {
+ t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String())
+ }
+}
diff --git a/apex/key.go b/apex/key.go
index 848e8ce..a627e4b 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -16,8 +16,6 @@
import (
"fmt"
- "io"
- "path/filepath"
"strings"
"android/soong/android"
@@ -39,7 +37,6 @@
public_key_file android.Path
private_key_file android.Path
- installDir android.OutputPath
keyName string
}
@@ -64,7 +61,7 @@
}
func (m *apexKey) installable() bool {
- return m.properties.Installable == nil || proptools.Bool(m.properties.Installable)
+ return false
}
func (m *apexKey) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -93,31 +90,12 @@
pubKeyName := m.public_key_file.Base()[0 : len(m.public_key_file.Base())-len(m.public_key_file.Ext())]
privKeyName := m.private_key_file.Base()[0 : len(m.private_key_file.Base())-len(m.private_key_file.Ext())]
- if pubKeyName != privKeyName {
+ if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
m.public_key_file.String(), pubKeyName, m.private_key_file, privKeyName)
return
}
m.keyName = pubKeyName
-
- m.installDir = android.PathForModuleInstall(ctx, "etc/security/apex")
- if m.installable() {
- ctx.InstallFile(m.installDir, m.keyName, m.public_key_file)
- }
-}
-
-func (m *apexKey) AndroidMk() android.AndroidMkData {
- return android.AndroidMkData{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(m.public_key_file),
- Extra: []android.AndroidMkExtraFunc{
- func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", m.installDir.RelPathString()))
- fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", m.keyName)
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !m.installable())
- },
- },
- }
}
////////////////////////////////////////////////////////////////////////
diff --git a/cc/binary.go b/cc/binary.go
index 7f7c600..35c3d85 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -57,13 +57,13 @@
android.RegisterModuleType("cc_binary_host", binaryHostFactory)
}
-// Module factory for binaries
+// cc_binary produces a binary that is runnable on a device.
func BinaryFactory() android.Module {
module, _ := NewBinary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host binaries
+// cc_binary_host produces a binary that is runnable on a host.
func binaryHostFactory() android.Module {
module, _ := NewBinary(android.HostSupported)
return module.Init()
@@ -384,7 +384,7 @@
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
- builderFlags, outputFile, nil)
+ builderFlags, outputFile)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -417,6 +417,10 @@
return binary.symlinks
}
+func (binary *binaryDecorator) nativeCoverage() bool {
+ return true
+}
+
// /system/bin/linker -> /apex/com.android.runtime/bin/linker
func (binary *binaryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
dir := binary.baseInstaller.installDir(ctx)
diff --git a/cc/builder.go b/cc/builder.go
index dab887c..c64243f 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -26,7 +26,6 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/pathtools"
"android/soong/android"
"android/soong/cc/config"
@@ -70,6 +69,8 @@
CommandDeps: []string{"$ldCmd"},
Rspfile: "${out}.rsp",
RspfileContent: "${in}",
+ // clang -Wl,--out-implib doesn't update its output file if it hasn't changed.
+ Restat: true,
},
"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags")
@@ -258,13 +259,9 @@
stripAddGnuDebuglink bool
stripUseGnuStrip bool
- protoDeps android.Paths
- protoFlags string
- protoOutTypeFlag string
- protoOutParams string
+ proto android.ProtoFlags
protoC bool
protoOptionsFile bool
- protoRoot bool
}
type Objects struct {
@@ -598,7 +595,7 @@
// 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, implicitOutputs android.WritablePaths) {
+ crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath) {
ldCmd := "${config.ClangBin}/clang++"
@@ -635,11 +632,7 @@
}
for _, lib := range sharedLibs {
- libFile := lib.String()
- if ctx.Windows() {
- libFile = pathtools.ReplaceExtension(libFile, "a")
- }
- libFlagsList = append(libFlagsList, libFile)
+ libFlagsList = append(libFlagsList, lib.String())
}
deps = append(deps, staticLibs...)
@@ -650,12 +643,11 @@
}
ctx.Build(pctx, android.BuildParams{
- Rule: ld,
- Description: "link " + outputFile.Base(),
- Output: outputFile,
- ImplicitOutputs: implicitOutputs,
- Inputs: objFiles,
- Implicits: deps,
+ Rule: ld,
+ Description: "link " + outputFile.Base(),
+ Output: outputFile,
+ Inputs: objFiles,
+ Implicits: deps,
Args: map[string]string{
"ldCmd": ldCmd,
"crtBegin": crtBegin.String(),
diff --git a/cc/cc.go b/cc/cc.go
index c80d00c..0668fd9 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -162,13 +162,9 @@
GroupStaticLibs bool
- protoDeps android.Paths
- protoFlags []string // Flags that apply to proto source files
- protoOutTypeFlag string // The output type, --cpp_out for example
- protoOutParams []string // Flags that modify the output of proto generated files
- protoC bool // Whether to use C instead of C++
- protoOptionsFile bool // Whether to look for a .options file next to the .proto
- ProtoRoot bool
+ proto android.ProtoFlags
+ protoC bool // Whether to use C instead of C++
+ protoOptionsFile bool // Whether to look for a .options file next to the .proto
}
type ObjectLinkerProperties struct {
@@ -267,6 +263,7 @@
isStubs() bool
bootstrap() bool
mustUseVendorVariant() bool
+ nativeCoverage() bool
}
type ModuleContext interface {
@@ -312,6 +309,8 @@
link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path
appendLdflags([]string)
unstrippedOutputFilePath() android.Path
+
+ nativeCoverage() bool
}
type installer interface {
@@ -604,6 +603,10 @@
return Bool(c.Properties.Bootstrap)
}
+func (c *Module) nativeCoverage() bool {
+ return c.linker != nil && c.linker.nativeCoverage()
+}
+
func isBionic(name string) bool {
switch name {
case "libc", "libm", "libdl", "linker":
@@ -794,6 +797,10 @@
return ctx.mod.bootstrap()
}
+func (ctx *moduleContextImpl) nativeCoverage() bool {
+ return ctx.mod.nativeCoverage()
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -1586,6 +1593,10 @@
return
}
+ if depTag == android.ProtoPluginDepTag {
+ return
+ }
+
if dep.Target().Os != ctx.Os() {
ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
return
@@ -1966,6 +1977,11 @@
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
+// merged (when possible) by prepending the default module's values to the
+// depending module's values.
func defaultsFactory() android.Module {
return DefaultsFactory()
}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 8c0bcfe..05d74b9 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -54,6 +54,7 @@
func createTestContext(t *testing.T, config android.Config, bp string, os android.OsType) *android.TestContext {
ctx := android.NewTestArchContext()
ctx.RegisterModuleType("cc_binary", android.ModuleFactoryAdaptor(BinaryFactory))
+ ctx.RegisterModuleType("cc_binary_host", android.ModuleFactoryAdaptor(binaryHostFactory))
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(LibraryFactory))
ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(LibrarySharedFactory))
ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(LibraryStaticFactory))
diff --git a/cc/compiler.go b/cc/compiler.go
index 0ab1f01..f9af4d8 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -221,12 +221,14 @@
deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
+ android.ProtoDeps(ctx, &compiler.Proto)
if compiler.hasSrcExt(".proto") {
deps = protoDeps(ctx, deps, &compiler.Proto, Bool(compiler.Properties.Proto.Static))
}
if compiler.hasSrcExt(".sysprop") {
- deps.SharedLibs = append(deps.SharedLibs, "libbase")
+ deps.HeaderLibs = append(deps.HeaderLibs, "libbase_headers")
+ deps.SharedLibs = append(deps.SharedLibs, "liblog")
}
if Bool(compiler.Properties.Openmp) {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 81439f3..347bfab 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -132,13 +132,13 @@
// Warnings from clang-8.0
"-Wno-defaulted-function-deleted",
- }, " "))
- pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
// Disable -Winconsistent-missing-override until we can clean up the existing
// codebase for it.
"-Wno-inconsistent-missing-override",
+ }, " "))
+ pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
// Enable clang's thread-safety annotations in libcxx.
// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
diff --git a/cc/config/global.go b/cc/config/global.go
index e3fab0c..372ffc4 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -120,8 +120,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r353983"
- ClangDefaultShortVersion = "9.0.1"
+ ClangDefaultVersion = "clang-r353983b"
+ ClangDefaultShortVersion = "9.0.2"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/coverage.go b/cc/coverage.go
index ad2f1e4..9dc7f06 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -23,6 +23,9 @@
type CoverageProperties struct {
Native_coverage *bool
+ NeedCoverageVariant bool `blueprint:"mutated"`
+ NeedCoverageBuild bool `blueprint:"mutated"`
+
CoverageEnabled bool `blueprint:"mutated"`
IsCoverageVariant bool `blueprint:"mutated"`
}
@@ -38,9 +41,24 @@
return []interface{}{&cov.Properties}
}
-func (cov *coverage) begin(ctx BaseModuleContext) {}
-
func (cov *coverage) deps(ctx BaseModuleContext, deps Deps) Deps {
+ if cov.Properties.NeedCoverageBuild {
+ // Link libprofile-extras/libprofile-extras_ndk when coverage
+ // variant is required. This is a no-op unless coverage is
+ // actually enabled during linking, when
+ // '-uinit_profile_extras' is added (in flags()) to force the
+ // setup code in libprofile-extras be linked into the
+ // binary/library.
+ //
+ // We cannot narrow it further to only the 'cov' variant since
+ // the mutator hasn't run (and we don't have the 'cov' variant
+ // yet).
+ if !ctx.useSdk() {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras")
+ } else {
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "libprofile-extras_ndk")
+ }
+ }
return deps
}
@@ -95,46 +113,54 @@
if cov.linkCoverage {
flags.LdFlags = append(flags.LdFlags, "--coverage")
+
+ // Force linking of constructor/setup code in libprofile-extras
+ flags.LdFlags = append(flags.LdFlags, "-uinit_profile_extras")
}
return flags
}
-func coverageMutator(mctx android.BottomUpMutatorContext) {
+func (cov *coverage) begin(ctx BaseModuleContext) {
// Coverage is disabled globally
- if !mctx.DeviceConfig().NativeCoverageEnabled() {
+ if !ctx.DeviceConfig().NativeCoverageEnabled() {
return
}
- if c, ok := mctx.Module().(*Module); ok {
- var needCoverageVariant bool
- var needCoverageBuild bool
+ var needCoverageVariant bool
+ var needCoverageBuild bool
- if mctx.Host() {
- // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
- // Just turn off for now.
- } else if c.IsStubs() {
- // Do not enable coverage for platform stub libraries
- } else if c.isNDKStubLibrary() {
- // Do not enable coverage for NDK stub libraries
- } else if c.coverage != nil {
- // Check if Native_coverage is set to false. This property defaults to true.
- needCoverageVariant = BoolDefault(c.coverage.Properties.Native_coverage, true)
+ if ctx.Host() {
+ // TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
+ // Just turn off for now.
+ } else if !ctx.nativeCoverage() {
+ // Native coverage is not supported for this module type.
+ } else {
+ // Check if Native_coverage is set to false. This property defaults to true.
+ needCoverageVariant = BoolDefault(cov.Properties.Native_coverage, true)
- if sdk_version := String(c.Properties.Sdk_version); sdk_version != "current" {
- // Native coverage is not supported for SDK versions < 23
- if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
- needCoverageVariant = false
- }
- }
-
- if needCoverageVariant {
- // Coverage variant is actually built with coverage if enabled for its module path
- needCoverageBuild = mctx.DeviceConfig().CoverageEnabledForPath(mctx.ModuleDir())
+ if sdk_version := ctx.sdkVersion(); ctx.useSdk() && sdk_version != "current" {
+ // Native coverage is not supported for SDK versions < 23
+ if fromApi, err := strconv.Atoi(sdk_version); err == nil && fromApi < 23 {
+ needCoverageVariant = false
}
}
if needCoverageVariant {
+ // Coverage variant is actually built with coverage if enabled for its module path
+ needCoverageBuild = ctx.DeviceConfig().CoverageEnabledForPath(ctx.ModuleDir())
+ }
+ }
+
+ cov.Properties.NeedCoverageBuild = needCoverageBuild
+ cov.Properties.NeedCoverageVariant = needCoverageVariant
+}
+
+func coverageMutator(mctx android.BottomUpMutatorContext) {
+ if c, ok := mctx.Module().(*Module); ok && c.coverage != nil {
+ needCoverageVariant := c.coverage.Properties.NeedCoverageVariant
+ needCoverageBuild := c.coverage.Properties.NeedCoverageBuild
+ if needCoverageVariant {
m := mctx.CreateVariations("", "cov")
// Setup the non-coverage version and set HideFromMake and
diff --git a/cc/library.go b/cc/library.go
index e5bb347..cf20747 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -153,42 +153,48 @@
android.RegisterModuleType("cc_library_headers", LibraryHeaderFactory)
}
-// Module factory for combined static + shared libraries, device by default but with possible host
-// support
+// cc_library creates both static and/or shared libraries for a device and/or
+// host. By default, a cc_library has a single variant that targets the device.
+// Specifying `host_supported: true` also creates a library that targets the
+// host.
func LibraryFactory() android.Module {
module, _ := NewLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for static libraries
+// cc_library_static creates a static library for a device and/or host binary.
func LibraryStaticFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for shared libraries
+// cc_library_shared creates a shared library for a device and/or host.
func LibrarySharedFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for host static libraries
+// cc_library_host_static creates a static library that is linkable to a host
+// binary.
func LibraryHostStaticFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyStatic()
return module.Init()
}
-// Module factory for host shared libraries
+// cc_library_host_shared creates a shared library that is usable on a host.
func LibraryHostSharedFactory() android.Module {
module, library := NewLibrary(android.HostSupported)
library.BuildOnlyShared()
return module.Init()
}
-// Module factory for header-only libraries
+// cc_library_headers contains a set of c/c++ headers which are imported by
+// other soong cc modules using the header_libs property. For best practices,
+// use export_include_dirs property or LOCAL_EXPORT_C_INCLUDE_DIRS for
+// Make.
func LibraryHeaderFactory() android.Module {
module, library := NewLibrary(android.HostAndDeviceSupported)
library.HeaderOnly()
@@ -351,10 +357,9 @@
)
}
} else {
- f = append(f, "-shared")
- if !ctx.Windows() {
- f = append(f, "-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
- }
+ f = append(f,
+ "-shared",
+ "-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
}
flags.LdFlags = append(f, flags.LdFlags...)
@@ -678,14 +683,6 @@
outputFile := android.PathForModuleOut(ctx, fileName)
ret := outputFile
- var implicitOutputs android.WritablePaths
- if ctx.Windows() {
- importLibraryPath := android.PathForModuleOut(ctx, pathtools.ReplaceExtension(fileName, "a"))
-
- 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
@@ -737,7 +734,7 @@
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
- linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -755,6 +752,13 @@
return library.unstrippedOutputFile
}
+func (library *libraryDecorator) nativeCoverage() bool {
+ if library.header() || library.buildStubs() {
+ return false
+ }
+ return true
+}
+
func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
isLlndk := inList(ctx.baseModuleName(), llndkLibraries) || inList(ctx.baseModuleName(), ndkMigratedLibs)
@@ -835,10 +839,10 @@
if Bool(library.Properties.Proto.Export_proto_headers) {
if library.baseCompiler.hasSrcExt(".proto") {
includes := []string{}
- if flags.ProtoRoot {
- includes = append(includes, "-I"+android.ProtoSubDir(ctx).String())
+ if flags.proto.CanonicalPathFromRoot {
+ includes = append(includes, "-I"+flags.proto.SubDir.String())
}
- includes = append(includes, "-I"+android.ProtoDir(ctx).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
diff --git a/cc/linker.go b/cc/linker.go
index 179a998..b279c06 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -255,6 +255,17 @@
}
}
+ if inList("libc_scudo", deps.SharedLibs) {
+ // libc_scudo is an alternate implementation of all
+ // allocation functions (malloc, free), that uses
+ // the scudo allocator instead of the default native
+ // allocator. If this library is in the list, make
+ // sure it's first so it properly overrides the
+ // allocation functions of all other shared libraries.
+ _, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
+ deps.SharedLibs = append([]string{"libc_scudo"}, deps.SharedLibs...)
+ }
+
// If libc and libdl are both in system_shared_libs make sure libdl comes after libc
// to avoid loading libdl before libc.
if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
@@ -290,6 +301,10 @@
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)
}
@@ -343,7 +358,7 @@
// darwin defaults to treating undefined symbols as errors
flags.LdFlags = append(flags.LdFlags, "-Wl,-undefined,dynamic_lookup")
}
- } else if !ctx.Darwin() && !ctx.Windows() {
+ } else if !ctx.Darwin() {
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
}
@@ -380,7 +395,7 @@
flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
- if ctx.Host() && !ctx.Windows() {
+ if ctx.Host() {
rpath_prefix := `\$$ORIGIN/`
if ctx.Darwin() {
rpath_prefix = "@loader_path/"
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index cdd2c48..5a36b7f 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -161,6 +161,10 @@
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *llndkStubDecorator) nativeCoverage() bool {
+ return false
+}
+
func NewLLndkStubLibrary() *Module {
module, library := NewLibrary(android.DeviceSupported)
library.BuildOnlyShared()
diff --git a/cc/lto.go b/cc/lto.go
index 0d7a246..1084869 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -91,22 +91,22 @@
flags.CFlags = append(flags.CFlags, ltoFlag)
flags.LdFlags = append(flags.LdFlags, ltoFlag)
- if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && !lto.useClangLld(ctx) {
+ if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
// Set appropriate ThinLTO cache policy
- cacheDirFormat := "-Wl,-plugin-opt,cache-dir="
+ cacheDirFormat := "-Wl,--thinlto-cache-dir="
cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
flags.LdFlags = append(flags.LdFlags, cacheDirFormat+cacheDir)
// Limit the size of the ThinLTO cache to the lesser of 10% of available
// disk space and 10GB.
- cachePolicyFormat := "-Wl,-plugin-opt,cache-policy="
+ cachePolicyFormat := "-Wl,--thinlto-cache-policy="
policy := "cache_size=10%:cache_size_bytes=10g"
flags.LdFlags = append(flags.LdFlags, cachePolicyFormat+policy)
}
// If the module does not have a profile, be conservative and do not inline
// or unroll loops during LTO, in order to prevent significant size bloat.
- if !ctx.isPgoCompile() && !lto.useClangLld(ctx) {
+ if !ctx.isPgoCompile() {
flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-inline-threshold=0")
flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-unroll-threshold=0")
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 3ae4452..7199467 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -338,6 +338,10 @@
return stub.libraryDecorator.link(ctx, flags, deps, objs)
}
+func (stub *stubDecorator) nativeCoverage() bool {
+ return false
+}
+
func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
arch := ctx.Target().Arch.ArchType.Name
apiLevel := stub.properties.ApiLevel
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 9265bff..e39bae5 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -63,7 +63,7 @@
android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
android.RegisterSingletonType("ndk", NdkSingleton)
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
}
func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
diff --git a/cc/object.go b/cc/object.go
index b9c5742..50ecc38 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -33,6 +33,9 @@
Properties ObjectLinkerProperties
}
+// cc_object runs the compiler without running the linker. It is rarely
+// necessary, but sometimes used to generate .s files from .c files to use as
+// input to a cc_genrule module.
func ObjectFactory() android.Module {
module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
module.linker = &objectLinker{
@@ -111,3 +114,7 @@
func (object *objectLinker) unstrippedOutputFilePath() android.Path {
return nil
}
+
+func (object *objectLinker) nativeCoverage() bool {
+ return true
+}
diff --git a/cc/pgo.go b/cc/pgo.go
index 9363916..7334ea2 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -27,8 +27,11 @@
var (
// Add flags to ignore warnings that profiles are old or missing for
- // some functions
- profileUseOtherFlags = []string{"-Wno-backend-plugin"}
+ // some functions, and turn on the experimental new pass manager.
+ profileUseOtherFlags = []string{
+ "-Wno-backend-plugin",
+ "-fexperimental-new-pass-manager",
+ }
globalPgoProfileProjects = []string{
"toolchain/pgo-profiles",
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 4c893d4..5ffeb32 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -29,16 +29,20 @@
prebuilt() *android.Prebuilt
}
+type prebuiltLinkerProperties struct {
+
+ // a prebuilt library or binary. Can reference a genrule module that generates an executable file.
+ Srcs []string `android:"path,arch_variant"`
+
+ // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined
+ // symbols, etc), default true.
+ Check_elf_files *bool
+}
+
type prebuiltLinker struct {
android.Prebuilt
- properties struct {
- Srcs []string `android:"path,arch_variant"`
-
- // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined
- // symbols, etc), default true.
- Check_elf_files *bool
- }
+ properties prebuiltLinkerProperties
}
func (p *prebuiltLinker) prebuilt() *android.Prebuilt {
@@ -108,6 +112,12 @@
return p.libraryDecorator.shared()
}
+func (p *prebuiltLibraryLinker) nativeCoverage() bool {
+ return false
+}
+
+// cc_prebuilt_library_shared installs a precompiled shared library that are
+// listed in the srcs property in the device's directory.
func prebuiltSharedLibraryFactory() android.Module {
module, _ := NewPrebuiltSharedLibrary(android.HostAndDeviceSupported)
return module.Init()
@@ -133,6 +143,8 @@
return module, library
}
+// cc_prebuilt_library_static installs a precompiled static library that are
+// listed in the srcs property in the device's directory.
func prebuiltStaticLibraryFactory() android.Module {
module, _ := NewPrebuiltStaticLibrary(android.HostAndDeviceSupported)
return module.Init()
@@ -193,6 +205,8 @@
return nil
}
+// cc_prebuilt_binary installs a precompiled executable in srcs property in the
+// device's directory.
func prebuiltBinaryFactory() android.Module {
module, _ := NewPrebuiltBinary(android.HostAndDeviceSupported)
return module.Init()
diff --git a/cc/proto.go b/cc/proto.go
index ce8a30e..f818edc 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -15,126 +15,99 @@
package cc
import (
- "strings"
-
- "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"android/soong/android"
)
-func init() {
- pctx.HostBinToolVariable("protocCmd", "aprotoc")
- pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
-}
-
-var (
- proto = pctx.AndroidStaticRule("protoc",
- blueprint.RuleParams{
- Command: "$protocCmd $protoOut=$protoOutParams:$outDir --dependency_out=$out.d -I $protoBase $protoFlags $in && " +
- `$depFixCmd $out.d`,
- CommandDeps: []string{"$protocCmd", "$depFixCmd"},
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- }, "protoFlags", "protoOut", "protoOutParams", "protoBase", "outDir")
-)
-
// genProto creates a rule to convert a .proto file to generated .pb.cc and .pb.h files and returns
// the paths to the generated files.
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags builderFlags) (ccFile, headerFile android.WritablePath) {
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags builderFlags) (cc, header android.WritablePath) {
+ var ccFile, headerFile android.ModuleGenPath
srcSuffix := ".cc"
if flags.protoC {
srcSuffix = ".c"
}
- var protoBase string
- if flags.protoRoot {
- protoBase = "."
+ if flags.proto.CanonicalPathFromRoot {
ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb"+srcSuffix)
headerFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.h")
} else {
rel := protoFile.Rel()
- protoBase = strings.TrimSuffix(protoFile.String(), rel)
ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb"+srcSuffix))
headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.h"))
}
- protoDeps := flags.protoDeps
+ protoDeps := flags.proto.Deps
if flags.protoOptionsFile {
optionsFile := pathtools.ReplaceExtension(protoFile.String(), "options")
- optionsPath := android.ExistentPathForSource(ctx, optionsFile)
- if optionsPath.Valid() {
- protoDeps = append(android.Paths{optionsPath.Path()}, protoDeps...)
- }
+ optionsPath := android.PathForSource(ctx, optionsFile)
+ protoDeps = append(android.Paths{optionsPath}, protoDeps...)
}
- ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Output: ccFile,
- ImplicitOutput: headerFile,
- Input: protoFile,
- Implicits: protoDeps,
- Args: map[string]string{
- "outDir": android.ProtoDir(ctx).String(),
- "protoFlags": flags.protoFlags,
- "protoOut": flags.protoOutTypeFlag,
- "protoOutParams": flags.protoOutParams,
- "protoBase": protoBase,
- },
- })
+ outDir := flags.proto.Dir
+ depFile := ccFile.ReplaceExtension(ctx, "d")
+ outputs := android.WritablePaths{ccFile, headerFile}
+
+ rule := android.NewRuleBuilder()
+
+ android.ProtoRule(ctx, rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return ccFile, headerFile
}
-func protoDeps(ctx BaseModuleContext, deps Deps, p *android.ProtoProperties, static bool) Deps {
+func protoDeps(ctx DepsContext, deps Deps, p *android.ProtoProperties, static bool) Deps {
var lib string
- switch String(p.Proto.Type) {
- case "full":
- if ctx.useSdk() {
- lib = "libprotobuf-cpp-full-ndk"
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "full":
+ if ctx.useSdk() {
+ lib = "libprotobuf-cpp-full-ndk"
+ static = true
+ } else {
+ lib = "libprotobuf-cpp-full"
+ }
+ case "lite", "":
+ if ctx.useSdk() {
+ lib = "libprotobuf-cpp-lite-ndk"
+ static = true
+ } else {
+ lib = "libprotobuf-cpp-lite"
+ }
+ case "nanopb-c":
+ lib = "libprotobuf-c-nano"
static = true
- } else {
- lib = "libprotobuf-cpp-full"
- }
- case "lite", "":
- if ctx.useSdk() {
- lib = "libprotobuf-cpp-lite-ndk"
+ case "nanopb-c-enable_malloc":
+ lib = "libprotobuf-c-nano-enable_malloc"
static = true
- } else {
- lib = "libprotobuf-cpp-lite"
+ case "nanopb-c-16bit":
+ lib = "libprotobuf-c-nano-16bit"
+ static = true
+ case "nanopb-c-enable_malloc-16bit":
+ lib = "libprotobuf-c-nano-enable_malloc-16bit"
+ static = true
+ case "nanopb-c-32bit":
+ lib = "libprotobuf-c-nano-32bit"
+ static = true
+ case "nanopb-c-enable_malloc-32bit":
+ lib = "libprotobuf-c-nano-enable_malloc-32bit"
+ static = true
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- case "nanopb-c":
- lib = "libprotobuf-c-nano"
- static = true
- case "nanopb-c-enable_malloc":
- lib = "libprotobuf-c-nano-enable_malloc"
- static = true
- case "nanopb-c-16bit":
- lib = "libprotobuf-c-nano-16bit"
- static = true
- case "nanopb-c-enable_malloc-16bit":
- lib = "libprotobuf-c-nano-enable_malloc-16bit"
- static = true
- case "nanopb-c-32bit":
- lib = "libprotobuf-c-nano-32bit"
- static = true
- case "nanopb-c-enable_malloc-32bit":
- lib = "libprotobuf-c-nano-enable_malloc-32bit"
- static = true
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- String(p.Proto.Type))
- }
- if static {
- deps.StaticLibs = append(deps.StaticLibs, lib)
- deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
- } else {
- deps.SharedLibs = append(deps.SharedLibs, lib)
- deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
+ if static {
+ deps.StaticLibs = append(deps.StaticLibs, lib)
+ deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, lib)
+ } else {
+ deps.SharedLibs = append(deps.SharedLibs, lib)
+ deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, lib)
+ }
}
return deps
@@ -143,41 +116,41 @@
func protoFlags(ctx ModuleContext, flags Flags, p *android.ProtoProperties) Flags {
flags.CFlags = append(flags.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
- flags.ProtoRoot = android.ProtoCanonicalPathFromRoot(ctx, p)
- if flags.ProtoRoot {
- flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.ProtoSubDir(ctx).String())
+ flags.proto = android.GetProtoFlags(ctx, p)
+ if flags.proto.CanonicalPathFromRoot {
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.SubDir.String())
}
- flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.ProtoDir(ctx).String())
+ flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.Dir.String())
- flags.protoFlags = android.ProtoFlags(ctx, p)
+ if String(p.Proto.Plugin) == "" {
+ var plugin string
- var plugin string
+ switch String(p.Proto.Type) {
+ case "nanopb-c", "nanopb-c-enable_malloc", "nanopb-c-16bit", "nanopb-c-enable_malloc-16bit", "nanopb-c-32bit", "nanopb-c-enable_malloc-32bit":
+ flags.protoC = true
+ flags.protoOptionsFile = true
+ flags.proto.OutTypeFlag = "--nanopb_out"
+ plugin = "protoc-gen-nanopb"
+ case "full":
+ flags.proto.OutTypeFlag = "--cpp_out"
+ case "lite":
+ flags.proto.OutTypeFlag = "--cpp_out"
+ flags.proto.OutParams = append(flags.proto.OutParams, "lite")
+ case "":
+ // TODO(b/119714316): this should be equivalent to "lite" in
+ // order to match protoDeps, but some modules are depending on
+ // this behavior
+ flags.proto.OutTypeFlag = "--cpp_out"
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
+ }
- switch String(p.Proto.Type) {
- case "nanopb-c", "nanopb-c-enable_malloc", "nanopb-c-16bit", "nanopb-c-enable_malloc-16bit", "nanopb-c-32bit", "nanopb-c-enable_malloc-32bit":
- flags.protoC = true
- flags.protoOptionsFile = true
- flags.protoOutTypeFlag = "--nanopb_out"
- plugin = "protoc-gen-nanopb"
- case "full":
- flags.protoOutTypeFlag = "--cpp_out"
- case "lite":
- flags.protoOutTypeFlag = "--cpp_out"
- flags.protoOutParams = append(flags.protoOutParams, "lite")
- case "":
- // TODO(b/119714316): this should be equivalent to "lite" in
- // order to match protoDeps, but some modules are depending on
- // this behavior
- flags.protoOutTypeFlag = "--cpp_out"
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- String(p.Proto.Type))
- }
-
- if plugin != "" {
- path := ctx.Config().HostToolPath(ctx, plugin)
- flags.protoDeps = append(flags.protoDeps, path)
- flags.protoFlags = append(flags.protoFlags, "--plugin="+path.String())
+ if plugin != "" {
+ path := ctx.Config().HostToolPath(ctx, plugin)
+ flags.proto.Deps = append(flags.proto.Deps, path)
+ flags.proto.Flags = append(flags.proto.Flags, "--plugin="+path.String())
+ }
}
return flags
diff --git a/cc/proto_test.go b/cc/proto_test.go
new file mode 100644
index 0000000..a7fcef9
--- /dev/null
+++ b/cc/proto_test.go
@@ -0,0 +1,75 @@
+// Copyright 2016 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 cc
+
+import (
+ "runtime"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestProto(t *testing.T) {
+ t.Run("simple", func(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["a.proto"],
+ }`)
+
+ proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+
+ if cmd := proto.RuleParams.Command; !strings.Contains(cmd, "--cpp_out=") {
+ t.Errorf("expected '--cpp_out' in %q", cmd)
+ }
+ })
+
+ t.Run("plugin", func(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("TODO(b/129763458): cc_binary_host tests fail on mac when trying to exec xcrun")
+ }
+ ctx := testCc(t, `
+ cc_binary_host {
+ name: "protoc-gen-foobar",
+ stl: "none",
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["a.proto"],
+ proto: {
+ plugin: "foobar",
+ },
+ }`)
+
+ buildOS := android.BuildOs.String()
+
+ proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Output("proto/a.pb.cc")
+ foobar := ctx.ModuleForTests("protoc-gen-foobar", buildOS+"_x86_64")
+
+ cmd := proto.RuleParams.Command
+ if w := "--foobar_out="; !strings.Contains(cmd, w) {
+ t.Errorf("expected %q in %q", w, cmd)
+ }
+
+ foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+
+ if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
+ t.Errorf("expected %q in %q", w, cmd)
+ }
+ })
+
+}
diff --git a/cc/test.go b/cc/test.go
index 045cc4f..dae2a37 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -74,31 +74,41 @@
android.RegisterModuleType("cc_benchmark_host", BenchmarkHostFactory)
}
-// Module factory for tests
+// cc_test generates a test config file and an executable binary file to test
+// specific functionality on a device. The executable binary gets an implicit
+// static_libs dependency on libgtests unless the gtest flag is set to false.
func TestFactory() android.Module {
module := NewTest(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for test libraries
+// cc_test_library creates an archive of files (i.e. .o files) which is later
+// referenced by another module (such as cc_test, cc_defaults or cc_test_library)
+// for archiving or linking.
func TestLibraryFactory() android.Module {
module := NewTestLibrary(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for benchmarks
+// cc_benchmark compiles an executable binary that performs benchmark testing
+// of a specific component in a device. Additional files such as test suites
+// and test configuration are installed on the side of the compiled executed
+// binary.
func BenchmarkFactory() android.Module {
module := NewBenchmark(android.HostAndDeviceSupported)
return module.Init()
}
-// Module factory for host tests
+// cc_test_host compiles a test host binary.
func TestHostFactory() android.Module {
module := NewTest(android.HostSupported)
return module.Init()
}
-// Module factory for host benchmarks
+// cc_benchmark_host compiles an executable binary that performs benchmark
+// testing of a specific component in the host. Additional files such as
+// test suites and test configuration are installed on the side of the
+// compiled executed binary.
func BenchmarkHostFactory() android.Module {
module := NewBenchmark(android.HostSupported)
return module.Init()
diff --git a/cc/testing.go b/cc/testing.go
index b3b2756..2f41de1 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -70,16 +70,6 @@
}
cc_library {
- name: "libbase",
- no_libgcc: true,
- nocrt: true,
- vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- }
- }
- cc_library {
name: "libc",
no_libgcc: true,
nocrt: true,
diff --git a/cc/tidy.go b/cc/tidy.go
index 0b78d6f..5455392 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -31,6 +31,9 @@
// Extra checks to enable or disable in clang-tidy
Tidy_checks []string
+
+ // Checks that should be treated as errors.
+ Tidy_checks_as_errors []string
}
type tidyFeature struct {
@@ -116,5 +119,9 @@
}
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
+ if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
+ flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
+ }
return flags
}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 5811b01..ae08b1c 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -77,3 +77,7 @@
return android.PathForSource(ctx, *library.Properties.Src)
}
+
+func (library *toolchainLibraryDecorator) nativeCoverage() bool {
+ return false
+}
diff --git a/cc/util.go b/cc/util.go
index 782bf61..5dcbaef 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -84,13 +84,9 @@
groupStaticLibs: in.GroupStaticLibs,
- protoDeps: in.protoDeps,
- protoFlags: strings.Join(in.protoFlags, " "),
- protoOutTypeFlag: in.protoOutTypeFlag,
- protoOutParams: strings.Join(in.protoOutParams, ","),
+ proto: in.proto,
protoC: in.protoC,
protoOptionsFile: in.protoOptionsFile,
- protoRoot: in.ProtoRoot,
}
}
diff --git a/cmd/dep_fixer/main.go b/cmd/dep_fixer/main.go
index 0647fb2..f94cf2f 100644
--- a/cmd/dep_fixer/main.go
+++ b/cmd/dep_fixer/main.go
@@ -29,30 +29,42 @@
func main() {
flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "Usage: %s <depfile.d>", os.Args[0])
+ fmt.Fprintf(os.Stderr, "Usage: %s [-o <output>] <depfile.d> [<depfile.d>...]", os.Args[0])
flag.PrintDefaults()
}
output := flag.String("o", "", "Optional output file (defaults to rewriting source if necessary)")
flag.Parse()
- if flag.NArg() != 1 {
- log.Fatal("Expected a single file as an argument")
+ if flag.NArg() < 1 {
+ log.Fatal("Expected at least one input file as an argument")
}
- old, err := ioutil.ReadFile(flag.Arg(0))
- if err != nil {
- log.Fatalf("Error opening %q: %v", flag.Arg(0), err)
+ var mergedDeps *Deps
+ var firstInput []byte
+
+ for i, arg := range flag.Args() {
+ input, err := ioutil.ReadFile(arg)
+ if err != nil {
+ log.Fatalf("Error opening %q: %v", arg, err)
+ }
+
+ deps, err := Parse(arg, bytes.NewBuffer(append([]byte(nil), input...)))
+ if err != nil {
+ log.Fatalf("Failed to parse: %v", err)
+ }
+
+ if i == 0 {
+ mergedDeps = deps
+ firstInput = input
+ } else {
+ mergedDeps.Inputs = append(mergedDeps.Inputs, deps.Inputs...)
+ }
}
- deps, err := Parse(flag.Arg(0), bytes.NewBuffer(append([]byte(nil), old...)))
- if err != nil {
- log.Fatalf("Failed to parse: %v", err)
- }
-
- new := deps.Print()
+ new := mergedDeps.Print()
if *output == "" || *output == flag.Arg(0) {
- if !bytes.Equal(old, new) {
+ if !bytes.Equal(firstInput, new) {
err := ioutil.WriteFile(flag.Arg(0), new, 0666)
if err != nil {
log.Fatalf("Failed to write: %v", err)
diff --git a/cmd/diff_target_files/Android.bp b/cmd/diff_target_files/Android.bp
new file mode 100644
index 0000000..5397f4b
--- /dev/null
+++ b/cmd/diff_target_files/Android.bp
@@ -0,0 +1,16 @@
+blueprint_go_binary {
+ name: "diff_target_files",
+ srcs: [
+ "compare.go",
+ "diff_target_files.go",
+ "glob.go",
+ "target_files.go",
+ "whitelist.go",
+ "zip_artifact.go",
+ ],
+ testSrcs: [
+ "compare_test.go",
+ "glob_test.go",
+ "whitelist_test.go",
+ ],
+}
diff --git a/cmd/diff_target_files/compare.go b/cmd/diff_target_files/compare.go
new file mode 100644
index 0000000..00cd9ca
--- /dev/null
+++ b/cmd/diff_target_files/compare.go
@@ -0,0 +1,133 @@
+// 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 main
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// compareTargetFiles takes two ZipArtifacts and compares the files they contain by examining
+// the path, size, and CRC of each file.
+func compareTargetFiles(priZip, refZip ZipArtifact, artifact string, whitelists []whitelist, filters []string) (zipDiff, error) {
+ priZipFiles, err := priZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from primary zip %v", err)
+ }
+
+ refZipFiles, err := refZip.Files()
+ if err != nil {
+ return zipDiff{}, fmt.Errorf("error fetching target file lists from reference zip %v", err)
+ }
+
+ priZipFiles, err = filterTargetZipFiles(priZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ refZipFiles, err = filterTargetZipFiles(refZipFiles, artifact, filters)
+ if err != nil {
+ return zipDiff{}, err
+ }
+
+ // Compare the file lists from both builds
+ diff := diffTargetFilesLists(refZipFiles, priZipFiles)
+
+ return applyWhitelists(diff, whitelists)
+}
+
+// zipDiff contains the list of files that differ between two zip files.
+type zipDiff struct {
+ modified [][2]*ZipArtifactFile
+ onlyInA, onlyInB []*ZipArtifactFile
+}
+
+// String pretty-prints the list of files that differ between two zip files.
+func (d *zipDiff) String() string {
+ buf := &bytes.Buffer{}
+
+ must := func(n int, err error) {
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ var sizeChange int64
+
+ if len(d.modified) > 0 {
+ must(fmt.Fprintln(buf, "files modified:"))
+ for _, f := range d.modified {
+ must(fmt.Fprintf(buf, " %v (%v bytes -> %v bytes)\n", f[0].Name, f[0].UncompressedSize64, f[1].UncompressedSize64))
+ sizeChange += int64(f[1].UncompressedSize64) - int64(f[0].UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInA) > 0 {
+ must(fmt.Fprintln(buf, "files removed:"))
+ for _, f := range d.onlyInA {
+ must(fmt.Fprintf(buf, " - %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange -= int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.onlyInB) > 0 {
+ must(fmt.Fprintln(buf, "files added:"))
+ for _, f := range d.onlyInB {
+ must(fmt.Fprintf(buf, " + %v (%v bytes)\n", f.Name, f.UncompressedSize64))
+ sizeChange += int64(f.UncompressedSize64)
+ }
+ }
+
+ if len(d.modified) > 0 || len(d.onlyInA) > 0 || len(d.onlyInB) > 0 {
+ must(fmt.Fprintf(buf, "total size change: %v bytes\n", sizeChange))
+ }
+
+ return buf.String()
+}
+
+func diffTargetFilesLists(a, b []*ZipArtifactFile) zipDiff {
+ i := 0
+ j := 0
+
+ diff := zipDiff{}
+
+ for i < len(a) && j < len(b) {
+ if a[i].Name == b[j].Name {
+ if a[i].UncompressedSize64 != b[j].UncompressedSize64 || a[i].CRC32 != b[j].CRC32 {
+ diff.modified = append(diff.modified, [2]*ZipArtifactFile{a[i], b[j]})
+ }
+ i++
+ j++
+ } else if a[i].Name < b[j].Name {
+ // a[i] is not present in b
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ } else {
+ // b[j] is not present in a
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+ }
+ for i < len(a) {
+ diff.onlyInA = append(diff.onlyInA, a[i])
+ i++
+ }
+ for j < len(b) {
+ diff.onlyInB = append(diff.onlyInB, b[j])
+ j++
+ }
+
+ return diff
+}
diff --git a/cmd/diff_target_files/compare_test.go b/cmd/diff_target_files/compare_test.go
new file mode 100644
index 0000000..9d3f8a5
--- /dev/null
+++ b/cmd/diff_target_files/compare_test.go
@@ -0,0 +1,131 @@
+// 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 main
+
+import (
+ "archive/zip"
+ "reflect"
+ "testing"
+)
+
+func TestDiffTargetFilesLists(t *testing.T) {
+ zipArtifactFile := func(name string, crc32 uint32, size uint64) *ZipArtifactFile {
+ return &ZipArtifactFile{
+ File: &zip.File{
+ FileHeader: zip.FileHeader{
+ Name: name,
+ CRC32: crc32,
+ UncompressedSize64: size,
+ },
+ },
+ }
+ }
+ x0 := zipArtifactFile("x", 0, 0)
+ x1 := zipArtifactFile("x", 1, 0)
+ x2 := zipArtifactFile("x", 0, 2)
+ y0 := zipArtifactFile("y", 0, 0)
+ //y1 := zipArtifactFile("y", 1, 0)
+ //y2 := zipArtifactFile("y", 1, 2)
+ z0 := zipArtifactFile("z", 0, 0)
+ z1 := zipArtifactFile("z", 1, 0)
+ //z2 := zipArtifactFile("z", 1, 2)
+
+ testCases := []struct {
+ name string
+ a, b []*ZipArtifactFile
+ diff zipDiff
+ }{
+ {
+ name: "same",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, nil},
+ },
+ {
+ name: "first only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{y0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{x0}, nil},
+ },
+ {
+ name: "middle only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, z0},
+ diff: zipDiff{nil, []*ZipArtifactFile{y0}, nil},
+ },
+ {
+ name: "last only in a",
+ a: []*ZipArtifactFile{x0, y0, z0},
+ b: []*ZipArtifactFile{x0, y0},
+ diff: zipDiff{nil, []*ZipArtifactFile{z0}, nil},
+ },
+
+ {
+ name: "first only in b",
+ a: []*ZipArtifactFile{y0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{x0}},
+ },
+ {
+ name: "middle only in b",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "last only in b",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x0, y0, z0},
+ diff: zipDiff{nil, nil, []*ZipArtifactFile{z0}},
+ },
+
+ {
+ name: "diff",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, nil, nil},
+ },
+ {
+ name: "diff plus unique last",
+ a: []*ZipArtifactFile{x0, y0},
+ b: []*ZipArtifactFile{x1, z0},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x1}}, []*ZipArtifactFile{y0}, []*ZipArtifactFile{z0}},
+ },
+ {
+ name: "diff plus unique first",
+ a: []*ZipArtifactFile{x0, z0},
+ b: []*ZipArtifactFile{y0, z1},
+ diff: zipDiff{[][2]*ZipArtifactFile{{z0, z1}}, []*ZipArtifactFile{x0}, []*ZipArtifactFile{y0}},
+ },
+ {
+ name: "diff size",
+ a: []*ZipArtifactFile{x0},
+ b: []*ZipArtifactFile{x2},
+ diff: zipDiff{[][2]*ZipArtifactFile{{x0, x2}}, nil, nil},
+ },
+ }
+
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ diff := diffTargetFilesLists(test.a, test.b)
+
+ if !reflect.DeepEqual(diff, test.diff) {
+
+ t.Errorf("diffTargetFilesLists = %v, %v, %v", diff.modified, diff.onlyInA, diff.onlyInB)
+ t.Errorf(" want %v, %v, %v", test.diff.modified, test.diff.onlyInA, test.diff.onlyInB)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/diff_target_files.go b/cmd/diff_target_files/diff_target_files.go
new file mode 100644
index 0000000..75bc8ee
--- /dev/null
+++ b/cmd/diff_target_files/diff_target_files.go
@@ -0,0 +1,82 @@
+// 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 main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+)
+
+var (
+ whitelists = newMultiString("whitelist", "whitelist patterns in the form <pattern>[:<regex of line to ignore>]")
+ whitelistFiles = newMultiString("whitelist_file", "files containing whitelist definitions")
+
+ filters = newMultiString("filter", "filter patterns to apply to files in target-files.zip before comparing")
+)
+
+func newMultiString(name, usage string) *multiString {
+ var f multiString
+ flag.Var(&f, name, usage)
+ return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+ flag.Parse()
+
+ if flag.NArg() != 2 {
+ fmt.Fprintf(os.Stderr, "Error, exactly two arguments are required\n")
+ os.Exit(1)
+ }
+
+ whitelists, err := parseWhitelists(*whitelists, *whitelistFiles)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error parsing whitelists: %v\n", err)
+ os.Exit(1)
+ }
+
+ priZip, err := NewLocalZipArtifact(flag.Arg(0))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(0), err)
+ os.Exit(1)
+ }
+ defer priZip.Close()
+
+ refZip, err := NewLocalZipArtifact(flag.Arg(1))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening zip file %v: %v\n", flag.Arg(1), err)
+ os.Exit(1)
+ }
+ defer refZip.Close()
+
+ diff, err := compareTargetFiles(priZip, refZip, targetFilesPattern, whitelists, *filters)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error comparing zip files: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Print(diff.String())
+
+ if len(diff.modified) > 0 || len(diff.onlyInA) > 0 || len(diff.onlyInB) > 0 {
+ fmt.Fprintln(os.Stderr, "differences found")
+ os.Exit(1)
+ }
+}
diff --git a/cmd/diff_target_files/glob.go b/cmd/diff_target_files/glob.go
new file mode 100644
index 0000000..ed91af7
--- /dev/null
+++ b/cmd/diff_target_files/glob.go
@@ -0,0 +1,81 @@
+// 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 main
+
+import (
+ "errors"
+ "path/filepath"
+ "strings"
+)
+
+// Match returns true if name matches pattern using the same rules as filepath.Match, but supporting
+// recursive globs (**).
+func Match(pattern, name string) (bool, error) {
+ if filepath.Base(pattern) == "**" {
+ return false, errors.New("pattern has '**' as last path element")
+ }
+
+ patternDir := pattern[len(pattern)-1] == '/'
+ nameDir := name[len(name)-1] == '/'
+
+ if patternDir != nameDir {
+ return false, nil
+ }
+
+ if nameDir {
+ name = name[:len(name)-1]
+ pattern = pattern[:len(pattern)-1]
+ }
+
+ for {
+ var patternFile, nameFile string
+ pattern, patternFile = filepath.Dir(pattern), filepath.Base(pattern)
+
+ if patternFile == "**" {
+ if strings.Contains(pattern, "**") {
+ return false, errors.New("pattern contains multiple '**'")
+ }
+ // Test if the any prefix of name matches the part of the pattern before **
+ for {
+ if name == "." || name == "/" {
+ return name == pattern, nil
+ }
+ if match, err := filepath.Match(pattern, name); err != nil {
+ return false, err
+ } else if match {
+ return true, nil
+ }
+ name = filepath.Dir(name)
+ }
+ } else if strings.Contains(patternFile, "**") {
+ return false, errors.New("pattern contains other characters between '**' and path separator")
+ }
+
+ name, nameFile = filepath.Dir(name), filepath.Base(name)
+
+ if nameFile == "." && patternFile == "." {
+ return true, nil
+ } else if nameFile == "/" && patternFile == "/" {
+ return true, nil
+ } else if nameFile == "." || patternFile == "." || nameFile == "/" || patternFile == "/" {
+ return false, nil
+ }
+
+ match, err := filepath.Match(patternFile, nameFile)
+ if err != nil || !match {
+ return match, err
+ }
+ }
+}
diff --git a/cmd/diff_target_files/glob_test.go b/cmd/diff_target_files/glob_test.go
new file mode 100644
index 0000000..63df68d
--- /dev/null
+++ b/cmd/diff_target_files/glob_test.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 main
+
+import (
+ "testing"
+)
+
+func TestMatch(t *testing.T) {
+ testCases := []struct {
+ pattern, name string
+ match bool
+ }{
+ {"a/*", "b/", false},
+ {"a/*", "b/a", false},
+ {"a/*", "b/b/", false},
+ {"a/*", "b/b/c", false},
+ {"a/**/*", "b/", false},
+ {"a/**/*", "b/a", false},
+ {"a/**/*", "b/b/", false},
+ {"a/**/*", "b/b/c", false},
+
+ {"a/*", "a/", false},
+ {"a/*", "a/a", true},
+ {"a/*", "a/b/", false},
+ {"a/*", "a/b/c", false},
+
+ {"a/*/", "a/", false},
+ {"a/*/", "a/a", false},
+ {"a/*/", "a/b/", true},
+ {"a/*/", "a/b/c", false},
+
+ {"a/**/*", "a/", false},
+ {"a/**/*", "a/a", true},
+ {"a/**/*", "a/b/", false},
+ {"a/**/*", "a/b/c", true},
+
+ {"a/**/*/", "a/", false},
+ {"a/**/*/", "a/a", false},
+ {"a/**/*/", "a/b/", true},
+ {"a/**/*/", "a/b/c", false},
+
+ {"**/*", "a/", false},
+ {"**/*", "a/a", true},
+ {"**/*", "a/b/", false},
+ {"**/*", "a/b/c", true},
+
+ {"**/*/", "a/", true},
+ {"**/*/", "a/a", false},
+ {"**/*/", "a/b/", true},
+ {"**/*/", "a/b/c", false},
+
+ {`a/\*\*/\*`, `a/**/*`, true},
+ {`a/\*\*/\*`, `a/a/*`, false},
+ {`a/\*\*/\*`, `a/**/a`, false},
+ {`a/\*\*/\*`, `a/a/a`, false},
+
+ {`a/**/\*`, `a/**/*`, true},
+ {`a/**/\*`, `a/a/*`, true},
+ {`a/**/\*`, `a/**/a`, false},
+ {`a/**/\*`, `a/a/a`, false},
+
+ {`a/\*\*/*`, `a/**/*`, true},
+ {`a/\*\*/*`, `a/a/*`, false},
+ {`a/\*\*/*`, `a/**/a`, true},
+ {`a/\*\*/*`, `a/a/a`, false},
+
+ {`*/**/a`, `a/a/a`, true},
+ {`*/**/a`, `*/a/a`, true},
+ {`*/**/a`, `a/**/a`, true},
+ {`*/**/a`, `*/**/a`, true},
+
+ {`\*/\*\*/a`, `a/a/a`, false},
+ {`\*/\*\*/a`, `*/a/a`, false},
+ {`\*/\*\*/a`, `a/**/a`, false},
+ {`\*/\*\*/a`, `*/**/a`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {`a/[a-c]`, `a/b`, true},
+ {`a/[abc]`, `a/b`, true},
+
+ {`a/\[abc]`, `a/b`, false},
+ {`a/\[abc]`, `a/[abc]`, true},
+
+ {`a/\[abc\]`, `a/b`, false},
+ {`a/\[abc\]`, `a/[abc]`, true},
+
+ {`a/?`, `a/?`, true},
+ {`a/?`, `a/a`, true},
+ {`a/\?`, `a/?`, true},
+ {`a/\?`, `a/a`, false},
+
+ {"/a/*", "/a/", false},
+ {"/a/*", "/a/a", true},
+ {"/a/*", "/a/b/", false},
+ {"/a/*", "/a/b/c", false},
+
+ {"/a/*/", "/a/", false},
+ {"/a/*/", "/a/a", false},
+ {"/a/*/", "/a/b/", true},
+ {"/a/*/", "/a/b/c", false},
+
+ {"/a/**/*", "/a/", false},
+ {"/a/**/*", "/a/a", true},
+ {"/a/**/*", "/a/b/", false},
+ {"/a/**/*", "/a/b/c", true},
+
+ {"/**/*", "/a/", false},
+ {"/**/*", "/a/a", true},
+ {"/**/*", "/a/b/", false},
+ {"/**/*", "/a/b/c", true},
+
+ {"/**/*/", "/a/", true},
+ {"/**/*/", "/a/a", false},
+ {"/**/*/", "/a/b/", true},
+ {"/**/*/", "/a/b/c", false},
+
+ {`a`, `/a`, false},
+ {`/a`, `a`, false},
+ {`*`, `/a`, false},
+ {`/*`, `a`, false},
+ {`**/*`, `/a`, false},
+ {`/**/*`, `a`, false},
+ }
+
+ for _, test := range testCases {
+ t.Run(test.pattern+","+test.name, func(t *testing.T) {
+ match, err := Match(test.pattern, test.name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if match != test.match {
+ t.Errorf("want: %v, got %v", test.match, match)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/known_nondeterminism.whitelist b/cmd/diff_target_files/known_nondeterminism.whitelist
new file mode 100644
index 0000000..6d71403
--- /dev/null
+++ b/cmd/diff_target_files/known_nondeterminism.whitelist
@@ -0,0 +1,10 @@
+// List of files that are known to be non-deterministic, along with the
+// bug number to tracking fixing the non-determinism.
+[
+ {
+ "Paths": [
+ // b/120039850
+ "system/framework/oat/*/services.art"
+ ]
+ }
+]
diff --git a/cmd/diff_target_files/props.whitelist b/cmd/diff_target_files/props.whitelist
new file mode 100644
index 0000000..9245b8b
--- /dev/null
+++ b/cmd/diff_target_files/props.whitelist
@@ -0,0 +1,18 @@
+[
+ // Ignore date, version and hostname properties in build.prop and prop.default files.
+ {
+ "Paths": [
+ "**/build.prop",
+ "**/prop.default"
+ ],
+ "IgnoreMatchingLines": [
+ "ro\\..*build\\.date=.*",
+ "ro\\..*build\\.date\\.utc=.*",
+ "ro\\..*build\\.version\\.incremental=.*",
+ "ro\\..*build\\.fingerprint=.*",
+ "ro\\.build\\.display\\.id=.*",
+ "ro\\.build\\.description=.*",
+ "ro\\.build\\.host=.*"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/cmd/diff_target_files/target_files.go b/cmd/diff_target_files/target_files.go
new file mode 100644
index 0000000..8705ca7
--- /dev/null
+++ b/cmd/diff_target_files/target_files.go
@@ -0,0 +1,86 @@
+// 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 main
+
+import (
+ "fmt"
+ "strings"
+)
+
+const targetFilesPattern = "*-target_files-*.zip"
+
+var targetZipPartitions = []string{
+ "BOOT/RAMDISK/",
+ "BOOT/",
+ "DATA/",
+ "ODM/",
+ "OEM/",
+ "PRODUCT/",
+ "PRODUCT_SERVICES/",
+ "ROOT/",
+ "SYSTEM/",
+ "SYSTEM_OTHER/",
+ "VENDOR/",
+}
+
+var targetZipFilter = []string{
+ "IMAGES/",
+ "OTA/",
+ "META/",
+ "PREBUILT_IMAGES/",
+ "RADIO/",
+}
+
+func filterTargetZipFiles(files []*ZipArtifactFile, artifact string, patterns []string) ([]*ZipArtifactFile, error) {
+ var ret []*ZipArtifactFile
+outer:
+ for _, f := range files {
+ if f.FileInfo().IsDir() {
+ continue
+ }
+
+ if artifact == targetFilesPattern {
+ found := false
+ for _, p := range targetZipPartitions {
+ if strings.HasPrefix(f.Name, p) {
+ f.Name = strings.ToLower(p) + strings.TrimPrefix(f.Name, p)
+ found = true
+ }
+ }
+ for _, filter := range targetZipFilter {
+ if strings.HasPrefix(f.Name, filter) {
+ continue outer
+ }
+ }
+
+ if !found {
+ return nil, fmt.Errorf("unmatched prefix for %s", f.Name)
+ }
+ }
+
+ if patterns != nil {
+ for _, pattern := range patterns {
+ match, _ := Match(pattern, f.Name)
+ if match {
+ ret = append(ret, f)
+ }
+ }
+ } else {
+ ret = append(ret, f)
+ }
+ }
+
+ return ret, nil
+}
diff --git a/cmd/diff_target_files/whitelist.go b/cmd/diff_target_files/whitelist.go
new file mode 100644
index 0000000..f00fc1e
--- /dev/null
+++ b/cmd/diff_target_files/whitelist.go
@@ -0,0 +1,251 @@
+// 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 main
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+ "unicode"
+)
+
+type jsonWhitelist struct {
+ Paths []string
+ IgnoreMatchingLines []string
+}
+
+type whitelist struct {
+ path string
+ ignoreMatchingLines []string
+}
+
+func parseWhitelists(whitelists []string, whitelistFiles []string) ([]whitelist, error) {
+ var ret []whitelist
+
+ add := func(path string, ignoreMatchingLines []string) {
+ for _, x := range ret {
+ if x.path == path {
+ x.ignoreMatchingLines = append(x.ignoreMatchingLines, ignoreMatchingLines...)
+ return
+ }
+ }
+
+ ret = append(ret, whitelist{
+ path: path,
+ ignoreMatchingLines: ignoreMatchingLines,
+ })
+ }
+
+ for _, file := range whitelistFiles {
+ newWhitelists, err := parseWhitelistFile(file)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, w := range newWhitelists {
+ add(w.path, w.ignoreMatchingLines)
+ }
+ }
+
+ for _, s := range whitelists {
+ colon := strings.IndexRune(s, ':')
+ var ignoreMatchingLines []string
+ if colon >= 0 {
+ ignoreMatchingLines = []string{s[colon+1:]}
+ }
+ add(s, ignoreMatchingLines)
+ }
+
+ return ret, nil
+}
+
+func parseWhitelistFile(file string) ([]whitelist, error) {
+ r, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Close()
+
+ d := json.NewDecoder(newJSONCommentStripper(r))
+
+ var jsonWhitelists []jsonWhitelist
+
+ err = d.Decode(&jsonWhitelists)
+
+ var whitelists []whitelist
+ for _, w := range jsonWhitelists {
+ for _, p := range w.Paths {
+ whitelists = append(whitelists, whitelist{
+ path: p,
+ ignoreMatchingLines: w.IgnoreMatchingLines,
+ })
+ }
+ }
+
+ return whitelists, err
+}
+
+func filterModifiedPaths(l [][2]*ZipArtifactFile, whitelists []whitelist) ([][2]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i][0].Name); err != nil {
+ return l, err
+ } else if match {
+ if match, err := diffIgnoringMatchingLines(l[i][0], l[i][1], w.ignoreMatchingLines); err != nil {
+ return l, err
+ } else if match || len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func filterNewPaths(l []*ZipArtifactFile, whitelists []whitelist) ([]*ZipArtifactFile, error) {
+outer:
+ for i := 0; i < len(l); i++ {
+ for _, w := range whitelists {
+ if match, err := Match(w.path, l[i].Name); err != nil {
+ return l, err
+ } else if match && len(w.ignoreMatchingLines) == 0 {
+ l = append(l[:i], l[i+1:]...)
+ i--
+ }
+ continue outer
+ }
+ }
+
+ if len(l) == 0 {
+ l = nil
+ }
+
+ return l, nil
+}
+
+func diffIgnoringMatchingLines(a *ZipArtifactFile, b *ZipArtifactFile, ignoreMatchingLines []string) (match bool, err error) {
+ lineMatchesIgnores := func(b []byte) (bool, error) {
+ for _, m := range ignoreMatchingLines {
+ if match, err := regexp.Match(m, b); err != nil {
+ return false, err
+ } else if match {
+ return match, nil
+ }
+ }
+ return false, nil
+ }
+
+ filter := func(z *ZipArtifactFile) ([]byte, error) {
+ var ret []byte
+
+ r, err := z.Open()
+ if err != nil {
+ return nil, err
+ }
+ s := bufio.NewScanner(r)
+
+ for s.Scan() {
+ if match, err := lineMatchesIgnores(s.Bytes()); err != nil {
+ return nil, err
+ } else if !match {
+ ret = append(ret, "\n"...)
+ ret = append(ret, s.Bytes()...)
+ }
+ }
+
+ return ret, nil
+ }
+
+ bufA, err := filter(a)
+ if err != nil {
+ return false, err
+ }
+ bufB, err := filter(b)
+ if err != nil {
+ return false, err
+ }
+
+ return bytes.Compare(bufA, bufB) == 0, nil
+}
+
+func applyWhitelists(diff zipDiff, whitelists []whitelist) (zipDiff, error) {
+ var err error
+
+ diff.modified, err = filterModifiedPaths(diff.modified, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInA, err = filterNewPaths(diff.onlyInA, whitelists)
+ if err != nil {
+ return diff, err
+ }
+ diff.onlyInB, err = filterNewPaths(diff.onlyInB, whitelists)
+ if err != nil {
+ return diff, err
+ }
+
+ return diff, nil
+}
+
+func newJSONCommentStripper(r io.Reader) *jsonCommentStripper {
+ return &jsonCommentStripper{
+ r: bufio.NewReader(r),
+ }
+}
+
+type jsonCommentStripper struct {
+ r *bufio.Reader
+ b []byte
+ err error
+}
+
+func (j *jsonCommentStripper) Read(buf []byte) (int, error) {
+ for len(j.b) == 0 {
+ if j.err != nil {
+ return 0, j.err
+ }
+
+ j.b, j.err = j.r.ReadBytes('\n')
+
+ if isComment(j.b) {
+ j.b = nil
+ }
+ }
+
+ n := copy(buf, j.b)
+ j.b = j.b[n:]
+ return n, nil
+}
+
+var commentPrefix = []byte("//")
+
+func isComment(b []byte) bool {
+ for len(b) > 0 && unicode.IsSpace(rune(b[0])) {
+ b = b[1:]
+ }
+ return bytes.HasPrefix(b, commentPrefix)
+}
diff --git a/cmd/diff_target_files/whitelist_test.go b/cmd/diff_target_files/whitelist_test.go
new file mode 100644
index 0000000..4b19fdd
--- /dev/null
+++ b/cmd/diff_target_files/whitelist_test.go
@@ -0,0 +1,126 @@
+// 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 main
+
+import (
+ "archive/zip"
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func bytesToZipArtifactFile(name string, data []byte) *ZipArtifactFile {
+ buf := &bytes.Buffer{}
+ w := zip.NewWriter(buf)
+ f, err := w.Create(name)
+ if err != nil {
+ panic(err)
+ }
+ _, err = f.Write(data)
+ if err != nil {
+ panic(err)
+ }
+
+ w.Close()
+
+ r, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ panic(err)
+ }
+
+ return &ZipArtifactFile{r.File[0]}
+}
+
+var f1a = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: bar
+c
+`))
+
+var f1b = bytesToZipArtifactFile("dir/f1", []byte(`
+a
+foo: baz
+c
+`))
+
+var f2 = bytesToZipArtifactFile("dir/f2", nil)
+
+func Test_applyWhitelists(t *testing.T) {
+ type args struct {
+ diff zipDiff
+ whitelists []whitelist
+ }
+ tests := []struct {
+ name string
+ args args
+ want zipDiff
+ wantErr bool
+ }{
+ {
+ name: "simple",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/f1"}},
+ },
+ want: zipDiff{
+ onlyInA: []*ZipArtifactFile{f2},
+ },
+ },
+ {
+ name: "glob",
+ args: args{
+ diff: zipDiff{
+ onlyInA: []*ZipArtifactFile{f1a, f2},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "modified",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*"}},
+ },
+ want: zipDiff{},
+ },
+ {
+ name: "matching lines",
+ args: args{
+ diff: zipDiff{
+ modified: [][2]*ZipArtifactFile{{f1a, f1b}},
+ },
+ whitelists: []whitelist{{path: "dir/*", ignoreMatchingLines: []string{"foo: .*"}}},
+ },
+ want: zipDiff{},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := applyWhitelists(tt.args.diff, tt.args.whitelists)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("applyWhitelists() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("applyWhitelists() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/cmd/diff_target_files/zip_artifact.go b/cmd/diff_target_files/zip_artifact.go
new file mode 100644
index 0000000..08ce889
--- /dev/null
+++ b/cmd/diff_target_files/zip_artifact.go
@@ -0,0 +1,174 @@
+// 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 main
+
+import (
+ "archive/zip"
+ "context"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+)
+
+// ZipArtifact represents a zip file that may be local or remote.
+type ZipArtifact interface {
+ // Files returns the list of files contained in the zip file.
+ Files() ([]*ZipArtifactFile, error)
+
+ // Close closes the zip file artifact.
+ Close()
+}
+
+// localZipArtifact is a handle to a local zip file artifact.
+type localZipArtifact struct {
+ zr *zip.ReadCloser
+ files []*ZipArtifactFile
+}
+
+// NewLocalZipArtifact returns a ZipArtifact for a local zip file..
+func NewLocalZipArtifact(name string) (ZipArtifact, error) {
+ zr, err := zip.OpenReader(name)
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*ZipArtifactFile
+ for _, zf := range zr.File {
+ files = append(files, &ZipArtifactFile{zf})
+ }
+
+ return &localZipArtifact{
+ zr: zr,
+ files: files,
+ }, nil
+}
+
+// Files returns the list of files contained in the local zip file artifact.
+func (z *localZipArtifact) Files() ([]*ZipArtifactFile, error) {
+ return z.files, nil
+}
+
+// Close closes the buffered reader of the local zip file artifact.
+func (z *localZipArtifact) Close() {
+ z.zr.Close()
+}
+
+// ZipArtifactFile contains a zip.File handle to the data inside the remote *-target_files-*.zip
+// build artifact.
+type ZipArtifactFile struct {
+ *zip.File
+}
+
+// Extract begins extract a file from inside a ZipArtifact. It returns an
+// ExtractedZipArtifactFile handle.
+func (zf *ZipArtifactFile) Extract(ctx context.Context, dir string,
+ limiter chan bool) *ExtractedZipArtifactFile {
+
+ d := &ExtractedZipArtifactFile{
+ initCh: make(chan struct{}),
+ }
+
+ go func() {
+ defer close(d.initCh)
+ limiter <- true
+ defer func() { <-limiter }()
+
+ zr, err := zf.Open()
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer zr.Close()
+
+ crc := crc32.NewIEEE()
+ r := io.TeeReader(zr, crc)
+
+ if filepath.Clean(zf.Name) != zf.Name {
+ d.err = fmt.Errorf("invalid filename %q", zf.Name)
+ return
+ }
+ path := filepath.Join(dir, zf.Name)
+
+ err = os.MkdirAll(filepath.Dir(path), 0777)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Remove(path)
+ if err != nil && !os.IsNotExist(err) {
+ d.err = err
+ return
+ }
+
+ if zf.Mode().IsRegular() {
+ w, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, zf.Mode())
+ if err != nil {
+ d.err = err
+ return
+ }
+ defer w.Close()
+
+ _, err = io.Copy(w, r)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else if zf.Mode()&os.ModeSymlink != 0 {
+ target, err := ioutil.ReadAll(r)
+ if err != nil {
+ d.err = err
+ return
+ }
+
+ err = os.Symlink(string(target), path)
+ if err != nil {
+ d.err = err
+ return
+ }
+ } else {
+ d.err = fmt.Errorf("unknown mode %q", zf.Mode())
+ return
+ }
+
+ if crc.Sum32() != zf.CRC32 {
+ d.err = fmt.Errorf("crc mismatch for %v", zf.Name)
+ return
+ }
+
+ d.path = path
+ }()
+
+ return d
+}
+
+// ExtractedZipArtifactFile is a handle to a downloaded file from a remoteZipArtifact. The download
+// may still be in progress, and will be complete with Path() returns.
+type ExtractedZipArtifactFile struct {
+ initCh chan struct{}
+ err error
+
+ path string
+}
+
+// Path returns the path to the downloaded file and any errors that occurred during the download.
+// It will block until the download is complete.
+func (d *ExtractedZipArtifactFile) Path() (string, error) {
+ <-d.initCh
+ return d.path, d.err
+}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 0af1886..4167edb 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -24,6 +24,7 @@
"path"
"path/filepath"
"strings"
+ "time"
)
var (
@@ -265,6 +266,15 @@
if err != nil {
return err
}
+
+ // Update the timestamp of the output file in case the tool wrote an old timestamp (for example, tar can extract
+ // files with old timestamps).
+ now := time.Now()
+ err = os.Chtimes(tempPath, now, now)
+ if err != nil {
+ return err
+ }
+
err = os.Rename(tempPath, destPath)
if err != nil {
return err
diff --git a/cmd/soong_env/soong_env.go b/cmd/soong_env/soong_env.go
index 933e525..d305d83 100644
--- a/cmd/soong_env/soong_env.go
+++ b/cmd/soong_env/soong_env.go
@@ -15,7 +15,7 @@
// soong_glob is the command line tool that checks if the list of files matching a glob has
// changed, and only updates the output file list if it has changed. It is used to optimize
// out build.ninja regenerations when non-matching files are added. See
-// android/soong/common/glob.go for a longer description.
+// android/soong/android/glob.go for a longer description.
package main
import (
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 32acd8c..e259b1d 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -49,8 +49,10 @@
GeneratedDeps() android.Paths
}
+// Alias for android.HostToolProvider
+// Deprecated: use android.HostToolProvider instead.
type HostToolProvider interface {
- HostToolPath() android.OptionalPath
+ android.HostToolProvider
}
type hostToolDependencyTag struct {
@@ -193,7 +195,7 @@
tool := ctx.OtherModuleName(module)
var path android.OptionalPath
- if t, ok := module.(HostToolProvider); ok {
+ if t, ok := module.(android.HostToolProvider); ok {
if !t.(android.Module).Enabled() {
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{tool})
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 19b22f7..5cb51b8 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -527,4 +527,4 @@
return android.OptionalPathForPath(t.outputFile)
}
-var _ HostToolProvider = (*testTool)(nil)
+var _ android.HostToolProvider = (*testTool)(nil)
diff --git a/java/androidmk.go b/java/androidmk.go
index 5b4f738..908286a 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -95,16 +95,21 @@
}
}
+// Called for modules that are a component of a test suite.
+func testSuiteComponent(w io.Writer, test_suites []string) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(test_suites, " "))
+ } else {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
+ }
+}
+
func (j *Test) AndroidMk() android.AndroidMkData {
data := j.Library.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(j.testProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(j.testProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, j.testProperties.Test_suites)
if j.testConfig != nil {
fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", j.testConfig.String())
}
@@ -115,6 +120,15 @@
return data
}
+func (j *TestHelperLibrary) AndroidMk() android.AndroidMkData {
+ data := j.Library.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ testSuiteComponent(w, j.testHelperLibraryProperties.Test_suites)
+ })
+
+ return data
+}
+
func (prebuilt *Import) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Class: "JAVA_LIBRARIES",
@@ -321,13 +335,7 @@
func (a *AndroidTest) AndroidMk() android.AndroidMkData {
data := a.AndroidApp.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(a.testProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(a.testProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, a.testProperties.Test_suites)
if a.testConfig != nil {
fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", a.testConfig.String())
}
@@ -340,13 +348,7 @@
func (a *AndroidTestHelperApp) AndroidMk() android.AndroidMkData {
data := a.AndroidApp.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
- fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
- if len(a.appTestHelperAppProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(a.appTestHelperAppProperties.Test_suites, " "))
- } else {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := null-suite")
- }
+ testSuiteComponent(w, a.appTestHelperAppProperties.Test_suites)
})
return data
diff --git a/java/app.go b/java/app.go
index b31f232..ab623e2 100644
--- a/java/app.go
+++ b/java/app.go
@@ -74,6 +74,11 @@
// Store dex files uncompressed in the APK and set the android:useEmbeddedDex="true" manifest attribute so that
// they are used from inside the APK at runtime.
Use_embedded_dex *bool
+
+ // Forces native libraries to always be packaged into the APK,
+ // Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
+ // True for android_test* modules.
+ AlwaysPackageNativeLibs bool `blueprint:"mutated"`
}
// android_app properties that can be overridden by override_android_app
@@ -81,6 +86,9 @@
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
// or an android_app_certificate module name in the form ":module".
Certificate *string
+
+ // the package name of this app. The package name in the manifest file is used if one was not given.
+ Package_name *string
}
type AndroidApp struct {
@@ -223,11 +231,12 @@
}
}
- // TODO: LOCAL_PACKAGE_OVERRIDES
- // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
-
manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
- if overridden {
+ if overridden || a.overridableAppProperties.Package_name != nil {
+ // The product override variable has a priority over the package_name property.
+ if !overridden {
+ manifestPackageName = *a.overridableAppProperties.Package_name
+ }
aaptLinkFlags = append(aaptLinkFlags, "--rename-manifest-package "+manifestPackageName)
}
@@ -281,7 +290,8 @@
func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
var jniJarFile android.WritablePath
if len(jniLibs) > 0 {
- embedJni := ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs)
+ embedJni := ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
+ a.appProperties.AlwaysPackageNativeLibs
if embedJni {
jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.shouldUncompressJNI(ctx))
@@ -503,6 +513,7 @@
module.Module.properties.Instrument = true
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
@@ -543,6 +554,7 @@
module.Module.properties.Installable = proptools.BoolPtr(true)
module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
+ module.appProperties.AlwaysPackageNativeLibs = true
module.Module.dexpreopter.isTest = true
module.AddProperties(
diff --git a/java/app_test.go b/java/app_test.go
index cf57c80..1f6297c 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -16,6 +16,8 @@
import (
"android/soong/android"
+ "android/soong/cc"
+
"fmt"
"path/filepath"
"reflect"
@@ -537,43 +539,8 @@
}
}
-func TestJNI(t *testing.T) {
- ctx := testJava(t, `
- toolchain_library {
- name: "libcompiler_rt-extras",
- src: "",
- }
-
- toolchain_library {
- name: "libatomic",
- src: "",
- }
-
- toolchain_library {
- name: "libgcc",
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-aarch64-android",
- src: "",
- }
-
- toolchain_library {
- name: "libclang_rt.builtins-arm-android",
- src: "",
- }
-
- cc_object {
- name: "crtbegin_so",
- stl: "none",
- }
-
- cc_object {
- name: "crtend_so",
- stl: "none",
- }
-
+func TestJNIABI(t *testing.T) {
+ ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
name: "libjni",
system_shared_libs: [],
@@ -615,13 +582,6 @@
}
`)
- // check the existence of the internal modules
- ctx.ModuleForTests("test", "android_common")
- ctx.ModuleForTests("test_first", "android_common")
- ctx.ModuleForTests("test_both", "android_common")
- ctx.ModuleForTests("test_32", "android_common")
- ctx.ModuleForTests("test_64", "android_common")
-
testCases := []struct {
name string
abis []string
@@ -652,6 +612,90 @@
}
}
+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",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test {
+ name: "test_noembed",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ use_embedded_native_libs: false,
+ }
+
+ android_test_helper_app {
+ name: "test_helper",
+ no_framework_libs: true,
+ jni_libs: ["libjni"],
+ }
+
+ android_test_helper_app {
+ name: "test_helper_noembed",
+ no_framework_libs: true,
+ 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) {
testCases := []struct {
name string
@@ -833,6 +877,7 @@
android_app {
name: "foo",
srcs: ["a.java"],
+ certificate: "expiredkey",
overrides: ["baz"],
}
@@ -846,6 +891,12 @@
name: "new_certificate",
certificate: "cert/new_cert",
}
+
+ override_android_app {
+ name: "baz",
+ base: "foo",
+ package_name: "org.dandroid.bp",
+ }
`)
expectedVariants := []struct {
@@ -854,18 +905,28 @@
apkPath string
signFlag string
overrides []string
+ aaptFlag string
}{
{
variantName: "android_common",
apkPath: "/target/product/test_device/system/app/foo/foo.apk",
- signFlag: "build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
overrides: []string{"baz"},
+ aaptFlag: "",
},
{
variantName: "bar_android_common",
apkPath: "/target/product/test_device/system/app/bar/bar.apk",
signFlag: "cert/new_cert.x509.pem cert/new_cert.pk8",
overrides: []string{"baz", "foo"},
+ aaptFlag: "",
+ },
+ {
+ variantName: "baz_android_common",
+ apkPath: "/target/product/test_device/system/app/baz/baz.apk",
+ signFlag: "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
+ overrides: []string{"baz", "foo"},
+ aaptFlag: "--rename-manifest-package org.dandroid.bp",
},
}
for _, expected := range expectedVariants {
@@ -892,10 +953,18 @@
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
}
+ // Check if the overrides field values are correctly aggregated.
mod := variant.Module().(*AndroidApp)
if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
expected.overrides, mod.appProperties.Overrides)
}
+
+ // Check the package renaming flag, if exists.
+ res := variant.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ if !strings.Contains(aapt2Flags, expected.aaptFlag) {
+ t.Errorf("package renaming flag, %q is missing in aapt2 link flags, %q", expected.aaptFlag, aapt2Flags)
+ }
}
}
diff --git a/java/builder.go b/java/builder.go
index d8b303e..ce9a5ee 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -133,7 +133,7 @@
)
func init() {
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
pctx.Import("android/soong/java/config")
}
@@ -153,10 +153,7 @@
kotlincFlags string
kotlincClasspath classpath
- protoFlags []string
- protoOutTypeFlag string // The flag itself: --java_out
- protoOutParams string // Parameters to that flag: --java_out=$protoOutParams:$outDir
- protoRoot bool
+ proto android.ProtoFlags
}
func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 141f7ba..cbb52f1 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -103,7 +103,7 @@
expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
}
- outputs := bootArt.Outputs.Strings()
+ outputs := append(android.WritablePaths{bootArt.Output}, bootArt.ImplicitOutputs...).Strings()
sort.Strings(outputs)
sort.Strings(expectedOutputs)
diff --git a/java/gen.go b/java/gen.go
index 8362556..500d887 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -118,7 +118,7 @@
javaFile := genLogtags(ctx, srcFile)
outSrcFiles = append(outSrcFiles, javaFile)
case ".proto":
- srcJarFile := genProto(ctx, srcFile, flags)
+ srcJarFile := genProto(ctx, srcFile, flags.proto)
outSrcFiles = append(outSrcFiles, srcJarFile)
case ".sysprop":
srcJarFile := genSysprop(ctx, srcFile)
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 86531eb..9627dc6 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -159,9 +159,9 @@
for moduleList, pathList := range moduleListToPathList {
for i := range pathList {
if pathList[i] == nil {
+ pathList[i] = android.PathForOutput(ctx, "missing")
if ctx.Config().AllowMissingDependencies() {
missingDeps = append(missingDeps, (*moduleList)[i])
- pathList[i] = android.PathForOutput(ctx, "missing")
} else {
ctx.Errorf("failed to find dex jar path for module %q",
(*moduleList)[i])
@@ -236,6 +236,8 @@
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-max-o.txt")).
FlagWithInput("--blacklist ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blacklist.txt")).
+ FlagWithInput("--greylist-packages ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-greylist-packages.txt")).
FlagWithOutput("--output ", tempPath)
commitChangeForRestat(rule, tempPath, outputPath)
diff --git a/java/java.go b/java/java.go
index 1fd0a9e..0417dee 100644
--- a/java/java.go
+++ b/java/java.go
@@ -41,6 +41,7 @@
android.RegisterModuleType("java_binary", BinaryFactory)
android.RegisterModuleType("java_binary_host", BinaryHostFactory)
android.RegisterModuleType("java_test", TestFactory)
+ android.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
android.RegisterModuleType("java_test_host", TestHostFactory)
android.RegisterModuleType("java_import", ImportFactory)
android.RegisterModuleType("java_import_host", ImportFactoryHost)
@@ -480,6 +481,7 @@
{Mutator: "arch", Variation: ctx.Config().BuildOsCommonVariant},
}, pluginTag, j.properties.Plugins...)
+ android.ProtoDeps(ctx, &j.protoProperties)
if j.hasSrcExt(".proto") {
protoDeps(ctx, &j.protoProperties)
}
@@ -767,12 +769,6 @@
deps.classpath = append(deps.classpath, dep.Srcs()...)
deps.staticJars = append(deps.staticJars, dep.Srcs()...)
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.Srcs()...)
- case android.DefaultsDepTag, android.SourceDepTag:
- // Nothing to do
- case publicApiFileTag, systemApiFileTag, testApiFileTag:
- // Nothing to do
- default:
- ctx.ModuleErrorf("dependency on genrule %q may only be in srcs, libs, or static_libs", otherName)
}
default:
switch tag {
@@ -1536,6 +1532,12 @@
Data []string `android:"path"`
}
+type testHelperLibraryProperties struct {
+ // list of compatibility suites (for example "cts", "vts") that the module should be
+ // installed into.
+ Test_suites []string `android:"arch_variant"`
+}
+
type Test struct {
Library
@@ -1545,6 +1547,12 @@
data android.Paths
}
+type TestHelperLibrary struct {
+ Library
+
+ testHelperLibraryProperties testHelperLibraryProperties
+}
+
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template, j.testProperties.Test_suites)
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -1552,6 +1560,10 @@
j.Library.GenerateAndroidBuildActions(ctx)
}
+func (j *TestHelperLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ j.Library.GenerateAndroidBuildActions(ctx)
+}
+
// java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
// creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
//
@@ -1577,6 +1589,21 @@
return module
}
+// java_test_helper_library creates a java library and makes sure that it is added to the appropriate test suite.
+func TestHelperLibraryFactory() android.Module {
+ module := &TestHelperLibrary{}
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.dexpreoptProperties,
+ &module.Module.protoProperties,
+ &module.testHelperLibraryProperties)
+
+ InitJavaModule(module, android.HostAndDeviceSupported)
+ return module
+}
+
// java_test_host builds a and links sources into a `.jar` file for the host, and creates an `AndroidTest.xml` file to
// allow running the test with `atest` or a `TEST_MAPPING` file.
//
diff --git a/java/java_test.go b/java/java_test.go
index ec6d27a..2546698 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -103,6 +103,7 @@
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
+ ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("link", cc.LinkageMutator).Parallel()
ctx.BottomUp("begin", cc.BeginMutator).Parallel()
@@ -137,6 +138,9 @@
"prebuilts/sdk/17/public/android.jar": nil,
"prebuilts/sdk/17/public/framework.aidl": nil,
"prebuilts/sdk/17/system/android.jar": nil,
+ "prebuilts/sdk/25/public/android.jar": nil,
+ "prebuilts/sdk/25/public/framework.aidl": nil,
+ "prebuilts/sdk/25/system/android.jar": nil,
"prebuilts/sdk/current/core/android.jar": nil,
"prebuilts/sdk/current/public/android.jar": nil,
"prebuilts/sdk/current/public/framework.aidl": nil,
diff --git a/java/proto.go b/java/proto.go
index 8028039..37de1d2 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -15,108 +15,83 @@
package java
import (
- "strings"
-
- "github.com/google/blueprint"
-
"android/soong/android"
)
-func init() {
- pctx.HostBinToolVariable("protocCmd", "aprotoc")
- pctx.HostBinToolVariable("depFixCmd", "dep_fixer")
-}
-
-var (
- proto = pctx.AndroidStaticRule("protoc",
- blueprint.RuleParams{
- Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
- `$protocCmd $protoOut=$protoOutParams:$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
- `$depFixCmd $out.d && ` +
- `${config.SoongZipCmd} -jar -o $out -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
- CommandDeps: []string{
- "$protocCmd",
- "$depFixCmd",
- "${config.SoongZipCmd}",
- },
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- }, "protoBase", "protoFlags", "protoOut", "protoOutParams")
-)
-
-func genProto(ctx android.ModuleContext, protoFile android.Path, flags javaBuilderFlags) android.Path {
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags) android.Path {
srcJarFile := android.GenPathWithExt(ctx, "proto", protoFile, "srcjar")
- var protoBase string
- if flags.protoRoot {
- protoBase = "."
- } else {
- protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
- }
+ outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
+ depFile := srcJarFile.ReplaceExtension(ctx, "srcjar.d")
- ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Output: srcJarFile,
- Input: protoFile,
- Args: map[string]string{
- "protoBase": protoBase,
- "protoOut": flags.protoOutTypeFlag,
- "protoOutParams": flags.protoOutParams,
- "protoFlags": strings.Join(flags.protoFlags, " "),
- },
- })
+ rule := android.NewRuleBuilder()
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+ rule.Command().Text("mkdir -p").Flag(outDir.String())
+
+ android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+
+ // Proto generated java files have an unknown package name in the path, so package the entire output directory
+ // into a srcjar.
+ rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ Flag("-jar").
+ FlagWithOutput("-o ", srcJarFile).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
+
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return srcJarFile
}
func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
- switch String(p.Proto.Type) {
- case "micro":
- ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
- case "nano":
- ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
- case "lite", "":
- ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
- case "full":
- if ctx.Host() {
- ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
- } else {
- ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "micro":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
+ case "nano":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
+ case "lite", "":
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
+ case "full":
+ if ctx.Host() {
+ ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
+ } else {
+ ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
+ }
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- String(p.Proto.Type))
}
}
func protoFlags(ctx android.ModuleContext, j *CompilerProperties, p *android.ProtoProperties,
flags javaBuilderFlags) javaBuilderFlags {
- switch String(p.Proto.Type) {
- case "micro":
- flags.protoOutTypeFlag = "--javamicro_out"
- case "nano":
- flags.protoOutTypeFlag = "--javanano_out"
- case "lite":
- flags.protoOutTypeFlag = "--java_out"
- flags.protoOutParams = "lite"
- case "full", "":
- flags.protoOutTypeFlag = "--java_out"
- default:
- ctx.PropertyErrorf("proto.type", "unknown proto type %q",
- String(p.Proto.Type))
- }
+ flags.proto = android.GetProtoFlags(ctx, p)
- if len(j.Proto.Output_params) > 0 {
- if flags.protoOutParams != "" {
- flags.protoOutParams += ","
+ if String(p.Proto.Plugin) == "" {
+ switch String(p.Proto.Type) {
+ case "micro":
+ flags.proto.OutTypeFlag = "--javamicro_out"
+ case "nano":
+ flags.proto.OutTypeFlag = "--javanano_out"
+ case "lite":
+ flags.proto.OutTypeFlag = "--java_out"
+ flags.proto.OutParams = append(flags.proto.OutParams, "lite")
+ case "full", "":
+ flags.proto.OutTypeFlag = "--java_out"
+ default:
+ ctx.PropertyErrorf("proto.type", "unknown proto type %q",
+ String(p.Proto.Type))
}
- flags.protoOutParams += strings.Join(j.Proto.Output_params, ",")
}
- flags.protoFlags = android.ProtoFlags(ctx, p)
- flags.protoRoot = android.ProtoCanonicalPathFromRoot(ctx, p)
+ flags.proto.OutParams = append(flags.proto.OutParams, j.Proto.Output_params...)
return flags
}
diff --git a/java/sdk.go b/java/sdk.go
index 0959be7..48e7746 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -84,7 +84,7 @@
v = strconv.Itoa(latestSdkVersion)
}
- i, err := sdkVersionToNumber(ctx, v)
+ numericSdkVersion, err := sdkVersionToNumber(ctx, v)
if err != nil {
ctx.PropertyErrorf("sdk_version", "%s", err)
return sdkDep{}
@@ -151,15 +151,14 @@
// Ensures that the specificed system SDK version is one of BOARD_SYSTEMSDK_VERSIONS (for vendor apks)
// or PRODUCT_SYSTEMSDK_VERSIONS (for other apks or when BOARD_SYSTEMSDK_VERSIONS is not set)
- if strings.HasPrefix(v, "system_") && i != android.FutureApiLevel {
+ if strings.HasPrefix(v, "system_") && numericSdkVersion != android.FutureApiLevel {
allowed_versions := ctx.DeviceConfig().PlatformSystemSdkVersions()
if ctx.DeviceSpecific() || ctx.SocSpecific() {
if len(ctx.DeviceConfig().SystemSdkVersions()) > 0 {
allowed_versions = ctx.DeviceConfig().SystemSdkVersions()
}
}
- version := strings.TrimPrefix(v, "system_")
- if len(allowed_versions) > 0 && !android.InList(version, allowed_versions) {
+ if len(allowed_versions) > 0 && !android.InList(strconv.Itoa(numericSdkVersion), allowed_versions) {
ctx.PropertyErrorf("sdk_version", "incompatible sdk version %q. System SDK version should be one of %q",
v, allowed_versions)
}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 6924e26..7fa40a3 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -50,11 +50,11 @@
},
{
- name: "sdk v14",
- properties: `sdk_version: "14",`,
+ name: "sdk v25",
+ properties: `sdk_version: "25",`,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
@@ -72,11 +72,11 @@
},
{
- name: "system_14",
- properties: `sdk_version: "system_14",`,
+ name: "system_25",
+ properties: `sdk_version: "system_25",`,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/14/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
@@ -140,12 +140,12 @@
},
{
- name: "unbundled sdk v14",
+ name: "unbundled sdk v25",
unbundled: true,
- properties: `sdk_version: "14",`,
+ properties: `sdk_version: "25",`,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
@@ -162,7 +162,7 @@
pdk: true,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/17/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
name: "pdk current",
@@ -170,15 +170,15 @@
properties: `sdk_version: "current",`,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/17/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
{
- name: "pdk 14",
+ name: "pdk 25",
pdk: true,
- properties: `sdk_version: "14",`,
+ properties: `sdk_version: "25",`,
bootclasspath: []string{`""`},
system: "bootclasspath", // special value to tell 1.9 test to expect bootclasspath
- classpath: []string{"prebuilts/sdk/14/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+ classpath: []string{"prebuilts/sdk/25/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
},
}
@@ -285,6 +285,44 @@
t.Errorf("bootclasspath expected %q != got %q", expected, got)
}
})
+
+ // Test again with PLATFORM_VERSION_CODENAME=REL
+ t.Run("REL", func(t *testing.T) {
+ config := testConfig(nil)
+ config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("REL")
+ config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(true)
+
+ if testcase.unbundled {
+ config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+ }
+ if testcase.pdk {
+ config.TestProductVariables.Pdk = proptools.BoolPtr(true)
+ }
+ ctx := testContext(config, bp, nil)
+ run(t, ctx, config)
+
+ javac := ctx.ModuleForTests("foo", variant).Rule("javac")
+
+ got := javac.Args["bootClasspath"]
+ if got != bc {
+ t.Errorf("bootclasspath expected %q != got %q", bc, got)
+ }
+
+ got = javac.Args["classpath"]
+ if got != c {
+ t.Errorf("classpath expected %q != got %q", c, got)
+ }
+
+ var deps []string
+ if len(bootclasspath) > 0 && bootclasspath[0] != `""` {
+ deps = append(deps, bootclasspath...)
+ }
+ deps = append(deps, classpath...)
+
+ if !reflect.DeepEqual(javac.Implicits.Strings(), deps) {
+ t.Errorf("implicits expected %q != got %q", deps, javac.Implicits.Strings())
+ }
+ })
})
}
diff --git a/java/testing.go b/java/testing.go
index 6febfa1..7d23d8f 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -28,7 +28,6 @@
env["ANDROID_JAVA8_HOME"] = "jdk8"
}
config := android.TestArchConfig(buildDir, env)
- config.TestProductVariables.DeviceSystemSdkVersions = []string{"14", "15"}
return config
}
diff --git a/phony/phony.go b/phony/phony.go
index e8a6550..ed6a2fe 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -34,7 +34,7 @@
func PhonyFactory() android.Module {
module := &phony{}
- android.InitAndroidModule(module)
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
return module
}
@@ -51,6 +51,9 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ if p.Host() {
+ fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
+ }
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES := "+strings.Join(p.requiredModuleNames, " "))
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
diff --git a/python/builder.go b/python/builder.go
index e3b490c..36baecd 100644
--- a/python/builder.go
+++ b/python/builder.go
@@ -73,7 +73,7 @@
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
- pctx.Import("android/soong/common")
+ pctx.Import("android/soong/android")
pctx.HostBinToolVariable("parCmd", "soong_zip")
pctx.HostBinToolVariable("mergeParCmd", "merge_zips")
diff --git a/python/proto.go b/python/proto.go
index 2370cd2..b3ffaa6 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -16,58 +16,35 @@
import (
"android/soong/android"
- "strings"
-
- "github.com/google/blueprint"
)
-func init() {
- pctx.HostBinToolVariable("protocCmd", "aprotoc")
-}
+func genProto(ctx android.ModuleContext, protoFile android.Path, flags android.ProtoFlags, pkgPath string) android.Path {
+ srcsZipFile := android.PathForModuleGen(ctx, protoFile.Base()+".srcszip")
-var (
- proto = pctx.AndroidStaticRule("protoc",
- blueprint.RuleParams{
- Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
- `$protocCmd --python_out=$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
- `$parCmd -o $out $pkgPathArgs -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
- CommandDeps: []string{
- "$protocCmd",
- "$parCmd",
- },
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- }, "protoBase", "protoFlags", "pkgPathArgs")
-)
+ outDir := srcsZipFile.ReplaceExtension(ctx, "tmp")
+ depFile := srcsZipFile.ReplaceExtension(ctx, "srcszip.d")
-func genProto(ctx android.ModuleContext, p *android.ProtoProperties,
- protoFile android.Path, protoFlags []string, pkgPath string) android.Path {
- srcJarFile := android.PathForModuleGen(ctx, protoFile.Base()+".srcszip")
+ rule := android.NewRuleBuilder()
- protoRoot := android.ProtoCanonicalPathFromRoot(ctx, p)
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+ rule.Command().Text("mkdir -p").Flag(outDir.String())
- var protoBase string
- if protoRoot {
- protoBase = "."
- } else {
- protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
- }
+ android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
- var pkgPathArgs string
+ // Proto generated python files have an unknown package name in the path, so package the entire output directory
+ // into a srcszip.
+ zipCmd := rule.Command().
+ Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ FlagWithOutput("-o ", srcsZipFile).
+ FlagWithArg("-C ", outDir.String()).
+ FlagWithArg("-D ", outDir.String())
if pkgPath != "" {
- pkgPathArgs = "-P " + pkgPath
+ zipCmd.FlagWithArg("-P ", pkgPath)
}
- ctx.Build(pctx, android.BuildParams{
- Rule: proto,
- Description: "protoc " + protoFile.Rel(),
- Output: srcJarFile,
- Input: protoFile,
- Args: map[string]string{
- "protoBase": protoBase,
- "protoFlags": strings.Join(protoFlags, " "),
- "pkgPathArgs": pkgPathArgs,
- },
- })
- return srcJarFile
+ rule.Command().Text("rm -rf").Flag(outDir.String())
+
+ rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+
+ return srcsZipFile
}
diff --git a/python/python.go b/python/python.go
index 6eb9b6e..ad08909 100644
--- a/python/python.go
+++ b/python/python.go
@@ -288,6 +288,8 @@
}
func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
+ android.ProtoDeps(ctx, &p.protoProperties)
+
if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
}
@@ -516,9 +518,11 @@
}
var zips android.Paths
if len(protoSrcs) > 0 {
+ protoFlags := android.GetProtoFlags(ctx, &p.protoProperties)
+ protoFlags.OutTypeFlag = "--python_out"
+
for _, srcFile := range protoSrcs {
- zip := genProto(ctx, &p.protoProperties, srcFile,
- android.ProtoFlags(ctx, &p.protoProperties), pkgPath)
+ zip := genProto(ctx, srcFile, protoFlags, pkgPath)
zips = append(zips, zip)
}
}
diff --git a/scripts/setup_go_workspace_for_soong.sh b/scripts/setup_go_workspace_for_soong.sh
index e2fb9fa..6374aae 100755
--- a/scripts/setup_go_workspace_for_soong.sh
+++ b/scripts/setup_go_workspace_for_soong.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
-# Copyright 2017 Google Inc. All rights reserved.
+# 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.
@@ -15,23 +15,174 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-#mounts the components of soong into a directory structure that Go tools and editors expect
+# Mounts the components of soong into a directory structure that Go tools
+# and editors expect.
-#move to the script's directory
-cd "$(dirname $0)"
-SCRIPT_PATH="$PWD"
-#find the root of the Repo checkout
-cd "${SCRIPT_PATH}"/../../..
-ANDROID_PATH="${PWD}"
-OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')" #if GOPATH contains multiple paths, use the first one
-
-if [ -z "${OUTPUT_PATH}" ]; then
- echo "Error; could not determine the desired location at which to create a Go-compatible workspace. Please update GOPATH to specify the desired destination directory"
+#####################################################################
+# Print the message to stderr with the prefix ERROR and abort this
+# script.
+#####################################################################
+function log_FATAL() {
+ echo "ERROR:" "$*" >&2
exit 1
-fi
+}
-function confirm() {
+#####################################################################
+# Print the message to stderr with the prefix WARN
+#####################################################################
+function log_WARN() {
+ echo "WARN:" "$*" >&2
+}
+
+
+#####################################################################
+# Print the message with the prefix INFO.
+#####################################################################
+function log_INFO() {
+ echo "INFO:" "$*"
+}
+
+
+#####################################################################
+# Find the root project directory of this repo. This is done by
+# finding the directory of where this script lives and then go up one
+# directory to check the ".repo" directory exist. If not, keep going
+# up until we find the ".repo" file or we reached to the filesystem
+# root. Project root directory is printed to stdout.
+#####################################################################
+function root_dir() (
+ local dir
+ if ! dir="$("${readlink}" -e $(dirname "$0"))"; then
+ log_FATAL "failed to read the script's current directory."
+ fi
+
+ dir=${dir}/../../..
+ if ! dir="$("${readlink}" -e "${dir}")"; then
+ log_FATAL "Cannot find the root project directory"
+ fi
+
+ echo "${dir}"
+)
+
+
+#####################################################################
+# executes a shell command by printing out to the screen first and
+# then evaluating the command.
+#####################################################################
+function execute() {
+ echo "$@"
+ eval "$@"
+}
+
+
+#####################################################################
+# Returns the source directory of a passed in path from BIND_PATHS
+# array.
+#####################################################################
+function bind_path_src_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/%|*/}"
+)
+
+
+#####################################################################
+# Returns the destination directory of a passed in path from
+# BIND_PATHS array.
+#####################################################################
+function bind_path_dst_dir() (
+ local -r bind_path="$1"
+ echo "${bind_path/#*|}"
+)
+
+
+#####################################################################
+# Executes the bindfs command in linux. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function linux_bind_dir() (
+ execute bindfs "$1" "$2"
+)
+
+#####################################################################
+# Executes the fusermount -u command in linux. Expects $1 to be the
+# destination directory.
+#####################################################################
+function linux_unbind_dir() (
+ execute fusermount -u "$1"
+)
+
+#####################################################################
+# Executes the bindfs command in darwin. Expects $1 to be src
+# directory and $2 to be destination directory.
+#####################################################################
+function darwin_bind_dir() (
+ execute bindfs -o allow_recursion -n "$1" "$2"
+)
+
+
+#####################################################################
+# Execute the umount command in darwin to unbind a directory. Expects
+# $1 to be the destination directory
+#####################################################################
+function darwin_unbind_dir() (
+ execute umount -f "$1"
+)
+
+
+#####################################################################
+# Bind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function bind_all() (
+ local src_dir
+ local dst_dir
+
+ for path in ${BIND_PATHS[@]}; do
+ src_dir=$(bind_path_src_dir "${path}")
+
+ dst_dir=$(bind_path_dst_dir "${path}")
+ mkdir -p "${dst_dir}"
+
+ "${bind_dir}" ${src_dir} "${dst_dir}"
+ done
+
+ echo
+ log_INFO "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Unbind all the paths that are specified in the BIND_PATHS array.
+#####################################################################
+function unbind_all() (
+ local dst_dir
+ local exit_code=0
+
+ # need to go into reverse since several parent directory may have been
+ # first before the child one.
+ for (( i=${#BIND_PATHS[@]}-1; i>=0; i-- )); do
+ dst_dir=$(bind_path_dst_dir "${BIND_PATHS[$i]}")
+
+ # continue to unmount even one of them fails
+ if ! "${unbind_dir}" "${dst_dir}"; then
+ log_WARN "Failed to umount ${dst_dir}."
+ exit_code=1
+ fi
+ done
+
+ if [[ ${exit_code} -ne 0 ]]; then
+ exit ${exit_code}
+ fi
+
+ echo
+ log_INFO "Unmounted the GOPATH-compatible directory structure at ${OUTPUT_PATH}."
+)
+
+
+#####################################################################
+# Asks the user to create the GOPATH-compatible directory structure.
+#####################################################################
+function confirm() (
while true; do
echo "Will create GOPATH-compatible directory structure at ${OUTPUT_PATH}"
echo -n "Ok [Y/n]?"
@@ -42,48 +193,162 @@
if [ "${decision}" == "n" ]; then
return 1
else
- echo "Invalid choice ${decision}; choose either 'y' or 'n'"
+ log_WARN "Invalid choice ${decision}; choose either 'y' or 'n'"
fi
fi
done
+)
+
+
+#####################################################################
+# Help function.
+#####################################################################
+function help() (
+ cat <<EOF
+Mounts the components of soong into a directory structure that Go tools
+and editors expect.
+
+ --help
+ This help
+
+ --bind
+ Create the directory structure that Go tools and editors expect by
+ binding the one to aosp build directory.
+
+ --unbind
+ Reverse operation of bind.
+
+If no flags were specified, the --bind one is selected by default.
+EOF
+)
+
+
+#####################################################################
+# Parse the arguments passed in to this script.
+#####################################################################
+function parse_arguments() {
+ while [[ -n "$1" ]]; do
+ case "$1" in
+ --bind)
+ ACTION="bind"
+ shift
+ ;;
+ --unbind)
+ ACTION="unbind"
+ shift
+ ;;
+ --help )
+ help
+ shift
+ exit 0
+ ;;
+ *)
+ log_WARN "Unknown option: $1"
+ help
+ exit 1
+ ;;
+ esac
+ done
+
+ if [[ -z "${ACTION}" ]]; then
+ ACTION=bind
+ fi
}
-function bindAll() {
- bindOne "${ANDROID_PATH}/build/blueprint" "${OUTPUT_PATH}/src/github.com/google/blueprint"
- bindOne "${ANDROID_PATH}/build/soong" "${OUTPUT_PATH}/src/android/soong"
- bindOne "${ANDROID_PATH}/art/build" "${OUTPUT_PATH}/src/android/soong/art"
- bindOne "${ANDROID_PATH}/external/golang-protobuf" "${OUTPUT_PATH}/src/github.com/golang/protobuf"
- bindOne "${ANDROID_PATH}/external/llvm/soong" "${OUTPUT_PATH}/src/android/soong/llvm"
- bindOne "${ANDROID_PATH}/external/clang/soong" "${OUTPUT_PATH}/src/android/soong/clang"
- echo
- echo "Created GOPATH-compatible directory structure at ${OUTPUT_PATH}"
-}
+#####################################################################
+# Verifies that a list of required binaries are installed in the
+# host in order to run this script.
+#####################################################################
+function check_exec_existence() (
+ function check() {
+ if ! hash "$1" &>/dev/null; then
+ log_FATAL "missing $1"
+ fi
+ }
-function bindOne() {
- #causes $newPath to mirror $existingPath
- existingPath="$1"
- newPath="$2"
- mkdir -p "$newPath"
- case $(uname -s) in
+ local bins
+ case "${os_type}" in
Darwin)
- echoAndDo bindfs -o allow_recursion -n "${existingPath}" "${newPath}"
+ bins=("bindfs" "greadlink")
;;
Linux)
- echoAndDo bindfs "${existingPath}" "${newPath}"
+ bins=("bindfs" "fusermount")
;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
esac
+
+ for bin in "${bins[@]}"; do
+ check "${bin}"
+ done
+)
+
+
+function main() {
+ parse_arguments "$@"
+
+ check_exec_existence
+
+ if [[ "${ACTION}" == "bind" ]]; then
+ if confirm; then
+ echo
+ bind_all
+ else
+ echo "skipping due to user request"
+ exit 1
+ fi
+ else
+ echo
+ unbind_all
+ fi
}
-function echoAndDo() {
- echo "$@"
- eval "$@"
-}
+readonly os_type="$(uname -s)"
+case "${os_type}" in
+ Darwin)
+ bind_dir=darwin_bind_dir
+ unbind_dir=darwin_unbind_dir
+ readlink=greadlink
+ ;;
+ Linux)
+ bind_dir=linux_bind_dir
+ unbind_dir=linux_unbind_dir
+ readlink=readlink
+ ;;
+ *)
+ log_FATAL "${os_type} is not a recognized system."
+esac
+readonly bind_dir
+readonly unbind_dir
+readonly readlink
-if confirm; then
- echo
- bindAll
-else
- echo "skipping due to user request"
- exit 1
+
+if ! ANDROID_PATH="$(root_dir)"; then
+ log_FATAL "failed to find the root of the repo checkout"
fi
+readonly ANDROID_PATH
+
+#if GOPATH contains multiple paths, use the first one
+if ! OUTPUT_PATH="$(echo ${GOPATH} | sed 's/\:.*//')"; then
+ log_FATAL "failed to extract the first GOPATH environment variable"
+fi
+readonly OUTPUT_PATH
+if [ -z "${OUTPUT_PATH}" ]; then
+ log_FATAL "Could not determine the desired location at which to create a" \
+ "Go-compatible workspace. Please update GOPATH to specify the" \
+ "desired destination directory."
+fi
+
+# Below are the paths to bind from src to dst. The paths are separated by |
+# where the left side is the source and the right side is destination.
+readonly BIND_PATHS=(
+ "${ANDROID_PATH}/build/blueprint|${OUTPUT_PATH}/src/github.com/google/blueprint"
+ "${ANDROID_PATH}/build/soong|${OUTPUT_PATH}/src/android/soong"
+ "${ANDROID_PATH}/art/build|${OUTPUT_PATH}/src/android/soong/art"
+ "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/github.com/golang/protobuf"
+ "${ANDROID_PATH}/external/llvm/soong|${OUTPUT_PATH}/src/android/soong/llvm"
+ "${ANDROID_PATH}/external/clang/soong|${OUTPUT_PATH}/src/android/soong/clang"
+)
+
+main "$@"
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 79b0f4e..a7aff59 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -73,6 +73,7 @@
})
ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
+ ctx.RegisterModuleType("cc_library_headers", android.ModuleFactoryAdaptor(cc.LibraryHeaderFactory))
ctx.RegisterModuleType("cc_library_static", android.ModuleFactoryAdaptor(cc.LibraryFactory))
ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
@@ -271,6 +272,25 @@
soc_specific: true,
static_libs: ["sysprop-platform", "sysprop-vendor"],
}
+
+ cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ recovery_available: true,
+ }
+
+ cc_library {
+ name: "liblog",
+ no_libgcc: true,
+ nocrt: true,
+ system_shared_libs: [],
+ recovery_available: true,
+ }
+
+ llndk_library {
+ name: "liblog",
+ symbol_file: "",
+ }
`)
for _, variant := range []string{