Merge "Moving $OUT/vendor-ramdisk-debug.cpio.lz4 to intermediate dir"
diff --git a/Android.bp b/Android.bp
index 866ed25..1012dba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -222,3 +222,8 @@
srcs: [":linker"],
out: ["linker.flags"],
}
+
+// Instantiate the dex_bootjars singleton module.
+dex_bootjars {
+ name: "dex_bootjars",
+}
diff --git a/README.md b/README.md
index f1857f8..b7e93f4 100644
--- a/README.md
+++ b/README.md
@@ -430,14 +430,24 @@
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b"],
+ values: ["soc_a", "soc_b", "soc_c"],
}
```
This example describes a new `acme_cc_defaults` module type that extends the
`cc_defaults` module type, with three additional conditionals based on
variables `board`, `feature` and `width`, which can affect properties `cflags`
-and `srcs`.
+and `srcs`. Additionally, each conditional will contain a `conditions_default`
+property can affect `cflags` and `srcs` in the following conditions:
+
+* bool variable (e.g. `feature`): the variable is unspecified or not set to a true value
+* value variable (e.g. `width`): the variable is unspecified
+* string variable (e.g. `board`): the variable is unspecified or the variable is set to a string unused in the
+given module. For example, with `board`, if the `board`
+conditional contains the properties `soc_a` and `conditions_default`, when
+board=soc_b, the `cflags` and `srcs` values under `conditions_default` will be
+used. To specify that no properties should be amended for `soc_b`, you can set
+`soc_b: {},`.
The values of the variables can be set from a product's `BoardConfig.mk` file:
```
@@ -445,6 +455,7 @@
SOONG_CONFIG_acme += \
board \
feature \
+ width \
SOONG_CONFIG_acme_board := soc_a
SOONG_CONFIG_acme_feature := true
@@ -473,12 +484,21 @@
soc_b: {
cflags: ["-DSOC_B"],
},
+ conditions_default: {
+ cflags: ["-DSOC_DEFAULT"],
+ },
},
feature: {
cflags: ["-DFEATURE"],
+ conditions_default: {
+ cflags: ["-DFEATURE_DEFAULT"],
+ },
},
width: {
cflags: ["-DWIDTH=%s"],
+ conditions_default: {
+ cflags: ["-DWIDTH=DEFAULT"],
+ },
},
},
}
@@ -490,8 +510,37 @@
}
```
-With the `BoardConfig.mk` snippet above, libacme_foo would build with
-cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+With the `BoardConfig.mk` snippet above, `libacme_foo` would build with
+`cflags: "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+ board \
+ feature \
+ width \
+
+SOONG_CONFIG_acme_feature := false
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
+
+Alternatively, with `DefaultBoardConfig.mk`:
+
+```
+SOONG_CONFIG_NAMESPACES += acme
+SOONG_CONFIG_acme += \
+ board \
+ feature \
+ width \
+
+SOONG_CONFIG_acme_board := soc_c
+```
+
+then `libacme_foo` would build with `cflags: "-DGENERIC -DSOC_DEFAULT
+-DFEATURE_DEFAULT -DSIZE=DEFAULT"`.
`soong_config_module_type` modules will work best when used to wrap defaults
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
diff --git a/android/Android.bp b/android/Android.bp
index efa70a9..eabb137 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -21,6 +21,7 @@
"bazel_handler.go",
"config.go",
"csuite_config.go",
+ "deapexer.go",
"defaults.go",
"defs.go",
"depset_generic.go",
diff --git a/android/apex.go b/android/apex.go
index 47d14cc..31c62e9 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -64,6 +64,14 @@
// module is part of. The ApexContents gives information about which modules the apexBundle
// has and whether a module became part of the apexBundle via a direct dependency or not.
ApexContents []*ApexContents
+
+ // True if this is for a prebuilt_apex.
+ //
+ // If true then this will customize the apex processing to make it suitable for handling
+ // prebuilt_apex, e.g. it will prevent ApexInfos from being merged together.
+ //
+ // See Prebuilt.ApexInfoMutator for more information.
+ ForPrebuiltApex bool
}
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
@@ -412,6 +420,16 @@
sort.Sort(byApexName(apexInfos))
seen := make(map[string]int)
for _, apexInfo := range apexInfos {
+ // If this is for a prebuilt apex then use the actual name of the apex variation to prevent this
+ // from being merged with other ApexInfo. See Prebuilt.ApexInfoMutator for more information.
+ if apexInfo.ForPrebuiltApex {
+ merged = append(merged, apexInfo)
+ continue
+ }
+
+ // Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from
+ // this one into it, otherwise create a new merged ApexInfo from this one and save it away so
+ // other ApexInfo instances can be merged into it.
apexName := apexInfo.ApexVariationName
mergedName := apexInfo.mergedName(ctx)
if index, exists := seen[mergedName]; exists {
@@ -582,10 +600,14 @@
// apexContents, and modules in that apex have a provider containing the apexContents of each
// apexBundle they are part of.
type ApexContents struct {
- // map from a module name to its membership to this apexBUndle
+ // map from a module name to its membership in this apexBundle
contents map[string]ApexMembership
}
+// NewApexContents creates and initializes an ApexContents that is suitable
+// for use with an apex module.
+// * contents is a map from a module name to information about its membership within
+// the apex.
func NewApexContents(contents map[string]ApexMembership) *ApexContents {
return &ApexContents{
contents: contents,
diff --git a/android/apex_test.go b/android/apex_test.go
index 512b50f..1f786e6 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -20,6 +20,10 @@
)
func Test_mergeApexVariations(t *testing.T) {
+ const (
+ ForPrebuiltApex = true
+ NotForPrebuiltApex = false
+ )
tests := []struct {
name string
in []ApexInfo
@@ -29,10 +33,10 @@
{
name: "single",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
+ {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", false, nil, []string{"foo"}, nil},
+ {"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"foo", "apex10000"},
@@ -41,11 +45,11 @@
{
name: "merge",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
- {"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil},
+ {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil}},
+ {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}},
wantAliases: [][2]string{
{"bar", "apex10000_baz_1"},
{"foo", "apex10000_baz_1"},
@@ -54,12 +58,12 @@
{
name: "don't merge version",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
- {"bar", "30", false, nil, []string{"bar"}, nil},
+ {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex30", "30", false, nil, []string{"bar"}, nil},
- {"apex10000", "current", false, nil, []string{"foo"}, nil},
+ {"apex30", "30", false, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex30"},
@@ -69,11 +73,11 @@
{
name: "merge updatable",
in: []ApexInfo{
- {"foo", "current", false, nil, []string{"foo"}, nil},
- {"bar", "current", true, nil, []string{"bar"}, nil},
+ {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000", "current", true, nil, []string{"bar", "foo"}, nil},
+ {"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000"},
@@ -83,19 +87,38 @@
{
name: "don't merge sdks",
in: []ApexInfo{
- {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
- {"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
+ {"foo", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
},
wantMerged: []ApexInfo{
- {"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil},
- {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil},
+ {"apex10000_baz_2", "current", false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex},
+ {"apex10000_baz_1", "current", false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex},
},
wantAliases: [][2]string{
{"bar", "apex10000_baz_2"},
{"foo", "apex10000_baz_1"},
},
},
+ {
+ name: "don't merge when for prebuilt_apex",
+ in: []ApexInfo{
+ {"foo", "current", false, nil, []string{"foo"}, nil, NotForPrebuiltApex},
+ {"bar", "current", true, nil, []string{"bar"}, nil, NotForPrebuiltApex},
+ // This one should not be merged in with the others because it is for
+ // a prebuilt_apex.
+ {"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ },
+ wantMerged: []ApexInfo{
+ {"apex10000", "current", true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+ {"baz", "current", true, nil, []string{"baz"}, nil, ForPrebuiltApex},
+ },
+ wantAliases: [][2]string{
+ {"bar", "apex10000"},
+ {"foo", "apex10000"},
+ },
+ },
}
+
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config := TestConfig(buildDir, nil, "", nil)
diff --git a/android/arch.go b/android/arch.go
index 34f9b29..baee9be 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -617,11 +617,15 @@
}
// Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
- // This is used for Windows support and for HOST_PREFER_32_BIT=true support for Art modules.
+ // This is used for HOST_PREFER_32_BIT=true support for Art modules.
prefer32 := false
if base.prefer32 != nil {
prefer32 = base.prefer32(mctx, base, os)
}
+ if os == Windows {
+ // Windows builds always prefer 32-bit
+ prefer32 = true
+ }
// Determine the multilib selection for this module.
multilib, extraMultilib := decodeMultilib(base, os.Class)
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index a00a54d..4a25119 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -371,7 +371,11 @@
var cqueryOutput string
var err error
- err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+ intermediatesDirPath := absolutePath(context.intermediatesDir())
+ if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
+ err = os.Mkdir(intermediatesDirPath, 0777)
+ }
+
if err != nil {
return err
}
@@ -441,7 +445,10 @@
return err
}
- context.buildStatements = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+ if err != nil {
+ return err
+ }
// Issue a build command of the phony root to generate symlink forests for dependencies of the
// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
diff --git a/android/config.go b/android/config.go
index a7e0b67..8090889 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1026,6 +1026,10 @@
return c.multilibConflicts[arch]
}
+func (c *config) PrebuiltHiddenApiDir(ctx PathContext) string {
+ return String(c.productVariables.PrebuiltHiddenApiDir)
+}
+
func (c *deviceConfig) Arches() []Arch {
var arches []Arch
for _, target := range c.config.Targets[Android] {
@@ -1401,6 +1405,14 @@
return c.config.productVariables.BoardReqdMaskPolicy
}
+func (c *deviceConfig) DirectedVendorSnapshot() bool {
+ return c.config.productVariables.DirectedVendorSnapshot
+}
+
+func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
+ return c.config.productVariables.VendorSnapshotModules
+}
+
// The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
// Such lists are used in the build system for things like bootclasspath jars or system server jars.
// The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/deapexer.go b/android/deapexer.go
new file mode 100644
index 0000000..63508d7
--- /dev/null
+++ b/android/deapexer.go
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
+// will delegate the work to export files from a prebuilt '.apex` file.
+
+// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
+type DeapexerInfo struct {
+ // map from the name of an exported file from a prebuilt_apex to the path to that file. The
+ // exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
+ // ".dexjar".
+ //
+ // See Prebuilt.ApexInfoMutator for more information.
+ exports map[string]Path
+}
+
+// The set of supported prebuilt export tags. Used to verify the tag parameter for
+// `PrebuiltExportPath`.
+var supportedPrebuiltExportTags = map[string]struct{}{
+ ".dexjar": {},
+}
+
+// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
+// prebuilt_apex that created this ApexInfo.
+//
+// The exported file is identified by the module name and the tag:
+// * The module name is the name of the module that contributed the file when the .apex file
+// referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
+// properties on the prebuilt_apex module.
+// * The tag identifies the type of file and is dependent on the module type.
+//
+// See apex/deapexer.go for more information.
+func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
+
+ if _, ok := supportedPrebuiltExportTags[tag]; !ok {
+ panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
+ tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
+ }
+
+ path := i.exports[name+"{"+tag+"}"]
+ return path
+}
+
+// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
+// on a `deapexer` module to retrieve its `DeapexerInfo`.
+var DeapexerProvider = blueprint.NewProvider(DeapexerInfo{})
+
+// NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
+// for use with a prebuilt_apex module.
+//
+// See apex/deapexer.go for more information.
+func NewDeapexerInfo(exports map[string]Path) DeapexerInfo {
+ return DeapexerInfo{
+ exports: exports,
+ }
+}
+
+type deapexerTagStruct struct {
+ blueprint.BaseDependencyTag
+}
+
+// A tag that is used for dependencies on the `deapexer` module.
+var DeapexerTag = deapexerTagStruct{}
diff --git a/android/filegroup.go b/android/filegroup.go
index 9425616..fd4a2fe 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -17,10 +17,50 @@
import (
"android/soong/bazel"
"strings"
+
+ "github.com/google/blueprint/proptools"
)
func init() {
RegisterModuleType("filegroup", FileGroupFactory)
+ RegisterBp2BuildMutator("filegroup", bp2buildMutator)
+}
+
+// https://docs.bazel.build/versions/master/be/general.html#filegroup
+type bazelFilegroupAttributes struct {
+ Name *string
+ Srcs []string
+}
+
+type bazelFilegroup struct {
+ BazelTargetModuleBase
+ bazelFilegroupAttributes
+}
+
+func BazelFileGroupFactory() Module {
+ module := &bazelFilegroup{}
+ module.AddProperties(&module.bazelFilegroupAttributes)
+ InitBazelTargetModule(module)
+ return module
+}
+
+func (bfg *bazelFilegroup) Name() string {
+ return bfg.BaseModuleName()
+}
+
+func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+// TODO: Create helper functions to avoid this boilerplate.
+func bp2buildMutator(ctx TopDownMutatorContext) {
+ if m, ok := ctx.Module().(*fileGroup); ok {
+ name := "__bp2build__" + m.base().BaseModuleName()
+ ctx.CreateModule(BazelFileGroupFactory, &bazelFilegroupAttributes{
+ Name: proptools.StringPtr(name),
+ Srcs: m.properties.Srcs,
+ }, &bazel.BazelTargetModuleProperties{
+ Rule_class: "filegroup",
+ })
+ }
}
type fileGroupProperties struct {
diff --git a/android/module.go b/android/module.go
index 17035bb..68008c2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"fmt"
"os"
"path"
@@ -491,6 +492,26 @@
TransitivePackagingSpecs() []PackagingSpec
}
+type BazelTargetModule interface {
+ Module
+
+ BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties
+}
+
+func InitBazelTargetModule(module BazelTargetModule) {
+ module.AddProperties(module.BazelTargetModuleProperties())
+ InitAndroidModule(module)
+}
+
+type BazelTargetModuleBase struct {
+ ModuleBase
+ Properties bazel.BazelTargetModuleProperties
+}
+
+func (btmb *BazelTargetModuleBase) BazelTargetModuleProperties() *bazel.BazelTargetModuleProperties {
+ return &btmb.Properties
+}
+
// Qualified id for a module
type qualifiedModuleName struct {
// The package (i.e. directory) in which the module is defined, without trailing /
@@ -1069,6 +1090,9 @@
archProperties [][]interface{}
customizableProperties []interface{}
+ // Properties specific to the Blueprint to BUILD migration.
+ bazelTargetModuleProperties bazel.BazelTargetModuleProperties
+
// Information about all the properties on the module that contains visibility rules that need
// checking.
visibilityPropertyInfo []visibilityProperty
@@ -2434,10 +2458,6 @@
m.commonProperties.System_ext_specific = boolPtr(false)
}
-func (m *ModuleBase) EnableNativeBridgeSupportByDefault() {
- m.commonProperties.Native_bridge_supported = boolPtr(true)
-}
-
func (m *ModuleBase) MakeAsSystemExt() {
m.commonProperties.Vendor = boolPtr(false)
m.commonProperties.Proprietary = boolPtr(false)
diff --git a/android/mutator.go b/android/mutator.go
index 72c68b2..2a2be6c 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -44,9 +44,16 @@
}
}
-func registerMutatorsForBazelConversion(ctx *blueprint.Context) {
- // FIXME(b/171263886): Start bringing in mutators to make the Bionic
- // module subgraph suitable for automated conversion.
+// RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
+func RegisterMutatorsForBazelConversion(ctx *blueprint.Context, bp2buildMutators []RegisterMutatorFunc) {
+ mctx := ®isterMutatorsContext{}
+
+ // Register bp2build mutators
+ for _, f := range bp2buildMutators {
+ f(mctx)
+ }
+
+ registerMutatorsToContext(ctx, mctx.mutators)
}
func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
@@ -196,6 +203,21 @@
finalDeps = append(finalDeps, f)
}
+var bp2buildMutators = []RegisterMutatorFunc{}
+
+// RegisterBp2BuildMutator registers specially crafted mutators for
+// converting Blueprint/Android modules into special modules that can
+// be code-generated into Bazel BUILD targets.
+//
+// TODO(b/178068862): bring this into TestContext.
+func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+ mutatorName := moduleType + "_bp2build"
+ f := func(ctx RegisterMutatorsContext) {
+ ctx.TopDown(mutatorName, m)
+ }
+ bp2buildMutators = append(bp2buildMutators, f)
+}
+
type BaseMutatorContext interface {
BaseModuleContext
diff --git a/android/neverallow.go b/android/neverallow.go
index 031b3f4..7455e6a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -51,7 +51,6 @@
func init() {
AddNeverAllowRules(createIncludeDirsRules()...)
AddNeverAllowRules(createTrebleRules()...)
- AddNeverAllowRules(createMediaRules()...)
AddNeverAllowRules(createJavaDeviceForHostRules()...)
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
@@ -132,14 +131,6 @@
}
}
-func createMediaRules() []Rule {
- return []Rule{
- NeverAllow().
- With("libs", "updatable-media").
- Because("updatable-media includes private APIs. Use updatable_media_stubs instead."),
- }
-}
-
func createJavaDeviceForHostRules() []Rule {
javaDeviceForHostProjectsAllowedList := []string{
"external/guava",
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 8c7a538..b761065 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -190,19 +190,6 @@
},
},
{
- name: "dependency on updatable-media",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "needs_updatable_media",
- libs: ["updatable-media"],
- }`),
- },
- expectedErrors: []string{
- "updatable-media includes private APIs. Use updatable_media_stubs instead.",
- },
- },
- {
name: "java_device_for_host",
fs: map[string][]byte{
"Android.bp": []byte(`
diff --git a/android/register.go b/android/register.go
index ca658f5..02fc97e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -37,9 +37,6 @@
var singletons []singleton
var preSingletons []singleton
-var bazelConverterSingletons []singleton
-var bazelConverterPreSingletons []singleton
-
type mutator struct {
name string
bottomUpMutator blueprint.BottomUpMutator
@@ -94,14 +91,6 @@
preSingletons = append(preSingletons, singleton{name, factory})
}
-func RegisterBazelConverterSingletonType(name string, factory SingletonFactory) {
- bazelConverterSingletons = append(bazelConverterSingletons, singleton{name, factory})
-}
-
-func RegisterBazelConverterPreSingletonType(name string, factory SingletonFactory) {
- bazelConverterPreSingletons = append(bazelConverterPreSingletons, singleton{name, factory})
-}
-
type Context struct {
*blueprint.Context
config Config
@@ -117,21 +106,20 @@
// singletons, module types and mutators to register for converting Blueprint
// files to semantically equivalent BUILD files.
func (ctx *Context) RegisterForBazelConversion() {
- for _, t := range bazelConverterPreSingletons {
- ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
- }
-
for _, t := range moduleTypes {
ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
}
- for _, t := range bazelConverterSingletons {
+ // Required for SingletonModule types, even though we are not using them.
+ for _, t := range singletons {
ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
- registerMutatorsForBazelConversion(ctx.Context)
+ RegisterMutatorsForBazelConversion(ctx.Context, bp2buildMutators)
}
+// Register the pipeline of singletons, module types, and mutators for
+// generating build.ninja and other files for Kati, from Android.bp files.
func (ctx *Context) Register() {
for _, t := range preSingletons {
ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 619cf86..289e910 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -51,6 +51,16 @@
// variables from another Android.bp file. The imported module type will exist for all
// modules after the import in the Android.bp file.
//
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+// bool variable: the variable is unspecified or not set to a true value
+// value variable: the variable is unspecified
+// string variable: the variable is unspecified or the variable is set to a string unused in the
+// given module. For example, string variable `test` takes values: "a" and "b",
+// if the module contains a property `a` and `conditions_default`, when test=b,
+// the properties under `conditions_default` will be used. To specify that no
+// properties should be amended for `b`, you can set `b: {},`.
+//
// For example, an Android.bp file could have:
//
// soong_config_module_type_import {
@@ -69,12 +79,21 @@
// soc_b: {
// cflags: ["-DSOC_B"],
// },
+// conditions_default: {
+// cflags: ["-DSOC_DEFAULT"],
+// },
// },
// feature: {
// cflags: ["-DFEATURE"],
+// conditions_default: {
+// cflags: ["-DFEATURE_DEFAULT"],
+// },
// },
// width: {
// cflags: ["-DWIDTH=%s"],
+// conditions_default: {
+// cflags: ["-DWIDTH=DEFAULT"],
+// },
// },
// },
// }
@@ -99,7 +118,7 @@
//
// soong_config_string_variable {
// name: "board",
-// values: ["soc_a", "soc_b"],
+// values: ["soc_a", "soc_b", "soc_c"],
// }
//
// If an acme BoardConfig.mk file contained:
@@ -114,6 +133,31 @@
// SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
+//
+// Alternatively, if acme BoardConfig.mk file contained:
+//
+// SOONG_CONFIG_NAMESPACES += acme
+// SOONG_CONFIG_acme += \
+// board \
+// feature \
+//
+// SOONG_CONFIG_acme_feature := false
+//
+// Then libacme_foo would build with cflags:
+// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+//
+// Similarly, if acme BoardConfig.mk file contained:
+//
+// SOONG_CONFIG_NAMESPACES += acme
+// SOONG_CONFIG_acme += \
+// board \
+// feature \
+//
+// SOONG_CONFIG_acme_board := soc_c
+//
+// Then libacme_foo would build with cflags:
+// "-DGENERIC -DSOC_DEFAULT -DFEATURE_DEFAULT -DSIZE=DEFAULT".
+
func soongConfigModuleTypeImportFactory() Module {
module := &soongConfigModuleTypeImport{}
@@ -148,6 +192,16 @@
// in an Android.bp file, and can be imported into other Android.bp files using
// soong_config_module_type_import.
//
+// Each soong_config_variable supports an additional value `conditions_default`. The properties
+// specified in `conditions_default` will only be used under the following conditions:
+// bool variable: the variable is unspecified or not set to a true value
+// value variable: the variable is unspecified
+// string variable: the variable is unspecified or the variable is set to a string unused in the
+// given module. For example, string variable `test` takes values: "a" and "b",
+// if the module contains a property `a` and `conditions_default`, when test=b,
+// the properties under `conditions_default` will be used. To specify that no
+// properties should be amended for `b`, you can set `b: {},`.
+//
// For example, an Android.bp file could have:
//
// soong_config_module_type {
@@ -176,12 +230,21 @@
// soc_b: {
// cflags: ["-DSOC_B"],
// },
+// conditions_default: {
+// cflags: ["-DSOC_DEFAULT"],
+// },
// },
// feature: {
// cflags: ["-DFEATURE"],
+// conditions_default: {
+// cflags: ["-DFEATURE_DEFAULT"],
+// },
// },
// width: {
// cflags: ["-DWIDTH=%s"],
+// conditions_default: {
+// cflags: ["-DWIDTH=DEFAULT"],
+// },
// },
// },
// }
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index b1810b3..45463fd 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -60,15 +60,20 @@
name: "acme_test",
module_type: "test",
config_namespace: "acme",
- variables: ["board", "feature1", "FEATURE3"],
- bool_variables: ["feature2"],
- value_variables: ["size"],
+ variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+ bool_variables: ["feature2", "unused_feature"],
+ value_variables: ["size", "unused_size"],
properties: ["cflags", "srcs", "defaults"],
}
soong_config_string_variable {
name: "board",
- values: ["soc_a", "soc_b"],
+ values: ["soc_a", "soc_b", "soc_c", "soc_d"],
+ }
+
+ soong_config_string_variable {
+ name: "unused_string_var",
+ values: ["a", "b"],
}
soong_config_bool_variable {
@@ -105,15 +110,28 @@
soc_b: {
cflags: ["-DSOC_B"],
},
+ soc_c: {},
+ conditions_default: {
+ cflags: ["-DSOC_CONDITIONS_DEFAULT"],
+ },
},
size: {
cflags: ["-DSIZE=%s"],
+ conditions_default: {
+ cflags: ["-DSIZE=CONDITIONS_DEFAULT"],
+ },
},
feature1: {
+ conditions_default: {
+ cflags: ["-DF1_CONDITIONS_DEFAULT"],
+ },
cflags: ["-DFEATURE1"],
},
feature2: {
cflags: ["-DFEATURE2"],
+ conditions_default: {
+ cflags: ["-DF2_CONDITIONS_DEFAULT"],
+ },
},
FEATURE3: {
cflags: ["-DFEATURE3"],
@@ -145,6 +163,7 @@
cflags: ["-DSOC_B"],
defaults: ["foo_defaults_b"],
},
+ soc_c: {},
},
size: {
cflags: ["-DSIZE=%s"],
@@ -163,43 +182,120 @@
`
run := func(t *testing.T, bp string, fs map[string][]byte) {
- config := TestConfig(buildDir, nil, bp, fs)
+ testCases := []struct {
+ name string
+ config Config
+ fooExpectedFlags []string
+ fooDefaultsExpectedFlags []string
+ }{
+ {
+ name: "withValues",
+ config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+ "acme": map[string]string{
+ "board": "soc_a",
+ "size": "42",
+ "feature1": "true",
+ "feature2": "false",
+ // FEATURE3 unset
+ "unused_feature": "true", // unused
+ "unused_size": "1", // unused
+ "unused_string_var": "a", // unused
+ },
+ }),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=42",
+ "-DSOC_A",
+ "-DFEATURE1",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT_A",
+ "DEFAULT",
+ "-DGENERIC",
+ "-DSIZE=42",
+ "-DSOC_A",
+ "-DFEATURE1",
+ },
+ },
+ {
+ name: "empty_prop_for_string_var",
+ config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+ "acme": map[string]string{"board": "soc_c"}}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
+ },
+ {
+ name: "unused_string_var",
+ config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{
+ "acme": map[string]string{"board": "soc_d"}}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DSOC_CONDITIONS_DEFAULT", // foo does not contain a prop "soc_d", so we use the default
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
+ },
- config.TestProductVariables.VendorVars = map[string]map[string]string{
- "acme": map[string]string{
- "board": "soc_a",
- "size": "42",
- "feature1": "true",
- "feature2": "false",
- // FEATURE3 unset
+ {
+ name: "conditions_default",
+ config: testConfigWithVendorVars(buildDir, bp, fs, map[string]map[string]string{}),
+ fooExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ "-DF2_CONDITIONS_DEFAULT",
+ "-DSIZE=CONDITIONS_DEFAULT",
+ "-DSOC_CONDITIONS_DEFAULT",
+ "-DF1_CONDITIONS_DEFAULT",
+ },
+ fooDefaultsExpectedFlags: []string{
+ "DEFAULT",
+ "-DGENERIC",
+ },
},
}
- ctx := NewTestContext(config)
- ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
- ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
- ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
- ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
- ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
- ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
- ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.Register()
+ for _, tc := range testCases {
+ ctx := NewTestContext(tc.config)
+ ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+ ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+ ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+ ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+ ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+ ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+ ctx.Register()
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(tc.config)
+ FailIfErrored(t, errs)
- basicCFlags := []string{"DEFAULT", "-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}
+ foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
+ if g, w := foo.props.Cflags, tc.fooExpectedFlags; !reflect.DeepEqual(g, w) {
+ t.Errorf("%s: wanted foo cflags %q, got %q", tc.name, w, g)
+ }
- foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
- if g, w := foo.props.Cflags, basicCFlags; !reflect.DeepEqual(g, w) {
- t.Errorf("wanted foo cflags %q, got %q", w, g)
- }
-
- fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
- if g, w := fooDefaults.props.Cflags, append([]string{"DEFAULT_A"}, basicCFlags...); !reflect.DeepEqual(g, w) {
- t.Errorf("wanted foo_with_defaults cflags %q, got %q", w, g)
+ fooDefaults := ctx.ModuleForTests("foo_with_defaults", "").Module().(*soongConfigTestModule)
+ if g, w := fooDefaults.props.Cflags, tc.fooDefaultsExpectedFlags; !reflect.DeepEqual(g, w) {
+ t.Errorf("%s: wanted foo_with_defaults cflags %q, got %q", tc.name, w, g)
+ }
}
}
@@ -214,3 +310,11 @@
})
})
}
+
+func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
+ config := TestConfig(buildDir, nil, bp, fs)
+
+ config.TestProductVariables.VendorVars = vendorVars
+
+ return config
+}
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index df912e6..6bb68eb 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,4 +10,7 @@
"config.go",
"modules.go",
],
+ testSrcs: [
+ "modules_test.go",
+ ],
}
diff --git a/android/soongconfig/config.go b/android/soongconfig/config.go
index 39a776c..c72da2f 100644
--- a/android/soongconfig/config.go
+++ b/android/soongconfig/config.go
@@ -14,7 +14,10 @@
package soongconfig
-import "strings"
+import (
+ "fmt"
+ "strings"
+)
type SoongConfig interface {
// Bool interprets the variable named `name` as a boolean, returning true if, after
@@ -31,7 +34,16 @@
}
func Config(vars map[string]string) SoongConfig {
- return soongConfig(vars)
+ configVars := make(map[string]string)
+ if len(vars) > 0 {
+ for k, v := range vars {
+ configVars[k] = v
+ }
+ if _, exists := configVars[conditionsDefault]; exists {
+ panic(fmt.Sprintf("%q is a reserved soong config variable name", conditionsDefault))
+ }
+ }
+ return soongConfig(configVars)
}
type soongConfig map[string]string
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 9f3f804..c62e76d 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -26,6 +26,8 @@
"github.com/google/blueprint/proptools"
)
+const conditionsDefault = "conditions_default"
+
var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
@@ -145,32 +147,10 @@
return errs
}
- mt := &ModuleType{
- affectableProperties: props.Properties,
- ConfigNamespace: props.Config_namespace,
- BaseModuleType: props.Module_type,
- variableNames: props.Variables,
- }
- v.ModuleTypes[props.Name] = mt
-
- for _, name := range props.Bool_variables {
- if name == "" {
- return []error{fmt.Errorf("bool_variable name must not be blank")}
- }
-
- mt.Variables = append(mt.Variables, newBoolVariable(name))
- }
-
- for _, name := range props.Value_variables {
- if name == "" {
- return []error{fmt.Errorf("value_variables entry must not be blank")}
- }
-
- mt.Variables = append(mt.Variables, &valueVariable{
- baseVariable: baseVariable{
- variable: name,
- },
- })
+ if mt, errs := newModuleType(props); len(errs) > 0 {
+ return errs
+ } else {
+ v.ModuleTypes[props.Name] = mt
}
return nil
@@ -196,6 +176,12 @@
return []error{fmt.Errorf("values property must be set")}
}
+ for _, name := range stringProps.Values {
+ if err := checkVariableName(name); err != nil {
+ return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+ }
+ }
+
v.variables[base.variable] = &stringVariable{
baseVariable: base,
values: CanonicalizeToProperties(stringProps.Values),
@@ -417,8 +403,7 @@
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
// based on SoongConfig values.
// Expects that props contains a struct field with name soong_config_variables. The fields within
-// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
-// props should be generated via CreateProperties.
+// soong_config_variables are expected to be in the same order as moduleType.Variables.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
props = props.Elem().FieldByName(soongConfigProperty)
@@ -441,6 +426,46 @@
variableNames []string
}
+func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
+ mt := &ModuleType{
+ affectableProperties: props.Properties,
+ ConfigNamespace: props.Config_namespace,
+ BaseModuleType: props.Module_type,
+ variableNames: props.Variables,
+ }
+
+ for _, name := range props.Bool_variables {
+ if err := checkVariableName(name); err != nil {
+ return nil, []error{fmt.Errorf("bool_variables %s", err)}
+ }
+
+ mt.Variables = append(mt.Variables, newBoolVariable(name))
+ }
+
+ for _, name := range props.Value_variables {
+ if err := checkVariableName(name); err != nil {
+ return nil, []error{fmt.Errorf("value_variables %s", err)}
+ }
+
+ mt.Variables = append(mt.Variables, &valueVariable{
+ baseVariable: baseVariable{
+ variable: name,
+ },
+ })
+ }
+
+ return mt, nil
+}
+
+func checkVariableName(name string) error {
+ if name == "" {
+ return fmt.Errorf("name must not be blank")
+ } else if name == conditionsDefault {
+ return fmt.Errorf("%q is reserved", conditionsDefault)
+ }
+ return nil
+}
+
type soongConfigVariable interface {
// variableProperty returns the name of the variable.
variableProperty() string
@@ -474,7 +499,10 @@
func (s *stringVariable) variableValuesType() reflect.Type {
var fields []reflect.StructField
- for _, v := range s.values {
+ var values []string
+ values = append(values, s.values...)
+ values = append(values, conditionsDefault)
+ for _, v := range values {
fields = append(fields, reflect.StructField{
Name: proptools.FieldNameForProperty(v),
Type: emptyInterfaceType,
@@ -484,26 +512,36 @@
return reflect.StructOf(fields)
}
+// initializeProperties initializes properties to zero value of typ for supported values and a final
+// conditions default field.
func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
for i := range s.values {
v.Field(i).Set(reflect.Zero(typ))
}
+ v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
}
+// Extracts an interface from values containing the properties to apply based on config.
+// If config does not match a value with a non-nil property set, the default value will be returned.
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
for j, v := range s.values {
- if config.String(s.variable) == v {
- return values.Field(j).Interface(), nil
+ f := values.Field(j)
+ if config.String(s.variable) == v && !f.Elem().IsNil() {
+ return f.Interface(), nil
}
}
-
- return nil, nil
+ // if we have reached this point, we have checked all valid values of string and either:
+ // * the value was not set
+ // * the value was set but that value was not specified in the Android.bp file
+ return values.Field(len(s.values)).Interface(), nil
}
+// Struct to allow conditions set based on a boolean variable
type boolVariable struct {
baseVariable
}
+// newBoolVariable constructs a boolVariable with the given name
func newBoolVariable(name string) *boolVariable {
return &boolVariable{
baseVariable{
@@ -516,18 +554,82 @@
return emptyInterfaceType
}
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
- v.Set(reflect.Zero(typ))
+ initializePropertiesWithDefault(v, typ)
}
-func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
- if config.Bool(b.variable) {
- return values.Interface(), nil
+// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
+// in typ, with an additional field for defaults of type typ. This should be used to initialize
+// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
+// one variable and a default.
+func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
+ sTyp := typ.Elem()
+ var fields []reflect.StructField
+ for i := 0; i < sTyp.NumField(); i++ {
+ fields = append(fields, sTyp.Field(i))
}
+ // create conditions_default field
+ nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
+ fields = append(fields, reflect.StructField{
+ Name: nestedFieldName,
+ Type: typ,
+ })
+
+ newTyp := reflect.PtrTo(reflect.StructOf(fields))
+ v.Set(reflect.Zero(newTyp))
+}
+
+// conditionsDefaultField extracts the conditions_default field from v. This is always the final
+// field if initialized with initializePropertiesWithDefault.
+func conditionsDefaultField(v reflect.Value) reflect.Value {
+ return v.Field(v.NumField() - 1)
+}
+
+// removeDefault removes the conditions_default field from values while retaining values from all
+// other fields. This allows
+func removeDefault(values reflect.Value) reflect.Value {
+ v := values.Elem().Elem()
+ s := conditionsDefaultField(v)
+ // if conditions_default field was not set, there will be no issues extending properties.
+ if !s.IsValid() {
+ return v
+ }
+
+ // If conditions_default field was set, it has the correct type for our property. Create a new
+ // reflect.Value of the conditions_default type and copy all fields (except for
+ // conditions_default) based on values to the result.
+ res := reflect.New(s.Type().Elem())
+ for i := 0; i < res.Type().Elem().NumField(); i++ {
+ val := v.Field(i)
+ res.Elem().Field(i).Set(val)
+ }
+
+ return res
+}
+
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the value was not set, conditions_default interface will be returned; otherwise,
+// the interface in values, without conditions_default will be returned.
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+ // If this variable was not referenced in the module, there are no properties to apply.
+ if values.Elem().IsZero() {
+ return nil, nil
+ }
+ if config.Bool(b.variable) {
+ values = removeDefault(values)
+ return values.Interface(), nil
+ }
+ v := values.Elem().Elem()
+ if f := conditionsDefaultField(v); f.IsValid() {
+ return f.Interface(), nil
+ }
return nil, nil
}
+// Struct to allow conditions set based on a value variable, supporting string substitution.
type valueVariable struct {
baseVariable
}
@@ -536,17 +638,28 @@
return emptyInterfaceType
}
+// initializeProperties initializes a property to zero value of typ with an additional conditions
+// default field.
func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
- v.Set(reflect.Zero(typ))
+ initializePropertiesWithDefault(v, typ)
}
+// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
+// the module. If the variable was not set, conditions_default interface will be returned;
+// otherwise, the interface in values, without conditions_default will be returned with all
+// appropriate string substitutions based on variable being set.
func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
- if !config.IsSet(s.variable) || !values.IsValid() {
+ // If this variable was not referenced in the module, there are no properties to apply.
+ if !values.IsValid() || values.Elem().IsZero() {
return nil, nil
}
+ if !config.IsSet(s.variable) {
+ return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
+ }
configValue := config.String(s.variable)
- propStruct := values.Elem().Elem()
+ values = removeDefault(values)
+ propStruct := values.Elem()
if !propStruct.IsValid() {
return nil, nil
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index fb0e189..b824c78 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -254,67 +254,75 @@
A *string
B bool
}
-type soongConfigVariables struct {
- Bool_var properties
- Other_bool_var properties
+
+type boolVarProps struct {
+ A *string
+ B bool
+ Conditions_default *properties
}
-type soongConfigProps struct {
- Soong_config_variables soongConfigVariables
+type soongConfigVars struct {
+ Bool_var interface{}
}
func Test_PropertiesToApply(t *testing.T) {
-
- mt := &ModuleType{
- BaseModuleType: "foo",
- ConfigNamespace: "bar",
- Variables: []soongConfigVariable{
- newBoolVariable("bool_var"),
- newBoolVariable("other_bool_var"),
- },
- affectableProperties: []string{
- "a",
- "b",
- },
+ mt, _ := newModuleType(&ModuleTypeProperties{
+ Module_type: "foo",
+ Config_namespace: "bar",
+ Bool_variables: []string{"bool_var"},
+ Properties: []string{"a", "b"},
+ })
+ boolVarPositive := &properties{
+ A: proptools.StringPtr("A"),
+ B: true,
}
- props := soongConfigProps{
- Soong_config_variables: soongConfigVariables{
- Bool_var: properties{
- A: proptools.StringPtr("a"),
- B: true,
- },
- Other_bool_var: properties{
- A: proptools.StringPtr("other"),
- B: false,
+ conditionsDefault := &properties{
+ A: proptools.StringPtr("default"),
+ B: false,
+ }
+ actualProps := &struct {
+ Soong_config_variables soongConfigVars
+ }{
+ Soong_config_variables: soongConfigVars{
+ Bool_var: &boolVarProps{
+ A: boolVarPositive.A,
+ B: boolVarPositive.B,
+ Conditions_default: conditionsDefault,
},
},
}
+ props := reflect.ValueOf(actualProps)
testCases := []struct {
+ name string
config SoongConfig
wantProps []interface{}
}{
{
- config: Config(map[string]string{}),
+ name: "no_vendor_config",
+ config: Config(map[string]string{}),
+ wantProps: []interface{}{conditionsDefault},
},
{
+ name: "vendor_config_false",
+ config: Config(map[string]string{"bool_var": "n"}),
+ wantProps: []interface{}{conditionsDefault},
+ },
+ {
+ name: "bool_var_true",
config: Config(map[string]string{"bool_var": "y"}),
- wantProps: []interface{}{props.Soong_config_variables.Bool_var},
- },
- {
- config: Config(map[string]string{"other_bool_var": "y"}),
- wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+ wantProps: []interface{}{boolVarPositive},
},
}
for _, tc := range testCases {
- gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+ gotProps, err := PropertiesToApply(mt, props, tc.config)
if err != nil {
- t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+ t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
}
if !reflect.DeepEqual(gotProps, tc.wantProps) {
- t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+ t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
}
}
}
diff --git a/android/testing.go b/android/testing.go
index 92ce014..76172d1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -57,6 +57,7 @@
type TestContext struct {
*Context
preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+ bp2buildMutators []RegisterMutatorFunc
NameResolver *NameResolver
}
@@ -81,12 +82,27 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
+// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
+// type to the equivalent Bazel target.
+func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
+ mutatorName := moduleType + "_bp2build"
+ f := func(ctx RegisterMutatorsContext) {
+ ctx.TopDown(mutatorName, m)
+ }
+ bp2buildMutators = append(bp2buildMutators, f)
+}
+
func (ctx *TestContext) Register() {
registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
ctx.RegisterSingletonType("env", EnvSingleton)
}
+// RegisterForBazelConversion prepares a test context for bp2build conversion.
+func (ctx *TestContext) RegisterForBazelConversion() {
+ RegisterMutatorsForBazelConversion(ctx.Context.Context, bp2buildMutators)
+}
+
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
// This function adapts the old style ParseFileList calls that are spread throughout the tests
// to the new style that takes a config.
@@ -469,6 +485,9 @@
//
// The build and source paths should be distinguishable based on their contents.
func NormalizePathForTesting(path Path) string {
+ if path == nil {
+ return "<nil path>"
+ }
p := path.String()
// Allow absolute paths to /dev/
if strings.HasPrefix(p, "/dev/") {
diff --git a/android/variable.go b/android/variable.go
index 41cd337..9b3ed17 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -309,6 +309,9 @@
VndkUseCoreVariant *bool `json:",omitempty"`
VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
+ DirectedVendorSnapshot bool `json:",omitempty"`
+ VendorSnapshotModules map[string]bool `json:",omitempty"`
+
BoardVendorSepolicyDirs []string `json:",omitempty"`
BoardOdmSepolicyDirs []string `json:",omitempty"`
BoardReqdMaskPolicy []string `json:",omitempty"`
@@ -364,6 +367,8 @@
BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
+
+ PrebuiltHiddenApiDir *string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -435,13 +440,15 @@
variableValues := reflect.ValueOf(a.variableProperties).Elem().FieldByName("Product_variables")
+ productVariables := reflect.ValueOf(mctx.Config().productVariables)
+
for i := 0; i < variableValues.NumField(); i++ {
variableValue := variableValues.Field(i)
name := variableValues.Type().Field(i).Name
property := "product_variables." + proptools.PropertyNameForField(name)
// Check that the variable was set for the product
- val := reflect.ValueOf(mctx.Config().productVariables).FieldByName(name)
+ val := productVariables.FieldByName(name)
if !val.IsValid() || val.Kind() != reflect.Ptr || val.IsNil() {
continue
}
diff --git a/apex/Android.bp b/apex/Android.bp
index e3a547c..77dde72 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -18,6 +18,7 @@
"apex.go",
"apex_singleton.go",
"builder.go",
+ "deapexer.go",
"key.go",
"prebuilt.go",
"vndk.go",
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 786496f..c4ea381 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -171,7 +171,10 @@
ExtServices-core(minSdkVersion:current)
flatbuffer_headers(minSdkVersion:(no version))
fmtlib(minSdkVersion:29)
+framework-permission(minSdkVersion:30)
framework-permission(minSdkVersion:current)
+framework-permission-s(minSdkVersion:30)
+framework-permission-s-shared(minSdkVersion:30)
framework-sdkextensions(minSdkVersion:30)
framework-sdkextensions(minSdkVersion:current)
framework-statsd(minSdkVersion:current)
@@ -456,6 +459,7 @@
mediaswcodec(minSdkVersion:29)
metrics-constants-protos(minSdkVersion:29)
modules-utils-build(minSdkVersion:29)
+modules-utils-os(minSdkVersion:30)
ndk_crtbegin_so.19(minSdkVersion:(no version))
ndk_crtbegin_so.21(minSdkVersion:(no version))
ndk_crtbegin_so.27(minSdkVersion:(no version))
@@ -569,9 +573,13 @@
prebuilt_libclang_rt.builtins-arm-android(minSdkVersion:(no version))
prebuilt_libclang_rt.builtins-i686-android(minSdkVersion:(no version))
prebuilt_libclang_rt.builtins-x86_64-android(minSdkVersion:(no version))
+prebuilt_libunwind(minSdkVersion:(no version))
prebuilt_test_framework-sdkextensions(minSdkVersion:(no version))
server_configurable_flags(minSdkVersion:29)
+service-media-s(minSdkVersion:29)
+service-permission(minSdkVersion:30)
service-permission(minSdkVersion:current)
+service-permission-shared(minSdkVersion:30)
service-statsd(minSdkVersion:current)
SettingsLibActionBarShadow(minSdkVersion:21)
SettingsLibAppPreference(minSdkVersion:21)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 0b114f8..99cd75e 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -126,8 +126,18 @@
moduleName := a.fullModuleName(apexBundleName, &fi)
- if !android.InList(moduleName, moduleNames) {
- moduleNames = append(moduleNames, moduleName)
+ // This name will be added to LOCAL_REQUIRED_MODULES of the APEX. We need to be
+ // arch-specific otherwise we will end up installing both ABIs even when only
+ // either of the ABI is requested.
+ aName := moduleName
+ switch fi.multilib {
+ case "lib32":
+ aName = aName + ":32"
+ case "lib64":
+ aName = aName + ":64"
+ }
+ if !android.InList(aName, moduleNames) {
+ moduleNames = append(moduleNames, aName)
}
if linkToSystemLib {
@@ -440,12 +450,21 @@
fmt.Fprintf(w, dist)
}
- if a.coverageOutputPath.String() != "" {
+ if a.apisUsedByModuleFile.String() != "" {
goal := "apps_only"
- distFile := a.coverageOutputPath.String()
+ distFile := a.apisUsedByModuleFile.String()
fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
" $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
- "endif",
+ "endif\n",
+ goal, distFile, distFile)
+ }
+
+ if a.apisBackedByModuleFile.String() != "" {
+ goal := "apps_only"
+ distFile := a.apisBackedByModuleFile.String()
+ fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
+ " $(call dist-for-goals,%s,%s:ndk_apis_backedby_apex/$(notdir %s))\n"+
+ "endif\n",
goal, distFile, distFile)
}
}
diff --git a/apex/apex.go b/apex/apex.go
index 5cd18ed..384d6c7 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,6 +29,7 @@
"android/soong/android"
"android/soong/bpf"
"android/soong/cc"
+ "android/soong/dexpreopt"
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
@@ -389,7 +390,8 @@
isCompressed bool
// Path of API coverage generate file
- coverageOutputPath android.ModuleOutPath
+ apisUsedByModuleFile android.ModuleOutPath
+ apisBackedByModuleFile android.ModuleOutPath
}
// apexFileClass represents a type of file that can be included in APEX.
@@ -440,6 +442,8 @@
transitiveDep bool
isJniLib bool
+ multilib string
+
// TODO(jiyong): remove this
module android.Module
}
@@ -459,6 +463,7 @@
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+ ret.multilib = module.Target().Arch.ArchType.Multilib
}
return ret
}
@@ -720,9 +725,15 @@
ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
- // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
- if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ if a.artApex {
+ // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ }
+ // The ART boot image depends on dex2oat to compile it.
+ if !java.SkipDexpreoptBootJars(ctx) {
+ dexpreopt.RegisterToolDeps(ctx)
+ }
}
// Dependencies for signing
@@ -1428,6 +1439,7 @@
}
var _ javaModule = (*java.Library)(nil)
+var _ javaModule = (*java.Import)(nil)
var _ javaModule = (*java.SdkLibrary)(nil)
var _ javaModule = (*java.DexImport)(nil)
var _ javaModule = (*java.SdkLibraryImport)(nil)
@@ -1501,7 +1513,7 @@
return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
}
-// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+// WalkPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
// visited module, the `do` callback is executed. Returning true in the callback continues the visit
// to the child modules. Returning false makes the visit to continue in the sibling or the parent
// modules. This is used in check* functions below.
@@ -1520,6 +1532,9 @@
if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
return false
}
+ if depTag == dexpreopt.Dex2oatDepTag {
+ return false
+ }
ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
@@ -1629,7 +1644,7 @@
}
case javaLibTag:
switch child.(type) {
- case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+ case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
af := apexFileForJavaModule(ctx, child.(javaModule))
if !af.ok() {
ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
@@ -1841,10 +1856,10 @@
return
}
- // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
- // generated by the dexpreopt singleton, and here we access build artifacts via the global
- // boot image config.
if a.artApex {
+ // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
+ // generated by the dexpreopt singleton, and here we access build artifacts via the global
+ // boot image config.
for arch, files := range java.DexpreoptedArtApexJars(ctx) {
dirInApex := filepath.Join("javalib", arch.String())
for _, f := range files {
@@ -1853,6 +1868,11 @@
filesInfo = append(filesInfo, af)
}
}
+ // Call GetGlobalSoongConfig to initialize it, which may be necessary if dexpreopt is
+ // disabled for libraries/apps, but boot images are still needed.
+ if !java.SkipDexpreoptBootJars(ctx) {
+ dexpreopt.GetGlobalSoongConfig(ctx)
+ }
}
// Remove duplicates in filesInfo
@@ -2854,7 +2874,7 @@
"libprofile-clang-extras_ndk",
"libprofile-extras",
"libprofile-extras_ndk",
- "libunwind_llvm",
+ "libunwind",
}
return m
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index fc74672..b1e8480 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -190,6 +190,7 @@
"testdata/baz": nil,
"AppSet.apks": nil,
"foo.rs": nil,
+ "libfoo.jar": nil,
}
cc.GatherRequiredFilesForTest(fs)
@@ -247,6 +248,7 @@
cc.RegisterRequiredBuildComponentsForTest(ctx)
rust.RegisterRequiredBuildComponentsForTest(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("cc_test", cc.TestFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -255,14 +257,6 @@
ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterAppImportBuildComponents(ctx)
- java.RegisterAppSetBuildComponents(ctx)
- java.RegisterRuntimeResourceOverlayBuildComponents(ctx)
- java.RegisterSdkLibraryBuildComponents(ctx)
- java.RegisterPrebuiltApisBuildComponents(ctx)
ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
ctx.RegisterModuleType("bpf", bpf.BpfFactory)
@@ -1751,10 +1745,10 @@
// ensure apex variant of c++ is linked with static unwinder
cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module)
- ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+ ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
// note that platform variant is not.
cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module)
- ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped")
+ ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind")
}
func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) {
@@ -4219,6 +4213,121 @@
}
}
+func TestPrebuiltExportDexImplementationJars(t *testing.T) {
+ transform := func(config *dexpreopt.GlobalConfig) {
+ config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+ }
+
+ checkDexJarBuildPath := func(ctx *android.TestContext, name string) {
+ // Make sure the import has been given the correct path to the dex jar.
+ p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.Dependency)
+ dexJarBuildPath := p.DexJarBuildPath()
+ if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
+ t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
+ }
+ }
+
+ ensureNoSourceVariant := func(ctx *android.TestContext) {
+ // Make sure that an apex variant is not created for the source module.
+ if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests("libfoo"); !reflect.DeepEqual(expected, actual) {
+ t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
+ }
+ }
+
+ t.Run("prebuilt only", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ checkDexJarBuildPath(ctx, "libfoo")
+ })
+
+ t.Run("prebuilt with source preferred", func(t *testing.T) {
+
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+
+ java_library {
+ name: "libfoo",
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ checkDexJarBuildPath(ctx, "prebuilt_libfoo")
+ ensureNoSourceVariant(ctx)
+ })
+
+ t.Run("prebuilt preferred with source", func(t *testing.T) {
+ bp := `
+ prebuilt_apex {
+ name: "myapex",
+ prefer: true,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+
+ java_library {
+ name: "libfoo",
+ }
+ `
+
+ // Make sure that dexpreopt can access dex implementation files from the prebuilt.
+ ctx := testDexpreoptWithApexes(t, bp, "", transform)
+
+ checkDexJarBuildPath(ctx, "prebuilt_libfoo")
+ ensureNoSourceVariant(ctx)
+ })
+}
+
func TestApexWithTests(t *testing.T) {
ctx, config := testApex(t, `
apex_test {
@@ -4399,6 +4508,34 @@
`)
}
+func TestApexWithJavaImport(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["myjavaimport"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_import {
+ name: "myjavaimport",
+ apex_available: ["myapex"],
+ jars: ["my.jar"],
+ compile_dex: true,
+ }
+ `)
+
+ module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+ apexRule := module.Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+ ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar")
+}
+
func TestApexWithApps(t *testing.T) {
ctx, _ := testApex(t, `
apex {
@@ -5536,7 +5673,7 @@
ensureNotContains(t, androidMk, "LOCAL_MODULE := prebuilt_myotherlib.myapex\n")
ensureNotContains(t, androidMk, "LOCAL_MODULE := myotherlib.myapex\n")
// `myapex` should have `myotherlib` in its required line, not `prebuilt_myotherlib`
- ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex myotherlib apex_manifest.pb.myapex apex_pubkey.myapex\n")
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += mylib.myapex:64 myotherlib:64 apex_manifest.pb.myapex apex_pubkey.myapex\n")
}
func TestApexWithJniLibs(t *testing.T) {
@@ -5783,7 +5920,7 @@
testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
}
-func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
t.Helper()
bp += cc.GatherRequiredDepsForTest(android.Android)
@@ -5808,21 +5945,18 @@
ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("apex", BundleFactory)
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+ ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
android.RegisterPrebuiltMutators(ctx)
cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterDexpreoptBootJarsComponents(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
ctx.Register()
- _ = dexpreopt.GlobalSoongConfigForTests(config)
dexpreopt.RegisterToolModulesForTest(ctx)
pathCtx := android.PathContextForTesting(config)
dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
@@ -5837,10 +5971,11 @@
android.FailIfErrored(t, errs)
} else if len(errs) > 0 {
android.FailIfNoMatchingErrors(t, errmsg, errs)
- return
} else {
t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
}
+
+ return ctx
}
func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
@@ -5939,6 +6074,56 @@
}
testNoUpdatableJarsInBootImage(t, "", transform)
})
+
+}
+
+func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
+ transform := func(config *dexpreopt.GlobalConfig) {
+ config.BootJars = android.CreateTestConfiguredJarList([]string{"myapex:libfoo"})
+ }
+ t.Run("prebuilt no source", func(t *testing.T) {
+ testDexpreoptWithApexes(t, `
+ prebuilt_apex {
+ name: "myapex" ,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+`, "", transform)
+ })
+
+ t.Run("prebuilt no source", func(t *testing.T) {
+ testDexpreoptWithApexes(t, `
+ prebuilt_apex {
+ name: "myapex" ,
+ arch: {
+ arm64: {
+ src: "myapex-arm64.apex",
+ },
+ arm: {
+ src: "myapex-arm.apex",
+ },
+ },
+ exported_java_libs: ["libfoo"],
+ }
+
+ java_import {
+ name: "libfoo",
+ jars: ["libfoo.jar"],
+ }
+`, "", transform)
+ })
}
func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, apexBootJars []string, rules []android.Rule) {
@@ -5968,9 +6153,7 @@
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
cc.RegisterRequiredBuildComponentsForTest(ctx)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
- java.RegisterDexpreoptBootJarsComponents(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
diff --git a/apex/builder.go b/apex/builder.go
index bc1b566..67314d8 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -687,7 +687,7 @@
implicitInputs = append(implicitInputs, unsignedOutputFile)
// Run coverage analysis
- apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+".txt")
+ apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt")
ctx.Build(pctx, android.BuildParams{
Rule: generateAPIsUsedbyApexRule,
Implicits: implicitInputs,
@@ -698,7 +698,19 @@
"readelf": "${config.ClangBin}/llvm-readelf",
},
})
- a.coverageOutputPath = apisUsedbyOutputFile
+ a.apisUsedByModuleFile = apisUsedbyOutputFile
+
+ apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
+ ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
+ Text(imageDir.String()).
+ Implicits(implicitInputs).
+ Output(apisBackedbyOutputFile).
+ Input(ndkLibraryList)
+ rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
+ a.apisBackedByModuleFile = apisBackedbyOutputFile
bundleConfig := a.buildBundleConfig(ctx)
@@ -786,6 +798,9 @@
compressRule.Build("compressRule", "Generate unsigned compressed APEX file")
signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex")
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+ args["outCommaList"] = signedCompressedOutputFile.String()
+ }
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "sign compressedApex",
diff --git a/apex/deapexer.go b/apex/deapexer.go
new file mode 100644
index 0000000..651cadf
--- /dev/null
+++ b/apex/deapexer.go
@@ -0,0 +1,139 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// 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 apex
+
+import (
+ "android/soong/android"
+)
+
+// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
+// within a .apex file referenced by `prebuilt_apex` available for use by their associated
+// `java_import` modules.
+//
+// An 'apex' module references `java_library` modules from which .dex files are obtained that are
+// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
+// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
+// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
+// that contains the Java classes.
+//
+// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
+// `java_import` provides the classes jar (jar containing `.class` files) against which the
+// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
+// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
+// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
+// library specific to the current Android version. This process requires access to implementation
+// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
+// the `.apex` file in the associated `prebuilt_apex`.
+//
+// This is intentionally not registered by name as it is not intended to be used from within an
+// `Android.bp` file.
+
+// Properties that are specific to `deapexer` but which need to be provided on the `prebuilt_apex`
+// module.`
+type DeapexerProperties struct {
+ // List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
+ // APEX bundle will provide dex implementation jars for use by dexpreopt and boot jars package
+ // check.
+ Exported_java_libs []string
+}
+
+type Deapexer struct {
+ android.ModuleBase
+ prebuilt android.Prebuilt
+
+ properties DeapexerProperties
+ apexFileProperties ApexFileProperties
+
+ inputApex android.Path
+}
+
+func privateDeapexerFactory() android.Module {
+ module := &Deapexer{}
+ module.AddProperties(
+ &module.properties,
+ &module.apexFileProperties,
+ )
+ android.InitSingleSourcePrebuiltModule(module, &module.apexFileProperties, "Source")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (p *Deapexer) Prebuilt() *android.Prebuilt {
+ return &p.prebuilt
+}
+
+func (p *Deapexer) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if err := p.apexFileProperties.selectSource(ctx); err != nil {
+ ctx.ModuleErrorf("%s", err)
+ return
+ }
+
+ // Add dependencies from the java modules to which this exports files from the `.apex` file onto
+ // this module so that they can access the `DeapexerInfo` object that this provides.
+ for _, lib := range p.properties.Exported_java_libs {
+ dep := prebuiltApexExportedModuleName(ctx, lib)
+ ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
+ }
+}
+
+func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
+
+ // Create and remember the directory into which the .apex file's contents will be unpacked.
+ deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
+
+ exports := make(map[string]android.Path)
+
+ // Create mappings from name+tag to all the required exported paths.
+ for _, l := range p.properties.Exported_java_libs {
+ // Populate the exports that this makes available. The path here must match the path of the
+ // file in the APEX created by apexFileForJavaModule(...).
+ exports[l+"{.dexjar}"] = deapexerOutput.Join(ctx, "javalib", l+".jar")
+ }
+
+ // If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
+ // deapexer and verifies that all the required files were created. Also, make the mapping from
+ // name+tag to path available for other modules.
+ if len(exports) > 0 {
+ // Make the information available for other modules.
+ ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
+
+ // Create a sorted list of the files that this exports.
+ exportedPaths := make(android.Paths, 0, len(exports))
+ for _, p := range exports {
+ exportedPaths = append(exportedPaths, p)
+ }
+ exportedPaths = android.SortedUniquePaths(exportedPaths)
+
+ // The apex needs to export some files so create a ninja rule to unpack the apex and check that
+ // the required files are present.
+ builder := android.NewRuleBuilder(pctx, ctx)
+ command := builder.Command()
+ command.
+ Tool(android.PathForSource(ctx, "build/soong/scripts/unpack-prebuilt-apex.sh")).
+ BuiltTool("deapexer").
+ BuiltTool("debugfs").
+ Input(p.inputApex).
+ Text(deapexerOutput.String())
+ for _, p := range exportedPaths {
+ command.Output(p.(android.WritablePath))
+ }
+ builder.Build("deapexer", "deapex "+ctx.ModuleName())
+ }
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 50e892e..c72a9eb 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -158,6 +158,7 @@
type PrebuiltProperties struct {
ApexFileProperties
+ DeapexerProperties
Installable *bool
// Optional name for the installed apex. If unspecified, name of the
@@ -198,19 +199,152 @@
}
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+//
+// If this needs to make files from within a `.apex` file available for use by other Soong modules,
+// e.g. make dex implementation jars available for java_import modules isted in exported_java_libs,
+// it does so as follows:
+//
+// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
+// makes them available for use by other modules, at both Soong and ninja levels.
+//
+// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
+// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
+// dexpreopt, will work the same way from source and prebuilt.
+//
+// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
+// itself so that they can retrieve the file paths to those files.
+//
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
module.AddProperties(&module.properties)
android.InitSingleSourcePrebuiltModule(module, &module.properties, "Source")
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ props := struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(module.BaseModuleName() + ".deapexer"),
+ }
+ ctx.CreateModule(privateDeapexerFactory,
+ &props,
+ &module.properties.ApexFileProperties,
+ &module.properties.DeapexerProperties,
+ )
+ })
+
return module
}
+func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
+ // The prebuilt_apex should be depending on prebuilt modules but as this runs after
+ // prebuilt_rename the prebuilt module may or may not be using the prebuilt_ prefixed named. So,
+ // check to see if the prefixed name is in use first, if it is then use that, otherwise assume
+ // the unprefixed name is the one to use. If the unprefixed one turns out to be a source module
+ // and not a renamed prebuilt module then that will be detected and reported as an error when
+ // processing the dependency in ApexInfoMutator().
+ prebuiltName := "prebuilt_" + name
+ if ctx.OtherModuleExists(prebuiltName) {
+ name = prebuiltName
+ }
+ return name
+}
+
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
if err := p.properties.selectSource(ctx); err != nil {
ctx.ModuleErrorf("%s", err)
return
}
+
+ // Add dependencies onto the java modules that represent the java libraries that are provided by
+ // and exported from this prebuilt apex.
+ for _, lib := range p.properties.Exported_java_libs {
+ dep := prebuiltApexExportedModuleName(ctx, lib)
+ ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), javaLibTag, dep)
+ }
+}
+
+var _ ApexInfoMutator = (*Prebuilt)(nil)
+
+// ApexInfoMutator marks any modules for which this apex exports a file as requiring an apex
+// specific variant and checks that they are supported.
+//
+// The apexMutator will ensure that the ApexInfo objects passed to BuildForApex(ApexInfo) are
+// associated with the apex specific variant using the ApexInfoProvider for later retrieval.
+//
+// Unlike the source apex module type the prebuilt_apex module type cannot share compatible variants
+// across prebuilt_apex modules. That is because there is no way to determine whether two
+// prebuilt_apex modules that export files for the same module are compatible. e.g. they could have
+// been built from different source at different times or they could have been built with different
+// build options that affect the libraries.
+//
+// While it may be possible to provide sufficient information to determine whether two prebuilt_apex
+// modules were compatible it would be a lot of work and would not provide much benefit for a couple
+// of reasons:
+// * The number of prebuilt_apex modules that will be exporting files for the same module will be
+// low as the prebuilt_apex only exports files for the direct dependencies that require it and
+// very few modules are direct dependencies of multiple prebuilt_apex modules, e.g. there are a
+// few com.android.art* apex files that contain the same contents and could export files for the
+// same modules but only one of them needs to do so. Contrast that with source apex modules which
+// need apex specific variants for every module that contributes code to the apex, whether direct
+// or indirect.
+// * The build cost of a prebuilt_apex variant is generally low as at worst it will involve some
+// extra copying of files. Contrast that with source apex modules that has to build each variant
+// from source.
+func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
+
+ // Collect direct dependencies into contents.
+ contents := make(map[string]android.ApexMembership)
+
+ // Collect the list of dependencies.
+ var dependencies []android.ApexModule
+ mctx.VisitDirectDeps(func(m android.Module) {
+ tag := mctx.OtherModuleDependencyTag(m)
+ if tag == javaLibTag {
+ depName := mctx.OtherModuleName(m)
+
+ // It is an error if the other module is not a prebuilt.
+ if _, ok := m.(android.PrebuiltInterface); !ok {
+ mctx.PropertyErrorf("exported_java_libs", "%q is not a prebuilt module", depName)
+ return
+ }
+
+ // It is an error if the other module is not an ApexModule.
+ if _, ok := m.(android.ApexModule); !ok {
+ mctx.PropertyErrorf("exported_java_libs", "%q is not usable within an apex", depName)
+ return
+ }
+
+ // Strip off the prebuilt_ prefix if present before storing content to ensure consistent
+ // behavior whether there is a corresponding source module present or not.
+ depName = android.RemoveOptionalPrebuiltPrefix(depName)
+
+ // Remember that this module was added as a direct dependency.
+ contents[depName] = contents[depName].Add(true)
+
+ // Add the module to the list of dependencies that need to have an APEX variant.
+ dependencies = append(dependencies, m.(android.ApexModule))
+ }
+ })
+
+ // Create contents for the prebuilt_apex and store it away for later use.
+ apexContents := android.NewApexContents(contents)
+ mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+ Contents: apexContents,
+ })
+
+ // Create an ApexInfo for the prebuilt_apex.
+ apexInfo := android.ApexInfo{
+ ApexVariationName: mctx.ModuleName(),
+ InApexes: []string{mctx.ModuleName()},
+ ApexContents: []*android.ApexContents{apexContents},
+ ForPrebuiltApex: true,
+ }
+
+ // Mark the dependencies of this module as requiring a variant for this module.
+ for _, am := range dependencies {
+ am.BuildForApex(apexInfo)
+ }
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 05eddc1..d222d98 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -6,6 +6,9 @@
"constants.go",
"properties.go",
],
+ testSrcs: [
+ "aquery_test.go",
+ ],
pluginFor: [
"soong_build",
],
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 404be8c..a196e8b 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -83,11 +83,15 @@
// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
// aquery invocation).
-func AqueryBuildStatements(aqueryJsonProto []byte) []BuildStatement {
+func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
buildStatements := []BuildStatement{}
var aqueryResult actionGraphContainer
- json.Unmarshal(aqueryJsonProto, &aqueryResult)
+ err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
+
+ if err != nil {
+ return nil, err
+ }
pathFragments := map[int]pathFragment{}
for _, pathFragment := range aqueryResult.PathFragments {
@@ -97,8 +101,7 @@
for _, artifact := range aqueryResult.Artifacts {
artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
if err != nil {
- // TODO(cparsons): Better error handling.
- panic(err.Error())
+ return nil, err
}
artifactIdToPath[artifact.Id] = artifactPath
}
@@ -110,15 +113,24 @@
for _, actionEntry := range aqueryResult.Actions {
outputPaths := []string{}
for _, outputId := range actionEntry.OutputIds {
- // TODO(cparsons): Validate the id is present.
- outputPaths = append(outputPaths, artifactIdToPath[outputId])
+ outputPath, exists := artifactIdToPath[outputId]
+ if !exists {
+ return nil, fmt.Errorf("undefined outputId %d", outputId)
+ }
+ outputPaths = append(outputPaths, outputPath)
}
inputPaths := []string{}
for _, inputDepSetId := range actionEntry.InputDepSetIds {
- // TODO(cparsons): Validate the id is present.
- for _, inputId := range depsetIdToArtifactIds[inputDepSetId] {
- // TODO(cparsons): Validate the id is present.
- inputPaths = append(inputPaths, artifactIdToPath[inputId])
+ inputArtifacts, exists := depsetIdToArtifactIds[inputDepSetId]
+ if !exists {
+ return nil, fmt.Errorf("undefined input depsetId %d", inputDepSetId)
+ }
+ for _, inputId := range inputArtifacts {
+ inputPath, exists := artifactIdToPath[inputId]
+ if !exists {
+ return nil, fmt.Errorf("undefined input artifactId %d", inputId)
+ }
+ inputPaths = append(inputPaths, inputPath)
}
}
buildStatement := BuildStatement{
@@ -130,7 +142,7 @@
buildStatements = append(buildStatements, buildStatement)
}
- return buildStatements
+ return buildStatements, nil
}
func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
@@ -140,7 +152,7 @@
for currId > 0 {
currFragment, ok := pathFragmentsMap[currId]
if !ok {
- return "", fmt.Errorf("undefined path fragment id '%s'", currId)
+ return "", fmt.Errorf("undefined path fragment id %d", currId)
}
labels = append([]string{currFragment.Label}, labels...)
currId = currFragment.ParentId
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
new file mode 100644
index 0000000..1bd6e67
--- /dev/null
+++ b/bazel/aquery_test.go
@@ -0,0 +1,450 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bazel
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+func TestAqueryMultiArchGenrule(t *testing.T) {
+ // This input string is retrieved from a real build of bionic-related genrules.
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 6
+ }, {
+ "id": 3,
+ "pathFragmentId": 8
+ }, {
+ "id": 4,
+ "pathFragmentId": 12
+ }, {
+ "id": 5,
+ "pathFragmentId": 19
+ }, {
+ "id": 6,
+ "pathFragmentId": 20
+ }, {
+ "id": 7,
+ "pathFragmentId": 21
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [1],
+ "outputIds": [4],
+ "primaryOutputId": 4
+ }, {
+ "targetId": 2,
+ "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [2],
+ "outputIds": [5],
+ "primaryOutputId": 5
+ }, {
+ "targetId": 3,
+ "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [3],
+ "outputIds": [6],
+ "primaryOutputId": 6
+ }, {
+ "targetId": 4,
+ "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
+ "mnemonic": "Genrule",
+ "configurationId": 1,
+ "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
+ "environmentVariables": [{
+ "key": "PATH",
+ "value": "/bin:/usr/bin:/usr/local/bin"
+ }],
+ "inputDepSetIds": [4],
+ "outputIds": [7],
+ "primaryOutputId": 7
+ }],
+ "targets": [{
+ "id": 1,
+ "label": "@sourceroot//bionic/libc:syscalls-arm",
+ "ruleClassId": 1
+ }, {
+ "id": 2,
+ "label": "@sourceroot//bionic/libc:syscalls-x86",
+ "ruleClassId": 1
+ }, {
+ "id": 3,
+ "label": "@sourceroot//bionic/libc:syscalls-x86_64",
+ "ruleClassId": 1
+ }, {
+ "id": 4,
+ "label": "@sourceroot//bionic/libc:syscalls-arm64",
+ "ruleClassId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 2,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 3,
+ "directArtifactIds": [1, 2, 3]
+ }, {
+ "id": 4,
+ "directArtifactIds": [1, 2, 3]
+ }],
+ "configuration": [{
+ "id": 1,
+ "mnemonic": "k8-fastbuild",
+ "platformName": "k8",
+ "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
+ }],
+ "ruleClasses": [{
+ "id": 1,
+ "name": "genrule"
+ }],
+ "pathFragments": [{
+ "id": 5,
+ "label": ".."
+ }, {
+ "id": 4,
+ "label": "sourceroot",
+ "parentId": 5
+ }, {
+ "id": 3,
+ "label": "bionic",
+ "parentId": 4
+ }, {
+ "id": 2,
+ "label": "libc",
+ "parentId": 3
+ }, {
+ "id": 1,
+ "label": "SYSCALLS.TXT",
+ "parentId": 2
+ }, {
+ "id": 7,
+ "label": "tools",
+ "parentId": 2
+ }, {
+ "id": 6,
+ "label": "gensyscalls.py",
+ "parentId": 7
+ }, {
+ "id": 11,
+ "label": "bazel_tools",
+ "parentId": 5
+ }, {
+ "id": 10,
+ "label": "tools",
+ "parentId": 11
+ }, {
+ "id": 9,
+ "label": "genrule",
+ "parentId": 10
+ }, {
+ "id": 8,
+ "label": "genrule-setup.sh",
+ "parentId": 9
+ }, {
+ "id": 18,
+ "label": "bazel-out"
+ }, {
+ "id": 17,
+ "label": "sourceroot",
+ "parentId": 18
+ }, {
+ "id": 16,
+ "label": "k8-fastbuild",
+ "parentId": 17
+ }, {
+ "id": 15,
+ "label": "bin",
+ "parentId": 16
+ }, {
+ "id": 14,
+ "label": "bionic",
+ "parentId": 15
+ }, {
+ "id": 13,
+ "label": "libc",
+ "parentId": 14
+ }, {
+ "id": 12,
+ "label": "syscalls-arm.S",
+ "parentId": 13
+ }, {
+ "id": 19,
+ "label": "syscalls-x86.S",
+ "parentId": 13
+ }, {
+ "id": 20,
+ "label": "syscalls-x86_64.S",
+ "parentId": 13
+ }, {
+ "id": 21,
+ "label": "syscalls-arm64.S",
+ "parentId": 13
+ }]
+}`
+ actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
+ expectedBuildStatements := []BuildStatement{}
+ for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
+ expectedBuildStatements = append(expectedBuildStatements,
+ BuildStatement{
+ Command: fmt.Sprintf(
+ "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
+ arch, arch),
+ OutputPaths: []string{
+ fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
+ },
+ InputPaths: []string{
+ "../sourceroot/bionic/libc/SYSCALLS.TXT",
+ "../sourceroot/bionic/libc/tools/gensyscalls.py",
+ "../bazel_tools/tools/genrule/genrule-setup.sh",
+ },
+ Env: []KeyValuePair{
+ KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
+ },
+ Mnemonic: "Genrule",
+ })
+ }
+ assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
+}
+
+func TestInvalidOutputId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [3],
+ "primaryOutputId": 3
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined outputId 3")
+}
+
+func TestInvalidInputDepsetId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [2],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined input depsetId 2")
+}
+
+func TestInvalidInputArtifactId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 3]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined input artifactId 3")
+}
+
+func TestInvalidPathFragmentId(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "x",
+ "arguments": ["touch", "foo"],
+ "inputDepSetIds": [1],
+ "outputIds": [1],
+ "primaryOutputId": 1
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1, 2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "two",
+ "parentId": 3
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, "undefined path fragment id 3")
+}
+
+func assertError(t *testing.T, err error, expected string) {
+ if err == nil || err.Error() != expected {
+ t.Errorf("expected error '%s', but got: %s", expected, err)
+ }
+}
+
+// Asserts that the given actual build statements match the given expected build statements.
+// Build statement equivalence is determined using buildStatementEquals.
+func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+ if len(expected) != len(actual) {
+ t.Errorf("expected %d build statements, but got %d,\n expected: %s,\n actual: %s",
+ len(expected), len(actual), expected, actual)
+ return
+ }
+ACTUAL_LOOP:
+ for _, actualStatement := range actual {
+ for _, expectedStatement := range expected {
+ if buildStatementEquals(actualStatement, expectedStatement) {
+ continue ACTUAL_LOOP
+ }
+ }
+ t.Errorf("unexpected build statement %s.\n expected: %s",
+ actualStatement, expected)
+ return
+ }
+}
+
+func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
+ if first.Mnemonic != second.Mnemonic {
+ return false
+ }
+ if first.Command != second.Command {
+ return false
+ }
+ // Ordering is significant for environment variables.
+ if !reflect.DeepEqual(first.Env, second.Env) {
+ return false
+ }
+ // Ordering is irrelevant for input and output paths, so compare sets.
+ if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
+ return false
+ }
+ if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
+ return false
+ }
+ return true
+}
+
+func stringSet(stringSlice []string) map[string]struct{} {
+ stringMap := make(map[string]struct{})
+ for _, s := range stringSlice {
+ stringMap[s] = struct{}{}
+ }
+ return stringMap
+}
diff --git a/bazel/properties.go b/bazel/properties.go
index 8bb1956..ac0047b 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,9 +19,16 @@
Label string
}
-// Properties contains common module properties for migration purposes.
+// Properties contains common module properties for Bazel migration purposes.
type Properties struct {
// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
// this Soong module.
Bazel_module bazelModuleProperties
}
+
+// BazelTargetModuleProperties contain properties and metadata used for
+// Blueprint to BUILD file conversion.
+type BazelTargetModuleProperties struct {
+ // The Bazel rule class for this target.
+ Rule_class string
+}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 49587f4..8007574 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -10,6 +10,7 @@
],
deps: [
"soong-android",
+ "soong-bazel",
],
testSrcs: [
"build_conversion_test.go",
diff --git a/bp2build/androidbp_to_build_templates.go b/bp2build/androidbp_to_build_templates.go
index 75c3ccb..9bac86b 100644
--- a/bp2build/androidbp_to_build_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -15,7 +15,7 @@
package bp2build
const (
- // The default `load` preamble for every generated BUILD file.
+ // The default `load` preamble for every generated queryview BUILD file.
soongModuleLoad = `package(default_visibility = ["//visibility:public"])
load("//build/bazel/queryview_rules:soong_module.bzl", "soong_module")
@@ -31,6 +31,10 @@
module_deps = %s,
%s)`
+ bazelTarget = `%s(
+ name = "%s",
+%s)`
+
// A simple provider to mark and differentiate Soong module rule shims from
// regular Bazel rules. Every Soong module rule shim returns a
// SoongModuleInfo provider, and can only depend on rules returning
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 30f298a..75b6097 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -16,52 +16,34 @@
import (
"android/soong/android"
+ "fmt"
"os"
)
-// The Bazel bp2build singleton is responsible for writing .bzl files that are equivalent to
+// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
// Android.bp files that are capable of being built with Bazel.
-func init() {
- android.RegisterBazelConverterPreSingletonType("androidbp_to_build", AndroidBpToBuildSingleton)
-}
-
-func AndroidBpToBuildSingleton() android.Singleton {
- return &androidBpToBuildSingleton{
- name: "bp2build",
- }
-}
-
-type androidBpToBuildSingleton struct {
- name string
- outputDir android.OutputPath
-}
-
-func (s *androidBpToBuildSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- s.outputDir = android.PathForOutput(ctx, s.name)
- android.RemoveAllOutputDir(s.outputDir)
-
- if !ctx.Config().IsEnvTrue("CONVERT_TO_BAZEL") {
- return
- }
+func Codegen(ctx CodegenContext) {
+ outputDir := android.PathForOutput(ctx, "bp2build")
+ android.RemoveAllOutputDir(outputDir)
ruleShims := CreateRuleShims(android.ModuleTypeFactories())
- buildToTargets := GenerateSoongModuleTargets(ctx)
+ buildToTargets := GenerateSoongModuleTargets(ctx.Context(), true)
- filesToWrite := CreateBazelFiles(ruleShims, buildToTargets)
+ filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, true)
for _, f := range filesToWrite {
- if err := s.writeFile(ctx, f); err != nil {
- ctx.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
+ if err := writeFile(outputDir, ctx, f); err != nil {
+ fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
}
}
}
-func (s *androidBpToBuildSingleton) getOutputPath(ctx android.PathContext, dir string) android.OutputPath {
- return s.outputDir.Join(ctx, dir)
+func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
+ return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
}
-func (s *androidBpToBuildSingleton) writeFile(ctx android.PathContext, f BazelFile) error {
- return writeReadOnlyFile(ctx, s.getOutputPath(ctx, f.Dir), f.Basename, f.Contents)
+func getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+ return outputDir.Join(ctx, dir)
}
// The auto-conversion directory should be read-only, sufficient for bazel query. The files
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 0329685..da2fb7f 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -39,8 +39,26 @@
ModuleSubDir(module blueprint.Module) string
ModuleType(module blueprint.Module) string
- VisitAllModulesBlueprint(visit func(blueprint.Module))
- VisitDirectDeps(module android.Module, visit func(android.Module))
+ VisitAllModules(visit func(blueprint.Module))
+ VisitDirectDeps(module blueprint.Module, visit func(blueprint.Module))
+}
+
+type CodegenContext struct {
+ config android.Config
+ context android.Context
+}
+
+func (ctx CodegenContext) AddNinjaFileDeps(...string) {}
+func (ctx CodegenContext) Config() android.Config { return ctx.config }
+func (ctx CodegenContext) Context() android.Context { return ctx.context }
+
+// NewCodegenContext creates a wrapper context that conforms to PathContext for
+// writing BUILD files in the output directory.
+func NewCodegenContext(config android.Config, context android.Context) CodegenContext {
+ return CodegenContext{
+ context: context,
+ config: config,
+ }
}
// props is an unsorted map. This function ensures that
@@ -55,16 +73,51 @@
return attributes
}
-func GenerateSoongModuleTargets(ctx bpToBuildContext) map[string][]BazelTarget {
+func GenerateSoongModuleTargets(ctx bpToBuildContext, bp2buildEnabled bool) map[string][]BazelTarget {
buildFileToTargets := make(map[string][]BazelTarget)
- ctx.VisitAllModulesBlueprint(func(m blueprint.Module) {
+ ctx.VisitAllModules(func(m blueprint.Module) {
dir := ctx.ModuleDir(m)
- t := generateSoongModuleTarget(ctx, m)
+ var t BazelTarget
+
+ if bp2buildEnabled {
+ if _, ok := m.(android.BazelTargetModule); !ok {
+ return
+ }
+ t = generateBazelTarget(ctx, m)
+ } else {
+ t = generateSoongModuleTarget(ctx, m)
+ }
+
buildFileToTargets[ctx.ModuleDir(m)] = append(buildFileToTargets[dir], t)
})
return buildFileToTargets
}
+func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
+ // extract the bazel attributes from the module.
+ props := getBuildProperties(ctx, m)
+
+ // extract the rule class name from the attributes. Since the string value
+ // will be string-quoted, remove the quotes here.
+ ruleClass := strings.Replace(props.Attrs["rule_class"], "\"", "", 2)
+ // Delete it from being generated in the BUILD file.
+ delete(props.Attrs, "rule_class")
+
+ // Return the Bazel target with rule class and attributes, ready to be
+ // code-generated.
+ attributes := propsToAttributes(props.Attrs)
+ targetName := targetNameForBp2Build(ctx, m)
+ return BazelTarget{
+ name: targetName,
+ content: fmt.Sprintf(
+ bazelTarget,
+ ruleClass,
+ targetName,
+ attributes,
+ ),
+ }
+}
+
// Convert a module and its deps and props into a Bazel macro/rule
// representation in the BUILD file.
func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
@@ -75,7 +128,7 @@
// out the implications of that.
depLabels := map[string]bool{}
if aModule, ok := m.(android.Module); ok {
- ctx.VisitDirectDeps(aModule, func(depModule android.Module) {
+ ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
depLabels[qualifiedTargetLabel(ctx, depModule)] = true
})
}
@@ -288,6 +341,10 @@
return strings.Repeat(" ", indent)
}
+func targetNameForBp2Build(c bpToBuildContext, logicModule blueprint.Module) string {
+ return strings.Replace(c.ModuleName(logicModule), "__bp2build__", "", 1)
+}
+
func targetNameWithVariant(c bpToBuildContext, logicModule blueprint.Module) string {
name := ""
if c.ModuleSubDir(logicModule) != "" {
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 8230ad8..2df72bd 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -200,11 +200,7 @@
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
- bp2BuildCtx := bp2buildBlueprintWrapContext{
- bpCtx: ctx.Context.Context,
- }
-
- bazelTargets := GenerateSoongModuleTargets(&bp2BuildCtx)[dir]
+ bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, false)[dir]
if g, w := len(bazelTargets), 1; g != w {
t.Fatalf("Expected %d bazel target, got %d", w, g)
}
@@ -214,7 +210,123 @@
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
testCase.expectedBazelTarget,
- actualBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ }
+}
+
+func TestGenerateBazelTargetModules(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ bp: `custom {
+ name: "foo",
+ string_list_prop: ["a", "b"],
+ string_prop: "a",
+}`,
+ expectedBazelTarget: `custom(
+ name = "foo",
+ string_list_prop = [
+ "a",
+ "b",
+ ],
+ string_prop = "a",
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, true)[dir]
+ if g, w := len(bazelTargets), 1; g != w {
+ t.Fatalf("Expected %d bazel target, got %d", w, g)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ }
+}
+
+func TestModuleTypeBp2Build(t *testing.T) {
+ testCases := []struct {
+ moduleTypeUnderTest string
+ moduleTypeUnderTestFactory android.ModuleFactory
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ bp: `filegroup {
+ name: "foo",
+ srcs: [],
+}`,
+ expectedBazelTarget: `filegroup(
+ name = "foo",
+ srcs = [
+ ],
+)`,
+ },
+ {
+ moduleTypeUnderTest: "filegroup",
+ moduleTypeUnderTestFactory: android.FileGroupFactory,
+ bp: `filegroup {
+ name: "foo",
+ srcs: ["a", "b"],
+}`,
+ expectedBazelTarget: `filegroup(
+ name = "foo",
+ srcs = [
+ "a",
+ "b",
+ ],
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+ ctx.RegisterForBazelConversion()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.ResolveDependencies(config)
+ android.FailIfErrored(t, errs)
+
+ bazelTargets := GenerateSoongModuleTargets(ctx.Context.Context, true)[dir]
+ if g, w := len(bazelTargets), 1; g != w {
+ t.Fatalf("Expected %d bazel target, got %d", w, g)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
)
}
}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 8bea3f6..01c7271 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -172,7 +172,7 @@
content: "irrelevant",
},
}
- files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget))
+ files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget), false)
var actualSoongModuleBzl BazelFile
for _, f := range files {
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index cdfb38b..cccc474 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -17,7 +17,8 @@
func CreateBazelFiles(
ruleShims map[string]RuleShim,
- buildToTargets map[string][]BazelTarget) []BazelFile {
+ buildToTargets map[string][]BazelTarget,
+ bp2buildEnabled bool) []BazelFile {
files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
// Write top level files: WORKSPACE and BUILD. These files are empty.
@@ -26,22 +27,30 @@
files = append(files, newFile("", "BUILD", ""))
files = append(files, newFile(bazelRulesSubDir, "BUILD", ""))
- files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
- for bzlFileName, ruleShim := range ruleShims {
- files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+ if !bp2buildEnabled {
+ // These files are only used for queryview.
+ files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
+
+ for bzlFileName, ruleShim := range ruleShims {
+ files = append(files, newFile(bazelRulesSubDir, bzlFileName+".bzl", ruleShim.content))
+ }
+ files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
}
- files = append(files, newFile(bazelRulesSubDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims)))
- files = append(files, createBuildFiles(buildToTargets)...)
+ files = append(files, createBuildFiles(buildToTargets, bp2buildEnabled)...)
return files
}
-func createBuildFiles(buildToTargets map[string][]BazelTarget) []BazelFile {
+func createBuildFiles(buildToTargets map[string][]BazelTarget, bp2buildEnabled bool) []BazelFile {
files := make([]BazelFile, 0, len(buildToTargets))
for _, dir := range android.SortedStringKeys(buildToTargets) {
content := soongModuleLoad
+ if bp2buildEnabled {
+ // No need to load soong_module for bp2build BUILD files.
+ content = ""
+ }
targets := buildToTargets[dir]
sort.Slice(targets, func(i, j int) bool { return targets[i].name < targets[j].name })
for _, t := range targets {
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index a38fa6a..b40aa1b 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -19,12 +19,44 @@
"testing"
)
-func TestCreateBazelFiles_AddsTopLevelFiles(t *testing.T) {
- files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{})
- expectedFilePaths := []struct {
- dir string
- basename string
- }{
+type filepath struct {
+ dir string
+ basename string
+}
+
+func assertFilecountsAreEqual(t *testing.T, actual []BazelFile, expected []filepath) {
+ if a, e := len(actual), len(expected); a != e {
+ t.Errorf("Expected %d files, got %d", e, a)
+ }
+}
+
+func assertFileContent(t *testing.T, actual []BazelFile, expected []filepath) {
+ for i := range actual {
+ if g, w := actual[i], expected[i]; g.Dir != w.dir || g.Basename != w.basename {
+ t.Errorf("Did not find expected file %s/%s", g.Dir, g.Basename)
+ } else if g.Basename == "BUILD" || g.Basename == "WORKSPACE" {
+ if g.Contents != "" {
+ t.Errorf("Expected %s to have no content.", g)
+ }
+ } else if g.Contents == "" {
+ t.Errorf("Contents of %s unexpected empty.", g)
+ }
+ }
+}
+
+func sortFiles(files []BazelFile) {
+ sort.Slice(files, func(i, j int) bool {
+ if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
+ return files[i].Basename < files[j].Basename
+ } else {
+ return dir1 < dir2
+ }
+ })
+}
+
+func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) {
+ files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{}, false)
+ expectedFilePaths := []filepath{
{
dir: "",
basename: "BUILD",
@@ -47,27 +79,29 @@
},
}
- if g, w := len(files), len(expectedFilePaths); g != w {
- t.Errorf("Expected %d files, got %d", w, g)
+ assertFilecountsAreEqual(t, files, expectedFilePaths)
+ sortFiles(files)
+ assertFileContent(t, files, expectedFilePaths)
+}
+
+func TestCreateBazelFiles_Bp2Build_AddsTopLevelFiles(t *testing.T) {
+ files := CreateBazelFiles(map[string]RuleShim{}, map[string][]BazelTarget{}, true)
+ expectedFilePaths := []filepath{
+ {
+ dir: "",
+ basename: "BUILD",
+ },
+ {
+ dir: "",
+ basename: "WORKSPACE",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "BUILD",
+ },
}
- sort.Slice(files, func(i, j int) bool {
- if dir1, dir2 := files[i].Dir, files[j].Dir; dir1 == dir2 {
- return files[i].Basename < files[j].Basename
- } else {
- return dir1 < dir2
- }
- })
-
- for i := range files {
- if g, w := files[i], expectedFilePaths[i]; g.Dir != w.dir || g.Basename != w.basename {
- t.Errorf("Did not find expected file %s/%s", g.Dir, g.Basename)
- } else if g.Basename == "BUILD" || g.Basename == "WORKSPACE" {
- if g.Contents != "" {
- t.Errorf("Expected %s to have no content.", g)
- }
- } else if g.Contents == "" {
- t.Errorf("Contents of %s unexpected empty.", g)
- }
- }
+ assertFilecountsAreEqual(t, files, expectedFilePaths)
+ sortFiles(files)
+ assertFileContent(t, files, expectedFilePaths)
}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 160412d..4c31d2d 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -2,8 +2,9 @@
import (
"android/soong/android"
+ "android/soong/bazel"
- "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
type nestedProps struct {
@@ -103,34 +104,36 @@
return m
}
-type bp2buildBlueprintWrapContext struct {
- bpCtx *blueprint.Context
+type customBazelModuleAttributes struct {
+ Name *string
+ String_prop string
+ String_list_prop []string
}
-func (ctx *bp2buildBlueprintWrapContext) ModuleName(module blueprint.Module) string {
- return ctx.bpCtx.ModuleName(module)
+type customBazelModule struct {
+ android.BazelTargetModuleBase
+ customBazelModuleAttributes
}
-func (ctx *bp2buildBlueprintWrapContext) ModuleDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleDir(module)
+func customBazelModuleFactory() android.Module {
+ module := &customBazelModule{}
+ module.AddProperties(&module.customBazelModuleAttributes)
+ android.InitBazelTargetModule(module)
+ return module
}
-func (ctx *bp2buildBlueprintWrapContext) ModuleSubDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleSubDir(module)
-}
+func (m *customBazelModule) Name() string { return m.BaseModuleName() }
+func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-func (ctx *bp2buildBlueprintWrapContext) ModuleType(module blueprint.Module) string {
- return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
- ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *bp2buildBlueprintWrapContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
- ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
- if aModule, ok := m.(android.Module); ok {
- visit(aModule)
- }
- })
+func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
+ if m, ok := ctx.Module().(*customModule); ok {
+ name := "__bp2build__" + m.Name()
+ ctx.CreateModule(customBazelModuleFactory, &customBazelModuleAttributes{
+ Name: proptools.StringPtr(name),
+ String_prop: m.props.String_prop,
+ String_list_prop: m.props.String_list_prop,
+ }, &bazel.BazelTargetModuleProperties{
+ Rule_class: "custom",
+ })
+ }
}
diff --git a/cc/cc.go b/cc/cc.go
index 90335b3..8755efe 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -256,11 +256,27 @@
// Deprecated. true is the default, false is invalid.
Clang *bool `android:"arch_variant"`
- // Minimum sdk version supported when compiling against the ndk. Setting this property causes
- // two variants to be built, one for the platform and one for apps.
+ // The API level that this module is built against. The APIs of this API level will be
+ // visible at build time, but use of any APIs newer than min_sdk_version will render the
+ // module unloadable on older devices. In the future it will be possible to weakly-link new
+ // APIs, making the behavior match Java: such modules will load on older devices, but
+ // calling new APIs on devices that do not support them will result in a crash.
+ //
+ // This property has the same behavior as sdk_version does for Java modules. For those
+ // familiar with Android Gradle, the property behaves similarly to how compileSdkVersion
+ // does for Java code.
+ //
+ // In addition, setting this property causes two variants to be built, one for the platform
+ // and one for apps.
Sdk_version *string
- // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+ // Minimum OS API level supported by this C or C++ module. This property becomes the value
+ // of the __ANDROID_API__ macro. When the C or C++ module is included in an APEX or an APK,
+ // this property is also used to ensure that the min_sdk_version of the containing module is
+ // not older (i.e. less) than this module's min_sdk_version. When not set, this property
+ // defaults to the value of sdk_version. When this is set to "apex_inherit", this tracks
+ // min_sdk_version of the containing APEX. When the module
+ // is not built for an APEX, "apex_inherit" defaults to sdk_version.
Min_sdk_version *string
// If true, always create an sdk variant and don't create a platform variant.
@@ -441,6 +457,8 @@
canUseSdk() bool
useSdk() bool
sdkVersion() string
+ minSdkVersion() string
+ isSdkVariant() bool
useVndk() bool
isNdk(config android.Config) bool
IsLlndk() bool
@@ -469,6 +487,7 @@
nativeCoverage() bool
directlyInAnyApex() bool
isPreventInstall() bool
+ isCfiAssemblySupportEnabled() bool
}
type ModuleContext interface {
@@ -1024,10 +1043,6 @@
c.AddProperties(feature.props()...)
}
- c.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, os android.OsType) bool {
- // Windows builds always prefer 32-bit
- return os == android.Windows
- })
android.InitAndroidArchModule(c, c.hod, c.multilib)
android.InitApexModule(c)
android.InitSdkAwareModule(c)
@@ -1058,7 +1073,8 @@
}
func (c *Module) canUseSdk() bool {
- return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
+ return c.Os() == android.Android && c.Target().NativeBridge == android.NativeBridgeDisabled &&
+ !c.UseVndk() && !c.InRamdisk() && !c.InRecovery() && !c.InVendorRamdisk()
}
func (c *Module) UseSdk() bool {
@@ -1243,6 +1259,11 @@
return c.kytheFiles
}
+func (c *Module) isCfiAssemblySupportEnabled() bool {
+ return c.sanitize != nil &&
+ Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
+}
+
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
@@ -1313,6 +1334,29 @@
return ""
}
+func (ctx *moduleContextImpl) minSdkVersion() string {
+ ver := ctx.mod.MinSdkVersion()
+ if ver == "apex_inherit" && !ctx.isForPlatform() {
+ ver = ctx.apexSdkVersion().String()
+ }
+ if ver == "apex_inherit" || ver == "" {
+ ver = ctx.sdkVersion()
+ }
+ // Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
+ sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
+ minSdkVersionInt, err2 := strconv.Atoi(ver)
+ if err == nil && err2 == nil {
+ if sdkVersionInt < minSdkVersionInt {
+ return strconv.Itoa(sdkVersionInt)
+ }
+ }
+ return ver
+}
+
+func (ctx *moduleContextImpl) isSdkVariant() bool {
+ return ctx.mod.IsSdkVariant()
+}
+
func (ctx *moduleContextImpl) useVndk() bool {
return ctx.mod.UseVndk()
}
@@ -1408,6 +1452,10 @@
return ctx.mod.Properties.PreventInstall
}
+func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
+ return ctx.mod.isCfiAssemblySupportEnabled()
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f422839..d0d8759 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1266,6 +1266,95 @@
}
}
+func TestVendorSnapshotDirected(t *testing.T) {
+ bp := `
+ cc_library_shared {
+ name: "libvendor",
+ vendor: true,
+ nocrt: true,
+ }
+
+ cc_library_shared {
+ name: "libvendor_available",
+ vendor_available: true,
+ nocrt: true,
+ }
+
+ genrule {
+ name: "libfoo_gen",
+ cmd: "",
+ out: ["libfoo.so"],
+ }
+
+ cc_prebuilt_library_shared {
+ name: "libfoo",
+ vendor: true,
+ prefer: true,
+ srcs: [":libfoo_gen"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ vendor: true,
+ nocrt: true,
+ }
+`
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ config.TestProductVariables.DirectedVendorSnapshot = true
+ config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+ config.TestProductVariables.VendorSnapshotModules["libvendor"] = true
+ config.TestProductVariables.VendorSnapshotModules["libfoo"] = true
+ ctx := testCcWithConfig(t, config)
+
+ // Check Vendor snapshot output.
+
+ snapshotDir := "vendor-snapshot"
+ snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ []string{"arm", "armv7-a-neon"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+ // Check that snapshot captures "prefer: true" prebuilt
+ checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+ // Excluded modules
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+
+ // Verify that each json file for an excluded module has no rule.
+ for _, jsonFile := range excludeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+ t.Errorf("exclude json file %q found", jsonFile)
+ }
+ }
+}
+
func TestVendorSnapshotUse(t *testing.T) {
frameworkBp := `
cc_library {
@@ -1535,17 +1624,19 @@
assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
}
-func assertExcludeFromVendorSnapshotIs(t *testing.T, c *Module, expected bool) {
+func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
t.Helper()
- if c.ExcludeFromVendorSnapshot() != expected {
- t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", c.String(), expected)
+ m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+ if m.ExcludeFromVendorSnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
}
}
-func assertExcludeFromRecoverySnapshotIs(t *testing.T, c *Module, expected bool) {
+func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
t.Helper()
- if c.ExcludeFromRecoverySnapshot() != expected {
- t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", c.String(), expected)
+ m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
+ if m.ExcludeFromRecoverySnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
}
}
@@ -1569,6 +1660,12 @@
vendor: true,
exclude_from_vendor_snapshot: true,
}
+ cc_library_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.cpp"],
+ vendor_available: true,
+ exclude_from_vendor_snapshot: true,
+ }
`
vendorProprietaryBp := `
@@ -1602,13 +1699,13 @@
android.FailIfErrored(t, errs)
// Test an include and exclude framework module.
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", coreVariant).Module().(*Module), false)
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libinclude", vendorVariant).Module().(*Module), false)
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libexclude", vendorVariant).Module().(*Module), true)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true)
// A vendor module is excluded, but by its path, not the
// exclude_from_vendor_snapshot property.
- assertExcludeFromVendorSnapshotIs(t, ctx.ModuleForTests("libvendor", vendorVariant).Module().(*Module), false)
+ assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
// Verify the content of the vendor snapshot.
@@ -1639,6 +1736,8 @@
excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
}
// Verify that each json file for an included module has a rule.
@@ -1700,53 +1799,6 @@
})
}
-func TestVendorSnapshotExcludeWithVendorAvailable(t *testing.T) {
-
- // This test verifies that using the exclude_from_vendor_snapshot
- // property on a module that is vendor available generates an error. A
- // vendor available module must be captured in the vendor snapshot and
- // must not built from source when building the vendor image against
- // the vendor snapshot.
-
- frameworkBp := `
- cc_library_shared {
- name: "libinclude",
- srcs: ["src/include.cpp"],
- vendor_available: true,
- exclude_from_vendor_snapshot: true,
- }
- `
-
- depsBp := GatherRequiredDepsForTest(android.Android)
-
- mockFS := map[string][]byte{
- "deps/Android.bp": []byte(depsBp),
- "framework/Android.bp": []byte(frameworkBp),
- "framework/include.cpp": nil,
- }
-
- config := TestConfig(buildDir, android.Android, nil, "", mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext(config)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp"})
- android.FailIfErrored(t, errs)
-
- _, errs = ctx.PrepareBuildActions(config)
- android.CheckErrorsAgainstExpectations(t, errs, []string{
- `module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm64_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- `module "libinclude\{.+,image:vendor.+,arch:arm_.+\}" may not use both "vendor_available: true" and "exclude_from_vendor_snapshot: true"`,
- })
-}
-
func TestRecoverySnapshotCapture(t *testing.T) {
bp := `
cc_library {
@@ -1884,7 +1936,7 @@
cc_library_shared {
name: "libinclude",
srcs: ["src/include.cpp"],
- recovery_available: true,
+ recovery_available: true,
}
cc_library_shared {
name: "libexclude",
@@ -1892,12 +1944,18 @@
recovery: true,
exclude_from_recovery_snapshot: true,
}
+ cc_library_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.cpp"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ }
`
vendorProprietaryBp := `
cc_library_shared {
- name: "libvendor",
- srcs: ["vendor.cpp"],
+ name: "librecovery",
+ srcs: ["recovery.cpp"],
recovery: true,
}
`
@@ -1910,7 +1968,7 @@
"framework/include.cpp": nil,
"framework/exclude.cpp": nil,
"device/Android.bp": []byte(vendorProprietaryBp),
- "device/vendor.cpp": nil,
+ "device/recovery.cpp": nil,
}
config := TestConfig(buildDir, android.Android, nil, "", mockFS)
@@ -1925,13 +1983,13 @@
android.FailIfErrored(t, errs)
// Test an include and exclude framework module.
- assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libinclude", coreVariant).Module().(*Module), false)
- assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libinclude", recoveryVariant).Module().(*Module), false)
- assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libexclude", recoveryVariant).Module().(*Module), true)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
- // A vendor module is excluded, but by its path, not the
+ // A recovery module is excluded, but by its path, not the
// exclude_from_recovery_snapshot property.
- assertExcludeFromRecoverySnapshotIs(t, ctx.ModuleForTests("libvendor", recoveryVariant).Module().(*Module), false)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
// Verify the content of the recovery snapshot.
@@ -1959,8 +2017,10 @@
// Excluded modules
checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
- checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
- excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
}
// Verify that each json file for an included module has a rule.
@@ -4454,137 +4514,309 @@
}
}
-func checkHasImplicitDep(t *testing.T, m android.TestingModule, name string) {
- implicits := m.Rule("ld").Implicits
- for _, lib := range implicits {
- if strings.Contains(lib.Rel(), name) {
- return
- }
- }
+type MemtagNoteType int
- t.Errorf("%q is not found in implicit deps of module %q", name, m.Module().(*Module).Name())
-}
+const (
+ None MemtagNoteType = iota + 1
+ Sync
+ Async
+)
-func checkDoesNotHaveImplicitDep(t *testing.T, m android.TestingModule, name string) {
- implicits := m.Rule("ld").Implicits
- for _, lib := range implicits {
- if strings.Contains(lib.Rel(), name) {
- t.Errorf("%q is found in implicit deps of module %q", name, m.Module().(*Module).Name())
- }
+func (t MemtagNoteType) str() string {
+ switch t {
+ case None:
+ return "none"
+ case Sync:
+ return "sync"
+ case Async:
+ return "async"
+ default:
+ panic("invalid note type")
}
}
-func TestSanitizeMemtagHeap(t *testing.T) {
- rootBp := `
- cc_library_static {
- name: "libstatic",
- sanitize: { memtag_heap: true },
- }
+func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+ note_async := "note_memtag_heap_async"
+ note_sync := "note_memtag_heap_sync"
- cc_library_shared {
- name: "libshared",
- sanitize: { memtag_heap: true },
+ found := None
+ implicits := m.Rule("ld").Implicits
+ for _, lib := range implicits {
+ if strings.Contains(lib.Rel(), note_async) {
+ found = Async
+ break
+ } else if strings.Contains(lib.Rel(), note_sync) {
+ found = Sync
+ break
}
+ }
- cc_library {
- name: "libboth",
- sanitize: { memtag_heap: true },
- }
+ if found != expected {
+ t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+ }
+}
- cc_binary {
- name: "binary",
- shared_libs: [ "libshared" ],
- static_libs: [ "libstatic" ],
- }
-
- cc_binary {
- name: "binary_true",
- sanitize: { memtag_heap: true },
- }
-
- cc_binary {
- name: "binary_true_sync",
- sanitize: { memtag_heap: true, diag: { memtag_heap: true }, },
- }
-
- cc_binary {
- name: "binary_false",
- sanitize: { memtag_heap: false },
- }
-
+func makeMemtagTestConfig(t *testing.T) android.Config {
+ templateBp := `
cc_test {
- name: "test",
+ name: "%[1]s_test",
gtest: false,
}
cc_test {
- name: "test_true",
- gtest: false,
- sanitize: { memtag_heap: true },
- }
-
- cc_test {
- name: "test_false",
+ name: "%[1]s_test_false",
gtest: false,
sanitize: { memtag_heap: false },
}
cc_test {
- name: "test_true_async",
+ name: "%[1]s_test_true",
+ gtest: false,
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_test {
+ name: "%[1]s_test_true_nodiag",
gtest: false,
sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
}
- `
+ cc_test {
+ name: "%[1]s_test_true_diag",
+ gtest: false,
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
+ }
- subdirAsyncBp := `
cc_binary {
- name: "binary_async",
+ name: "%[1]s_binary",
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_false",
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true",
+ sanitize: { memtag_heap: true },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_nodiag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: false } },
+ }
+
+ cc_binary {
+ name: "%[1]s_binary_true_diag",
+ sanitize: { memtag_heap: true, diag: { memtag_heap: true } },
}
`
-
- subdirSyncBp := `
- cc_binary {
- name: "binary_sync",
- }
- `
+ subdirDefaultBp := fmt.Sprintf(templateBp, "default")
+ subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
+ subdirSyncBp := fmt.Sprintf(templateBp, "sync")
+ subdirAsyncBp := fmt.Sprintf(templateBp, "async")
mockFS := map[string][]byte{
- "subdir_async/Android.bp": []byte(subdirAsyncBp),
- "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_default/Android.bp": []byte(subdirDefaultBp),
+ "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
}
- config := TestConfig(buildDir, android.Android, nil, rootBp, mockFS)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
- config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+ return TestConfig(buildDir, android.Android, nil, "", mockFS)
+}
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ config := makeMemtagTestConfig(t)
+ config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+ config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
ctx := CreateTestContext(config)
ctx.Register()
- _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
variant := "android_arm64_armv8-a"
- note_async := "note_memtag_heap_async"
- note_sync := "note_memtag_heap_sync"
- note_any := "note_memtag_"
- checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared"), note_any)
- checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("libboth", "android_arm64_armv8-a_shared"), note_any)
+ config := makeMemtagTestConfig(t)
+ config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+ config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+ config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+ config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
+ ctx := CreateTestContext(config)
+ ctx.Register()
- checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary", variant), note_any)
- checkHasImplicitDep(t, ctx.ModuleForTests("binary_true", variant), note_async)
- checkHasImplicitDep(t, ctx.ModuleForTests("binary_true_sync", variant), note_sync)
- checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("binary_false", variant), note_any)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
- checkHasImplicitDep(t, ctx.ModuleForTests("test", variant), note_sync)
- checkHasImplicitDep(t, ctx.ModuleForTests("test_true", variant), note_async)
- checkDoesNotHaveImplicitDep(t, ctx.ModuleForTests("test_false", variant), note_any)
- checkHasImplicitDep(t, ctx.ModuleForTests("test_true_async", variant), note_async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
- checkHasImplicitDep(t, ctx.ModuleForTests("binary_async", variant), note_async)
- checkHasImplicitDep(t, ctx.ModuleForTests("binary_sync", variant), note_sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+ variant := "android_arm64_armv8-a"
+
+ config := makeMemtagTestConfig(t)
+ config.TestProductVariables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
+ config.TestProductVariables.MemtagHeapSyncIncludePaths = []string{"subdir_sync"}
+ config.TestProductVariables.MemtagHeapAsyncIncludePaths = []string{"subdir_async"}
+ config.TestProductVariables.SanitizeDevice = []string{"memtag_heap"}
+ config.TestProductVariables.SanitizeDeviceDiag = []string{"memtag_heap"}
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp", "subdir_default/Android.bp", "subdir_exclude/Android.bp", "subdir_sync/Android.bp", "subdir_async/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
+
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
+ checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
}
diff --git a/cc/compiler.go b/cc/compiler.go
index eb2b566..5f30d3d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -404,7 +404,12 @@
target := "-target " + tc.ClangTriple()
if ctx.Os().Class == android.Device {
- version := ctx.sdkVersion()
+ // When built for the non-updateble part of platform, minSdkVersion doesn't matter.
+ // It matters only when building we are building for modules that can be unbundled.
+ version := "current"
+ if !ctx.isForPlatform() || ctx.isSdkVariant() {
+ version = ctx.minSdkVersion()
+ }
if version == "" || version == "current" {
target += strconv.Itoa(android.FutureApiLevelInt)
} else {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 3282958..35dd10f 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -141,6 +141,13 @@
// Warnings from clang-10
// Nested and array designated initialization is nice to have.
"-Wno-c99-designator",
+
+ // Calls to the APIs that are newer than the min sdk version of the caller should be
+ // guarded with __builtin_available.
+ "-Wunguarded-availability",
+ // This macro allows the bionic versioning.h to indirectly determine whether the
+ // option -Wunguarded-availability is on or not.
+ "-D__ANDROID_UNGUARDED_AVAILABILITY__",
}, " "))
pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
diff --git a/cc/config/global.go b/cc/config/global.go
index d47e0ce..c62f784 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -53,6 +53,7 @@
"-Werror=date-time",
"-Werror=pragma-pack",
"-Werror=pragma-pack-suspicious-include",
+ "-Werror=string-plus-int",
"-Werror=unreachable-code-loop-increment",
}
@@ -90,9 +91,12 @@
"-Wl,--warn-shared-textrel",
"-Wl,--fatal-warnings",
"-Wl,--no-undefined-version",
+ // TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
+ // --exclude-libs arguments can be removed.
"-Wl,--exclude-libs,libgcc.a",
"-Wl,--exclude-libs,libgcc_stripped.a",
"-Wl,--exclude-libs,libunwind_llvm.a",
+ "-Wl,--exclude-libs,libunwind.a",
}
deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
@@ -113,8 +117,13 @@
}
noOverrideGlobalCflags = []string{
+ "-Werror=bool-operation",
+ "-Werror=implicit-int-float-conversion",
+ "-Werror=int-in-bool-context",
"-Werror=int-to-pointer-cast",
"-Werror=pointer-to-int-cast",
+ "-Werror=string-compare",
+ "-Werror=xor-used-as-pow",
// http://b/161386391 for -Wno-void-pointer-to-enum-cast
"-Wno-void-pointer-to-enum-cast",
// http://b/161386391 for -Wno-void-pointer-to-int-cast
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index b3a86f3..d5d01b4 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -29,15 +29,12 @@
if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
return override
}
- return strings.Join([]string{
+ checks := strings.Join([]string{
"-*",
"abseil-*",
"android-*",
"bugprone-*",
"cert-*",
- // clang-analyzer-* check is slow and enables clang-diagnostic-padded,
- // which cannot be suppressed yet.
- // "clang-analyzer-*",
"clang-diagnostic-unused-command-line-argument",
"google-*",
"misc-*",
@@ -63,6 +60,14 @@
// -readability-*
// -zircon-*
}, ",")
+ // clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
+ // nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+ // Some test code have clang-diagnostic-padded warnings that cannot be
+ // suppressed, but only by disabling clang-analyzer-optin.performance.*.
+ if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
+ checks += ",clang-analyzer-*,-clang-analyzer-optin.performance.*"
+ }
+ return checks
})
// There are too many clang-tidy warnings in external and vendor projects.
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 286bb53..107ae7d 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -35,6 +35,7 @@
"android.hardware.security.sharedsecret-ndk_platform",
"android.hardware.security.sharedsecret-unstable-ndk_platform",
"android.hardware.vibrator-ndk_platform",
+ "android.hardware.weaver-unstable-ndk_platform",
"android.system.keystore2-unstable-ndk_platform",
"libbinder",
"libcrypto",
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index d7ff580..1035df3 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -67,6 +67,7 @@
"10.14",
"10.15",
"11.0",
+ "11.1",
}
darwinAvailableLibraries = append(
diff --git a/cc/coverage.go b/cc/coverage.go
index acf98dd..5b5ccf2 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -58,6 +58,8 @@
func getClangProfileLibraryName(ctx ModuleContextIntf) string {
if ctx.useSdk() {
return "libprofile-clang-extras_ndk"
+ } else if ctx.isCfiAssemblySupportEnabled() {
+ return "libprofile-clang-extras_cfi_support"
} else {
return "libprofile-clang-extras"
}
diff --git a/cc/library.go b/cc/library.go
index bc6ff69..af9aaca 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1318,17 +1318,13 @@
if library.baseCompiler.hasSrcExt(".sysprop") {
dir := android.PathForModuleGen(ctx, "sysprop", "include")
if library.Properties.Sysprop.Platform != nil {
- isClientProduct := ctx.ProductSpecific() && !ctx.useVndk()
- isClientVendor := ctx.useVndk()
isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
// If the owner is different from the user, expose public header. That is,
// 1) if the user is product (as owner can only be platform / vendor)
- // 2) if one is platform and the other is vendor
- // Exceptions are ramdisk and recovery. They are not enforced at all. So
- // they always use internal header.
- if !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() &&
- (isClientProduct || (isOwnerPlatform == isClientVendor)) {
+ // 2) if the owner is platform and the client is vendor
+ // We don't care Platform -> Vendor dependency as it's already forbidden.
+ if ctx.Device() && (ctx.ProductSpecific() || (isOwnerPlatform && ctx.inVendor())) {
dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
}
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index a5c43fe..10de889 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -354,6 +354,5 @@
func NdkLibraryFactory() android.Module {
module := newStubLibrary()
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
return module
}
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 793ab37..8d522d0 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -70,7 +70,6 @@
// ./prebuilts/ndk/current/platforms/android-<sdk_version>/arch-$(HOST_ARCH)/usr/lib/<NAME>.o.
func NdkPrebuiltObjectFactory() android.Module {
module := newBaseModule(android.DeviceSupported, android.MultilibBoth)
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
module.linker = &ndkPrebuiltObjectLinker{
objectLinker: objectLinker{
baseLinker: NewBaseLinker(nil),
@@ -150,7 +149,6 @@
module.Properties.AlwaysSdk = true
module.Properties.Sdk_version = StringPtr("current")
module.stl.Properties.Stl = StringPtr("none")
- module.ModuleBase.EnableNativeBridgeSupportByDefault()
return module.Init()
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index af17490..8eeb355 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -267,6 +267,12 @@
return
}
+ // cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
+ if ctx.testBinary() && s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+
var globalSanitizers []string
var globalSanitizersDiag []string
@@ -358,27 +364,29 @@
s.Diag.Cfi = boolPtr(true)
}
+ if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
+ s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
+
if len(globalSanitizersDiag) > 0 {
ctx.ModuleErrorf("unknown global sanitizer diagnostics option %s", globalSanitizersDiag[0])
}
}
- // cc_test targets default to SYNC MemTag.
- if ctx.testBinary() && s.Memtag_heap == nil {
- if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
- s.Memtag_heap = boolPtr(true)
- s.Diag.Memtag_heap = boolPtr(true)
- }
- }
-
// Enable Memtag for all components in the include paths (for Aarch64 only)
- if s.Memtag_heap == nil && ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Arch().ArchType == android.Arm64 {
if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
- s.Memtag_heap = boolPtr(true)
- s.Diag.Memtag_heap = boolPtr(true)
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ }
+ if s.Diag.Memtag_heap == nil {
+ s.Diag.Memtag_heap = boolPtr(true)
+ }
} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
- s.Memtag_heap = boolPtr(true)
- s.Diag.Memtag_heap = boolPtr(false)
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ }
}
}
diff --git a/cc/sdk.go b/cc/sdk.go
index ec57f06..2c3fec3 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -38,18 +38,31 @@
ctx.CreateVariations("sdk")
} else if m.UseSdk() || m.SplitPerApiLevel() {
modules := ctx.CreateVariations("", "sdk")
+
+ // Clear the sdk_version property for the platform (non-SDK) variant so later code
+ // doesn't get confused by it.
modules[0].(*Module).Properties.Sdk_version = nil
+
+ // Mark the SDK variant.
modules[1].(*Module).Properties.IsSdkVariant = true
if ctx.Config().UnbundledBuildApps() {
+ // For an unbundled apps build, hide the platform variant from Make.
modules[0].(*Module).Properties.HideFromMake = true
modules[0].(*Module).Properties.PreventInstall = true
} else {
+ // For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
+ // exposed to Make.
modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
modules[1].(*Module).Properties.PreventInstall = true
}
ctx.AliasVariation("")
} else {
+ if m, ok := ctx.Module().(*Module); ok {
+ // Clear the sdk_version property for modules that don't have an SDK variant so
+ // later code doesn't get confused by it.
+ m.Properties.Sdk_version = nil
+ }
ctx.CreateVariations("")
ctx.AliasVariation("")
}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 8979846..2003e03 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -40,10 +40,10 @@
// evalution of a function that may be not be defined.
inImage(m *Module) func() bool
- // Returns the value of the "available" property for a given module for
- // and snapshot, e.g., "vendor_available", "recovery_available", etc.
- // or nil if the property is not defined.
- available(m *Module) *bool
+ // Returns true if the module is private and must not be included in the
+ // snapshot. For example VNDK-private modules must return true for the
+ // vendor snapshots. But false for the recovery snapshots.
+ private(m *Module) bool
// Returns true if a dir under source tree is an SoC-owned proprietary
// directory, such as device/, vendor/, etc.
@@ -82,6 +82,11 @@
// Whether to skip the source mutator for a given module.
skipSourceMutator(ctx android.BottomUpMutatorContext) bool
+
+ // Whether to exclude a given module from the directed snapshot or not.
+ // If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
+ // and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
+ excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
}
type vendorSnapshotImage struct{}
@@ -107,8 +112,8 @@
return m.InVendor
}
-func (vendorSnapshotImage) available(m *Module) *bool {
- return m.VendorProperties.Vendor_available
+func (vendorSnapshotImage) private(m *Module) bool {
+ return m.IsVndkPrivate()
}
func (vendorSnapshotImage) isProprietaryPath(dir string) bool {
@@ -193,6 +198,16 @@
return false
}
+// returns true iff a given module SHOULD BE EXCLUDED, false if included
+func (vendorSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+ // If we're using full snapshot, not directed snapshot, capture every module
+ if !cfg.DirectedVendorSnapshot() {
+ return false
+ }
+ // Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
+ return !cfg.VendorSnapshotModules()[name]
+}
+
func (recoverySnapshotImage) init() {
android.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
android.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
@@ -212,8 +227,9 @@
return m.InRecovery
}
-func (recoverySnapshotImage) available(m *Module) *bool {
- return m.Properties.Recovery_available
+// recovery snapshot does not have private libraries.
+func (recoverySnapshotImage) private(m *Module) bool {
+ return false
}
func (recoverySnapshotImage) isProprietaryPath(dir string) bool {
@@ -275,6 +291,11 @@
return !ok || !module.InRecovery()
}
+func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+ // directed recovery snapshot is not implemented yet
+ return false
+}
+
var vendorSnapshotImageSingleton vendorSnapshotImage
var recoverySnapshotImageSingleton recoverySnapshotImage
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 3e6444b..c50ef45 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -80,7 +80,7 @@
}
for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
- if isSnapshotAware(m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
+ if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
return true
}
}
diff --git a/cc/stl.go b/cc/stl.go
index 406fa3a..75fab17 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -140,11 +140,7 @@
}
func staticUnwinder(ctx android.BaseModuleContext) string {
- if ctx.Arch().ArchType == android.Arm {
- return "libunwind_llvm"
- } else {
- return "libgcc_stripped"
- }
+ return "libunwind"
}
func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps {
@@ -192,6 +188,7 @@
if needsLibAndroidSupport(ctx) {
deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
}
+ // TODO: Switch the NDK over to the LLVM unwinder for non-arm32 architectures.
if ctx.Arch().ArchType == android.Arm {
deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
} else {
@@ -231,10 +228,6 @@
// Use Win32 threads in libc++.
"-D_LIBCPP_HAS_THREAD_API_WIN32")
}
- } else {
- if ctx.Arch().ArchType == android.Arm {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
- }
}
case "libstdc++":
// Nothing
diff --git a/cc/testing.go b/cc/testing.go
index 903f76c..dc9a59d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -110,6 +110,16 @@
}
toolchain_library {
+ name: "libunwind",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
+ }
+
+ toolchain_library {
name: "libclang_rt.fuzzer-arm-android",
vendor_available: true,
product_available: true,
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 0a89e47..7346aac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -23,8 +23,6 @@
"sort"
"strings"
- "github.com/google/blueprint/proptools"
-
"android/soong/android"
)
@@ -177,15 +175,15 @@
func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
- // Any module in a vendor proprietary path is a vendor proprietary
+ // Any module in a recovery proprietary path is a recovery proprietary
// module.
if isRecoveryProprietaryPath(ctx.ModuleDir()) {
return true
}
- // However if the module is not in a vendor proprietary path, it may
- // still be a vendor proprietary module. This happens for cc modules
- // that are excluded from the vendor snapshot, and it means that the
+ // However if the module is not in a recovery proprietary path, it may
+ // still be a recovery proprietary module. This happens for cc modules
+ // that are excluded from the recovery snapshot, and it means that the
// vendor has assumed control of the framework-provided module.
if c, ok := ctx.Module().(*Module); ok {
@@ -198,7 +196,7 @@
}
// Determines if the module is a candidate for snapshot.
-func isSnapshotAware(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
if !m.Enabled() || m.Properties.HideFromMake {
return false
}
@@ -241,6 +239,10 @@
if _, ok := m.linker.(*llndkHeadersDecorator); ok {
return false
}
+ // If we are using directed snapshot AND we have to exclude this module, skip this
+ if image.excludeFromDirectedSnapshot(cfg, m.BaseModuleName()) {
+ return false
+ }
// Libraries
if l, ok := m.linker.(snapshotLibraryInterface); ok {
@@ -260,7 +262,7 @@
}
}
if l.static() {
- return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+ return m.outputFile.Valid() && !image.private(m)
}
if l.shared() {
if !m.outputFile.Valid() {
@@ -278,7 +280,7 @@
// Binaries and Objects
if m.binary() || m.object() {
- return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+ return m.outputFile.Valid()
}
return false
@@ -522,20 +524,9 @@
ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
return
}
- if Bool(c.image.available(m)) {
- // Error: may not combine "vendor_available:
- // true" with "exclude_from_vendor_snapshot:
- // true".
- ctx.Errorf(
- "module %q may not use both \""+
- c.name+
- "_available: true\" and \"exclude_from_vendor_snapshot: true\"",
- m.String())
- return
- }
}
- if !isSnapshotAware(m, inProprietaryPath, apexInfo, c.image) {
+ if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
return
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 907bed3..1d5e7b3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -23,6 +23,7 @@
"github.com/google/blueprint/bootstrap"
"android/soong/android"
+ "android/soong/bp2build"
)
var (
@@ -54,18 +55,12 @@
// bazelConversionRequested checks that the user is intending to convert
// Blueprint to Bazel BUILD files.
func bazelConversionRequested(configuration android.Config) bool {
- return configuration.IsEnvTrue("CONVERT_TO_BAZEL")
+ return configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
}
-func newContext(srcDir string, configuration android.Config) *android.Context {
+func newContext(configuration android.Config) *android.Context {
ctx := android.NewContext(configuration)
- if bazelConversionRequested(configuration) {
- // Register an alternate set of singletons and mutators for bazel
- // conversion for Bazel conversion.
- ctx.RegisterForBazelConversion()
- } else {
- ctx.Register()
- }
+ ctx.Register()
if !shouldPrepareBuildActions(configuration) {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
@@ -100,13 +95,22 @@
// enabled even if it completed successfully.
extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
}
+
+ if bazelConversionRequested(configuration) {
+ // Run the alternate pipeline of bp2build mutators and singleton to convert Blueprint to BUILD files
+ // before everything else.
+ runBp2Build(configuration, extraNinjaDeps)
+ // Short-circuit and return.
+ return
+ }
+
if configuration.BazelContext.BazelEnabled() {
// Bazel-enabled mode. Soong runs in two passes.
// First pass: Analyze the build tree, but only store all bazel commands
// needed to correctly evaluate the tree in the second pass.
// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
// the incorrect results from the first pass, and file I/O is expensive.
- firstCtx := newContext(srcDir, configuration)
+ firstCtx := newContext(configuration)
configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
// Invoke bazel commands and save results for second pass.
@@ -120,10 +124,10 @@
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
- ctx = newContext(srcDir, secondPassConfig)
+ ctx = newContext(secondPassConfig)
bootstrap.Main(ctx.Context, secondPassConfig, extraNinjaDeps...)
} else {
- ctx = newContext(srcDir, configuration)
+ ctx = newContext(configuration)
bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
}
@@ -154,6 +158,22 @@
}
}
+// Run Soong in the bp2build mode. This creates a standalone context that registers
+// an alternate pipeline of mutators and singletons specifically for generating
+// Bazel BUILD files instead of Ninja files.
+func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ bp2buildCtx := android.NewContext(configuration)
+ bp2buildCtx.RegisterForBazelConversion()
+ configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
+ bp2buildCtx.SetNameInterface(newNameResolver(configuration))
+ bootstrap.Main(bp2buildCtx.Context, configuration, extraNinjaDeps...)
+
+ codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx)
+ bp2build.Codegen(codegenContext)
+}
+
// shouldPrepareBuildActions reads configuration and flags if build actions
// should be generated.
func shouldPrepareBuildActions(configuration android.Config) bool {
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 3657ea8..305a224 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -20,50 +20,13 @@
"io/ioutil"
"os"
"path/filepath"
-
- "github.com/google/blueprint"
)
-type queryviewContext struct {
- bpCtx *blueprint.Context
-}
-
-func (ctx *queryviewContext) ModuleName(module blueprint.Module) string {
- return ctx.bpCtx.ModuleName(module)
-}
-
-func (ctx *queryviewContext) ModuleDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleDir(module)
-}
-
-func (ctx *queryviewContext) ModuleSubDir(module blueprint.Module) string {
- return ctx.bpCtx.ModuleSubDir(module)
-}
-
-func (ctx *queryviewContext) ModuleType(module blueprint.Module) string {
- return ctx.bpCtx.ModuleType(module)
-}
-
-func (ctx *queryviewContext) VisitAllModulesBlueprint(visit func(blueprint.Module)) {
- ctx.bpCtx.VisitAllModules(visit)
-}
-
-func (ctx *queryviewContext) VisitDirectDeps(module android.Module, visit func(android.Module)) {
- ctx.bpCtx.VisitDirectDeps(module, func(m blueprint.Module) {
- if aModule, ok := m.(android.Module); ok {
- visit(aModule)
- }
- })
-}
-
func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
- qvCtx := queryviewContext{
- bpCtx: ctx.Context,
- }
ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
- buildToTargets := bp2build.GenerateSoongModuleTargets(&qvCtx)
+ buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx, false)
- filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets)
+ filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, false)
for _, f := range filesToWrite {
if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
return err
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index f52ecb4..867ece6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -27,8 +27,9 @@
// GlobalConfig stores the configuration for dex preopting. The fields are set
// from product variables via dex_preopt_config.mk.
type GlobalConfig struct {
- DisablePreopt bool // disable preopt for all modules
- DisablePreoptModules []string // modules with preopt disabled by product-specific config
+ DisablePreopt bool // disable preopt for all modules (excluding boot images)
+ DisablePreoptBootImages bool // disable prepot for boot images
+ DisablePreoptModules []string // modules with preopt disabled by product-specific config
OnlyPreoptBootImageAndSystemServer bool // only preopt jars in the boot image or system server
@@ -79,12 +80,10 @@
CpuVariant map[android.ArchType]string // cpu variant for each architecture
InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
- // Only used for boot image
- DirtyImageObjects android.OptionalPath // path to a dirty-image-objects file
- BootImageProfiles android.Paths // path to a boot-image-profile.txt file
- BootFlags string // extra flags to pass to dex2oat for the boot image
- Dex2oatImageXmx string // max heap size for dex2oat for the boot image
- Dex2oatImageXms string // initial heap size for dex2oat for the boot image
+ BootImageProfiles android.Paths // path to a boot-image-profile.txt file
+ BootFlags string // extra flags to pass to dex2oat for the boot image
+ Dex2oatImageXmx string // max heap size for dex2oat for the boot image
+ Dex2oatImageXms string // initial heap size for dex2oat for the boot image
}
// GlobalSoongConfig contains the global config that is generated from Soong,
@@ -178,7 +177,6 @@
// Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be
// used to construct the real value manually below.
- DirtyImageObjects string
BootImageProfiles []string
}
@@ -189,7 +187,6 @@
}
// Construct paths that require a PathContext.
- config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects))
config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles)
return config.GlobalConfig, nil
@@ -234,8 +231,9 @@
return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} {
// Nope, return a config with preopting disabled
return globalConfigAndRaw{&GlobalConfig{
- DisablePreopt: true,
- DisableGenerateProfile: true,
+ DisablePreopt: true,
+ DisablePreoptBootImages: true,
+ DisableGenerateProfile: true,
}, nil}
})
}).(globalConfigAndRaw)
@@ -305,7 +303,7 @@
}
}
-var dex2oatDepTag = struct {
+var Dex2oatDepTag = struct {
blueprint.BaseDependencyTag
}{}
@@ -316,7 +314,7 @@
func RegisterToolDeps(ctx android.BottomUpMutatorContext) {
dex2oatBin := dex2oatModuleName(ctx.Config())
v := ctx.Config().BuildOSTarget.Variations()
- ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin)
+ ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
}
func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
@@ -332,7 +330,7 @@
// prebuilt explicitly here instead.
var dex2oatModule android.Module
ctx.WalkDeps(func(child, parent android.Module) bool {
- if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == dex2oatDepTag {
+ if parent == ctx.Module() && ctx.OtherModuleDependencyTag(child) == Dex2oatDepTag {
// Found the source module, or prebuilt module that has replaced the source.
dex2oatModule = child
if p, ok := child.(android.PrebuiltInterface); ok && p.Prebuilt() != nil {
@@ -365,13 +363,6 @@
// createGlobalSoongConfig creates a GlobalSoongConfig from the current context.
// Should not be used in dexpreopt_gen.
func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig {
- if ctx.Config().TestProductVariables != nil {
- // If we're called in a test there'll be a confusing error from the path
- // functions below that gets reported without a stack trace, so let's panic
- // properly with a more helpful message.
- panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.")
- }
-
return &GlobalSoongConfig{
Profman: ctx.Config().HostToolPath(ctx, "profman"),
Dex2oat: dex2oatPathFromDep(ctx),
@@ -385,14 +376,13 @@
// The main reason for this Once cache for GlobalSoongConfig is to make the
// dex2oat path available to singletons. In ordinary modules we get it through a
-// dex2oatDepTag dependency, but in singletons there's no simple way to do the
+// Dex2oatDepTag dependency, but in singletons there's no simple way to do the
// same thing and ensure the right variant is selected, hence this cache to make
// the resolved path available to singletons. This means we depend on there
-// being at least one ordinary module with a dex2oatDepTag dependency.
+// being at least one ordinary module with a Dex2oatDepTag dependency.
//
// TODO(b/147613152): Implement a way to deal with dependencies from singletons,
-// and then possibly remove this cache altogether (but the use in
-// GlobalSoongConfigForTests also needs to be rethought).
+// and then possibly remove this cache altogether.
var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig")
// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called,
@@ -545,7 +535,6 @@
EmptyDirectory: "empty_dir",
CpuVariant: nil,
InstructionSetFeatures: nil,
- DirtyImageObjects: android.OptionalPath{},
BootImageProfiles: nil,
BootFlags: "",
Dex2oatImageXmx: "",
@@ -553,18 +542,14 @@
}
}
-func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig {
- // Install the test GlobalSoongConfig in the Once cache so that later calls to
- // Get(Cached)GlobalSoongConfig returns it without trying to create a real one.
- return config.Once(globalSoongConfigOnceKey, func() interface{} {
- return &GlobalSoongConfig{
- Profman: android.PathForTesting("profman"),
- Dex2oat: android.PathForTesting("dex2oat"),
- Aapt: android.PathForTesting("aapt"),
- SoongZip: android.PathForTesting("soong_zip"),
- Zip2zip: android.PathForTesting("zip2zip"),
- ManifestCheck: android.PathForTesting("manifest_check"),
- ConstructContext: android.PathForTesting("construct_context"),
- }
- }).(*GlobalSoongConfig)
+func globalSoongConfigForTests() *GlobalSoongConfig {
+ return &GlobalSoongConfig{
+ Profman: android.PathForTesting("profman"),
+ Dex2oat: android.PathForTesting("dex2oat"),
+ Aapt: android.PathForTesting("aapt"),
+ SoongZip: android.PathForTesting("soong_zip"),
+ Zip2zip: android.PathForTesting("zip2zip"),
+ ManifestCheck: android.PathForTesting("manifest_check"),
+ ConstructContext: android.PathForTesting("construct_context"),
+ }
}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 59278fd..af73d0c 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -61,7 +61,7 @@
func TestDexPreopt(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
@@ -83,7 +83,7 @@
func TestDexPreoptSystemOther(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
@@ -143,7 +143,7 @@
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
ctx := android.BuilderContextForTesting(config)
- globalSoong := GlobalSoongConfigForTests(config)
+ globalSoong := globalSoongConfigForTests()
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 2b3fbae..5ef4a90 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -20,6 +20,7 @@
"android/soong/android"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
@@ -30,10 +31,24 @@
android.ModuleBase
android.PackagingBase
+ properties filesystemProperties
+
output android.OutputPath
installDir android.InstallPath
}
+type filesystemProperties struct {
+ // When set to true, sign the image with avbtool. Default is false.
+ Use_avb *bool
+
+ // Path to the private key that avbtool will use to sign this filesystem image.
+ // TODO(jiyong): allow apex_key to be specified here
+ Avb_private_key *string `android:"path"`
+
+ // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096.
+ Avb_algorithm *string
+}
+
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
// image. The filesystem images are expected to be mounted in the target device, which means the
// modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
@@ -41,6 +56,7 @@
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
+ module.AddProperties(&module.properties)
android.InitPackageModule(module)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
@@ -72,21 +88,12 @@
FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
Input(zipFile)
- mkuserimg := ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")
- propFile := android.PathForModuleOut(ctx, "prop").OutputPath
- // TODO(jiyong): support more filesystem types other than ext4
- propsText := fmt.Sprintf(`mount_point=system\n`+
- `fs_type=ext4\n`+
- `use_dynamic_partition_size=true\n`+
- `ext_mkuserimg=%s\n`, mkuserimg.String())
- builder.Command().Text("echo").Flag("-e").Flag(`"` + propsText + `"`).
- Text(">").Output(propFile).
- Implicit(mkuserimg)
-
+ propFile, toolDeps := f.buildPropFile(ctx)
f.output = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
+ Implicits(toolDeps).
Output(f.output).
Text(rootDir.String()) // directory where to find fs_config_files|dirs
@@ -97,6 +104,56 @@
ctx.InstallFile(f.installDir, f.installFileName(), f.output)
}
+func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android.OutputPath, toolDeps android.Paths) {
+ type prop struct {
+ name string
+ value string
+ }
+
+ var props []prop
+ var deps android.Paths
+ addStr := func(name string, value string) {
+ props = append(props, prop{name, value})
+ }
+ addPath := func(name string, path android.Path) {
+ props = append(props, prop{name, path.String()})
+ deps = append(deps, path)
+ }
+
+ // TODO(jiyong): support more filesystem types other than ext4
+ addStr("fs_type", "ext4")
+ addStr("mount_point", "system")
+ addStr("use_dynamic_partition_size", "true")
+ addPath("ext_mkuserimg", ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs"))
+ // b/177813163 deps of the host tools have to be added. Remove this.
+ for _, t := range []string{"mke2fs", "e2fsdroid", "tune2fs"} {
+ deps = append(deps, ctx.Config().HostToolPath(ctx, t))
+ }
+
+ if proptools.Bool(f.properties.Use_avb) {
+ addStr("avb_hashtree_enable", "true")
+ addPath("avb_avbtool", ctx.Config().HostToolPath(ctx, "avbtool"))
+ algorithm := proptools.StringDefault(f.properties.Avb_algorithm, "SHA256_RSA4096")
+ addStr("avb_algorithm", algorithm)
+ key := android.PathForModuleSrc(ctx, proptools.String(f.properties.Avb_private_key))
+ addPath("avb_key_path", key)
+ addStr("avb_add_hashtree_footer_args", "--do_not_generate_fec")
+ addStr("partition_name", f.Name())
+ }
+
+ propFile = android.PathForModuleOut(ctx, "prop").OutputPath
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("rm").Flag("-rf").Output(propFile)
+ for _, p := range props {
+ builder.Command().
+ Text("echo").Flag("-e").
+ Flag(`"` + p.name + "=" + p.value + `"`).
+ Text(">>").Output(propFile)
+ }
+ builder.Build("build_filesystem_prop", fmt.Sprintf("Creating filesystem props for %s", f.BaseModuleName()))
+ return propFile, deps
+}
+
var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
// Implements android.AndroidMkEntriesProvider
diff --git a/java/androidmk.go b/java/androidmk.go
index aaad44f..cc454b0 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -196,6 +196,9 @@
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(entries *android.AndroidMkEntries) {
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
+ if prebuilt.dexJarFile != nil {
+ entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+ }
entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 233e9d5..e2647cf 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -166,3 +166,25 @@
}
}
}
+
+func TestImportSoongDexJar(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_import {
+ name: "my-java-import",
+ jars: ["a.jar"],
+ prefer: true,
+ compile_dex: true,
+ }
+ `)
+
+ mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
+ entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+ expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+ actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
+
+ if len(actualSoongDexJar) != 1 {
+ t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
+ } else if actualSoongDexJar[0] != expectedSoongDexJar {
+ t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
+ }
+}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 062005b..004cbbb 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -25,6 +25,18 @@
"github.com/google/blueprint/proptools"
)
+// =================================================================================================
+// WIP - see http://b/177892522 for details
+//
+// The build support for boot images is currently being migrated away from singleton to modules so
+// the documentation may not be strictly accurate. Rather than update the documentation at every
+// step which will create a lot of churn the changes that have been made will be listed here and the
+// documentation will be updated once it is closer to the final result.
+//
+// Changes:
+// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// =================================================================================================
+
// This comment describes:
// 1. ART boot images in general (their types, structure, file layout, etc.)
// 2. build system support for boot images
@@ -124,7 +136,7 @@
// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
-// Core libraries.
+// core libraries as they are already part of the primary ART boot image.
//
// 2.1. Libraries that go in the boot images
// -----------------------------------------
@@ -339,20 +351,24 @@
return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
}
-func dexpreoptBootJarsFactory() android.Singleton {
- return &dexpreoptBootJars{}
+func dexpreoptBootJarsFactory() android.SingletonModule {
+ m := &dexpreoptBootJars{}
+ android.InitAndroidModule(m)
+ return m
}
func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
- ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+ ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
}
-func skipDexpreoptBootJars(ctx android.PathContext) bool {
- return dexpreopt.GetGlobalConfig(ctx).DisablePreopt
+func SkipDexpreoptBootJars(ctx android.PathContext) bool {
+ return dexpreopt.GetGlobalConfig(ctx).DisablePreoptBootImages
}
-// Singleton for generating boot image build rules.
+// Singleton module for generating boot image build rules.
type dexpreoptBootJars struct {
+ android.SingletonModuleBase
+
// Default boot image config (currently always the Framework boot image extension). It should be
// noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
// but the switch is handled not here, but in the makefiles (triggered with
@@ -371,7 +387,7 @@
// Accessor function for the apex package. Returns nil if dexpreopt is disabled.
func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
- if skipDexpreoptBootJars(ctx) {
+ if SkipDexpreoptBootJars(ctx) {
return nil
}
// Include dexpreopt files for the primary boot image.
@@ -385,9 +401,16 @@
return files
}
+// Provide paths to boot images for use by modules that depend upon them.
+//
+// The build rules are created in GenerateSingletonBuildActions().
+func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Placeholder for now.
+}
+
// Generate build rules for boot images.
-func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
- if skipDexpreoptBootJars(ctx) {
+func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ if SkipDexpreoptBootJars(ctx) {
return
}
if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
@@ -422,16 +445,19 @@
// Note that the same jar may occur in multiple modules.
// This logic is tested in the apex package to avoid import cycle apex <-> java.
func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
- // Ignore any module that is not listed in the boot image configuration.
name := ctx.ModuleName(module)
+
+ // Strip a prebuilt_ prefix so that this can access the dex jar from a prebuilt module.
+ name = android.RemoveOptionalPrebuiltPrefix(name)
+
+ // Ignore any module that is not listed in the boot image configuration.
index := image.modules.IndexOfJar(name)
if index == -1 {
return -1, nil
}
- // It is an error if a module configured in the boot image does not support
- // accessing the dex jar. This is safe because every module that has the same
- // name has to have the same module type.
+ // It is an error if a module configured in the boot image does not support accessing the dex jar.
+ // This is safe because every module that has the same name has to have the same module type.
jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
if !hasJar {
ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
@@ -622,8 +648,10 @@
cmd.FlagWithInput("--profile-file=", profile)
}
- if global.DirtyImageObjects.Valid() {
- cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path())
+ dirtyImageFile := "frameworks/base/config/dirty-image-objects"
+ dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
+ if dirtyImagePath.Valid() {
+ cmd.FlagWithInput("--dirty-image-objects=", dirtyImagePath.Path())
}
if image.extends != nil {
@@ -724,7 +752,7 @@
globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx)
global := dexpreopt.GetGlobalConfig(ctx)
- if global.DisableGenerateProfile || ctx.Config().UnbundledBuild() {
+ if global.DisableGenerateProfile {
return nil
}
profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 95fe5e1..48bc244 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -52,7 +52,6 @@
dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
ctx := testContext(config)
- RegisterDexpreoptBootJarsComponents(ctx)
run(t, ctx, config)
dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index d302524..4bd255c 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -67,6 +67,19 @@
stubFlagsRule(ctx)
+ // If there is a prebuilt hiddenapi dir, generate rules to use the
+ // files within. Generally, we build the hiddenapi files from source
+ // during the build, ensuring consistency. It's possible, in a split
+ // build (framework and vendor) scenario, for the vendor build to use
+ // prebuilt hiddenapi files from the framework build. In this scenario,
+ // the framework and vendor builds must use the same source to ensure
+ // consistency.
+
+ if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+ h.flags = prebuiltFlagsRule(ctx)
+ return
+ }
+
// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
if ctx.Config().FrameworksBaseDirExists(ctx) {
h.flags = flagsRule(ctx)
@@ -212,6 +225,19 @@
rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
}
+func prebuiltFlagsRule(ctx android.SingletonContext) android.Path {
+ outputPath := hiddenAPISingletonPaths(ctx).flags
+ inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
+
+ return outputPath
+}
+
// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
// the unsupported API.
func flagsRule(ctx android.SingletonContext) android.Path {
@@ -391,6 +417,20 @@
return
}
+ if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+ outputPath := hiddenAPISingletonPaths(ctx).index
+ inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
+
+ h.index = outputPath
+ return
+ }
+
indexes := android.Paths{}
ctx.VisitAllModules(func(module android.Module) {
if h, ok := module.(hiddenAPIIntf); ok {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 955c739..0f9ef58 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -23,9 +23,10 @@
"github.com/google/blueprint/proptools"
)
-func testConfigWithBootJars(bp string, bootJars []string) android.Config {
+func testConfigWithBootJars(bp string, bootJars []string, prebuiltHiddenApiDir *string) android.Config {
config := testConfig(nil, bp, nil)
config.TestProductVariables.BootJars = android.CreateTestConfiguredJarList(bootJars)
+ config.TestProductVariables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
return config
}
@@ -44,8 +45,8 @@
return ctx
}
-func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string) (*android.TestContext, android.Config) {
- config := testConfigWithBootJars(bp, bootJars)
+func testHiddenAPIBootJars(t *testing.T, bp string, bootJars []string, prebuiltHiddenApiDir *string) (*android.TestContext, android.Config) {
+ config := testConfigWithBootJars(bp, bootJars, prebuiltHiddenApiDir)
return testHiddenAPIWithConfig(t, config), config
}
@@ -64,7 +65,7 @@
srcs: ["a.java"],
compile_dex: true,
}
- `, []string{":foo"})
+ `, []string{":foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -81,7 +82,7 @@
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"})
+ `, []string{":foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -105,7 +106,7 @@
compile_dex: true,
prefer: false,
}
- `, []string{":foo"})
+ `, []string{":foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -134,7 +135,7 @@
compile_dex: true,
prefer: true,
}
- `, []string{":foo"})
+ `, []string{":foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -217,3 +218,48 @@
}
return generateDexPath(module)
}
+
+func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+
+ // The idea behind this test is to ensure that when the build is
+ // confugured with a PrebuiltHiddenApiDir that the rules for the
+ // hiddenapi singleton copy the prebuilts to the typical output
+ // location, and then use that output location for the hiddenapi encode
+ // dex step.
+
+ // Where to find the prebuilt hiddenapi files:
+ prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
+
+ ctx, _ := testHiddenAPIBootJars(t, `
+ java_import {
+ name: "foo",
+ jars: ["a.jar"],
+ compile_dex: true,
+ }
+ `, []string{":foo"}, &prebuiltHiddenApiDir)
+
+ expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
+ expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+ expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+
+ foo := ctx.ModuleForTests("foo", "android_common")
+
+ hiddenAPI := ctx.SingletonForTests("hiddenapi")
+ cpRule := hiddenAPI.Rule("Cp")
+ actualCpInput := cpRule.BuildParams.Input
+ actualCpOutput := cpRule.BuildParams.Output
+ encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
+ actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
+
+ if actualCpInput.String() != expectedCpInput {
+ t.Errorf("Prebuilt hiddenapi cp rule input mismatch, actual: %s, expected: %s", actualCpInput, expectedCpInput)
+ }
+
+ if actualCpOutput.String() != expectedCpOutput {
+ t.Errorf("Prebuilt hiddenapi cp rule output mismatch, actual: %s, expected: %s", actualCpOutput, expectedCpOutput)
+ }
+
+ if actualFlagsCsv != expectedFlagsCsv {
+ t.Errorf("Prebuilt hiddenapi encode dex rule flags csv mismatch, actual: %s, expected: %s", actualFlagsCsv, expectedFlagsCsv)
+ }
+}
diff --git a/java/java.go b/java/java.go
index 99bc55d..59ec94d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2831,6 +2831,10 @@
return nil
}
+func (j *Import) LintDepSets() LintDepSets {
+ return LintDepSets{}
+}
+
func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
@@ -2859,6 +2863,7 @@
j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags
+ var deapexerModule android.Module
ctx.VisitDirectDeps(func(module android.Module) {
tag := ctx.OtherModuleDependencyTag(module)
@@ -2879,6 +2884,11 @@
}
addCLCFromDep(ctx, module, j.classLoaderContexts)
+
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = module
+ }
})
if Bool(j.properties.Installable) {
@@ -2888,39 +2898,60 @@
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
- if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
- sdkDep := decodeSdkDep(ctx, sdkContext(j))
- if sdkDep.invalidVersion {
- ctx.AddMissingDependencies(sdkDep.bootclasspath)
- ctx.AddMissingDependencies(sdkDep.java9Classpath)
- } else if sdkDep.useFiles {
- // sdkDep.jar is actually equivalent to turbine header.jar.
- flags.classpath = append(flags.classpath, sdkDep.jars...)
+ if ctx.Device() {
+ // If this is a variant created for a prebuilt_apex then use the dex implementation jar
+ // obtained from the associated deapexer module.
+ ai := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if ai.ForPrebuiltApex {
+ if deapexerModule == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // deapxer module has been configured to export the dex implementation jar for this module.
+ ctx.ModuleErrorf("internal error: module %q does not depend on a `deapexer` module for prebuilt_apex %q",
+ j.Name(), ai.ApexVariationName)
+ }
+
+ // Get the path of the dex implementation jar from the `deapexer` module.
+ di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ j.dexJarFile = di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar")
+ if j.dexJarFile == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // prebuilt_apex has been configured to export the java library dex file.
+ ctx.ModuleErrorf("internal error: no dex implementation jar available from prebuilt_apex %q", deapexerModule.Name())
+ }
+ } else if Bool(j.dexProperties.Compile_dex) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(j))
+ if sdkDep.invalidVersion {
+ ctx.AddMissingDependencies(sdkDep.bootclasspath)
+ ctx.AddMissingDependencies(sdkDep.java9Classpath)
+ } else if sdkDep.useFiles {
+ // sdkDep.jar is actually equivalent to turbine header.jar.
+ flags.classpath = append(flags.classpath, sdkDep.jars...)
+ }
+
+ // Dex compilation
+
+ j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+ if j.dexProperties.Uncompress_dex == nil {
+ // If the value was not force-set by the user, use reasonable default based on the module.
+ j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+ }
+ j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+ var dexOutputFile android.ModuleOutPath
+ dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+ if ctx.Failed() {
+ return
+ }
+
+ configurationName := j.BaseModuleName()
+ primary := j.Prebuilt().UsePrebuilt()
+
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
+ proptools.Bool(j.dexProperties.Uncompress_dex))
+
+ j.dexJarFile = dexOutputFile
}
-
- // Dex compilation
-
- j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
- if j.dexProperties.Uncompress_dex == nil {
- // If the value was not force-set by the user, use reasonable default based on the module.
- j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
- }
- j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-
- var dexOutputFile android.ModuleOutPath
- dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
- if ctx.Failed() {
- return
- }
-
- configurationName := j.BaseModuleName()
- primary := j.Prebuilt().UsePrebuilt()
-
- // Hidden API CSV generation and dex encoding
- dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
- proptools.Bool(j.dexProperties.Uncompress_dex))
-
- j.dexJarFile = dexOutputFile
}
}
diff --git a/java/java_test.go b/java/java_test.go
index a2466f9..7b89848 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -61,33 +61,16 @@
func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config {
bp += dexpreopt.BpToolModulesForTest()
- config := TestConfig(buildDir, env, bp, fs)
-
- // Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that
- // it doesn't create a real one, which would fail.
- _ = dexpreopt.GlobalSoongConfigForTests(config)
-
- return config
+ return TestConfig(buildDir, env, bp, fs)
}
func testContext(config android.Config) *android.TestContext {
ctx := android.NewTestArchContext(config)
- RegisterJavaBuildComponents(ctx)
- RegisterAppBuildComponents(ctx)
- RegisterAppImportBuildComponents(ctx)
- RegisterAppSetBuildComponents(ctx)
- RegisterAARBuildComponents(ctx)
- RegisterGenRuleBuildComponents(ctx)
- RegisterRuntimeResourceOverlayBuildComponents(ctx)
- RegisterSystemModulesBuildComponents(ctx)
+ RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("java_plugin", PluginFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
- RegisterDocsBuildComponents(ctx)
- RegisterStubsBuildComponents(ctx)
- RegisterPrebuiltApisBuildComponents(ctx)
- RegisterSdkLibraryBuildComponents(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(android.RegisterComponentsMutator)
@@ -867,83 +850,43 @@
return config
}
- isValidDependency := func(configInfo testConfigInfo) bool {
- if configInfo.enforceVendorInterface == false {
- return true
- }
-
- if configInfo.enforceJavaSdkLibraryCheck == false {
- return true
- }
-
- if inList("bar", configInfo.allowList) {
- return true
- }
-
- if configInfo.libraryType == "java_library" {
- if configInfo.fromPartition != configInfo.toPartition {
- if !configInfo.enforceProductInterface &&
- ((configInfo.fromPartition == "system" && configInfo.toPartition == "product") ||
- (configInfo.fromPartition == "product" && configInfo.toPartition == "system")) {
- return true
- }
- return false
- }
- }
-
- return true
- }
-
errorMessage := "is not allowed across the partitions"
- allPartitionCombinations := func() [][2]string {
- var result [][2]string
- partitions := []string{"system", "vendor", "product"}
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: false,
+ }))
- for _, fromPartition := range partitions {
- for _, toPartition := range partitions {
- result = append(result, [2]string{fromPartition, toPartition})
- }
- }
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: false,
+ enforceJavaSdkLibraryCheck: true,
+ }))
- return result
- }
+ testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "product",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }))
- allFlagCombinations := func() [][3]bool {
- var result [][3]bool
- flagValues := [2]bool{false, true}
-
- for _, vendorInterface := range flagValues {
- for _, productInterface := range flagValues {
- for _, enableEnforce := range flagValues {
- result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce})
- }
- }
- }
-
- return result
- }
-
- for _, libraryType := range []string{"java_library", "java_sdk_library"} {
- for _, partitionValues := range allPartitionCombinations() {
- for _, flagValues := range allFlagCombinations() {
- testInfo := testConfigInfo{
- libraryType: libraryType,
- fromPartition: partitionValues[0],
- toPartition: partitionValues[1],
- enforceVendorInterface: flagValues[0],
- enforceProductInterface: flagValues[1],
- enforceJavaSdkLibraryCheck: flagValues[2],
- }
-
- if isValidDependency(testInfo) {
- testJavaWithConfig(t, createTestConfig(testInfo))
- } else {
- testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo))
- }
- }
- }
- }
+ testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }))
testJavaWithConfig(t, createTestConfig(testConfigInfo{
libraryType: "java_library",
@@ -958,11 +901,37 @@
testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
libraryType: "java_library",
fromPartition: "vendor",
+ toPartition: "product",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }))
+
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "product",
toPartition: "system",
enforceVendorInterface: true,
enforceProductInterface: true,
enforceJavaSdkLibraryCheck: true,
- allowList: []string{"foo"},
+ }))
+
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ }))
+
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_sdk_library",
+ fromPartition: "vendor",
+ toPartition: "product",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
}))
}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 1a655a6..638740f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1048,6 +1048,30 @@
// Add other dependencies as normal.
func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ var missingApiModules []string
+ for _, apiScope := range module.getGeneratedApiScopes(ctx) {
+ if apiScope.unstable {
+ continue
+ }
+ if m := android.SrcIsModule(module.latestApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ missingApiModules = append(missingApiModules, m)
+ }
+ if m := android.SrcIsModule(module.latestRemovedApiFilegroupName(apiScope)); !ctx.OtherModuleExists(m) {
+ missingApiModules = append(missingApiModules, m)
+ }
+ }
+ if len(missingApiModules) != 0 && !module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api {
+ m := module.Name() + " is missing tracking files for previously released library versions.\n"
+ m += "You need to do one of the following:\n"
+ m += "- Add `unsafe_ignore_missing_latest_api: true` to your blueprint (to disable compat tracking)\n"
+ m += "- Add a set of prebuilt txt files representing the last released version of this library for compat checking.\n"
+ m += " (the current set of API files can be used as a seed for this compatibility tracking\n"
+ m += "\n"
+ m += "The following filegroup modules are missing:\n "
+ m += strings.Join(missingApiModules, "\n ") + "\n"
+ m += "Please see the documentation of the prebuilt_apis module type (and a usage example in prebuilts/sdk) for a convenient way to generate these."
+ ctx.ModuleErrorf(m)
+ }
if module.requiresRuntimeImplementationLibrary() {
// Only add the deps for the library if it is actually going to be built.
module.Library.deps(ctx)
diff --git a/java/testing.go b/java/testing.go
index fc4e477..0b1e2eb 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -95,6 +95,29 @@
return fs
}
+// Register build components provided by this package that are needed by tests.
+//
+// In particular this must register all the components that are used in the `Android.bp` snippet
+// returned by GatherRequiredDepsForTest()
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+ RegisterAARBuildComponents(ctx)
+ RegisterAppBuildComponents(ctx)
+ RegisterAppImportBuildComponents(ctx)
+ RegisterAppSetBuildComponents(ctx)
+ RegisterDexpreoptBootJarsComponents(ctx)
+ RegisterDocsBuildComponents(ctx)
+ RegisterGenRuleBuildComponents(ctx)
+ RegisterJavaBuildComponents(ctx)
+ RegisterPrebuiltApisBuildComponents(ctx)
+ RegisterRuntimeResourceOverlayBuildComponents(ctx)
+ RegisterSdkLibraryBuildComponents(ctx)
+ RegisterStubsBuildComponents(ctx)
+ RegisterSystemModulesBuildComponents(ctx)
+}
+
+// Gather the module definitions needed by tests that depend upon code from this package.
+//
+// Returns an `Android.bp` snippet that defines the modules that are needed by this package.
func GatherRequiredDepsForTest() string {
var bp string
@@ -181,6 +204,13 @@
`, extra)
}
+ // Make sure that the dex_bootjars singleton module is instantiated for the tests.
+ bp += `
+ dex_bootjars {
+ name: "dex_bootjars",
+ }
+`
+
return bp
}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 217792f..8032dc5 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -1052,6 +1052,11 @@
}
license_kind {
+ name: "legacy_permissive",
+ conditions: ["permissive"],
+}
+
+license_kind {
name: "legacy_notice",
conditions: ["notice"],
}
diff --git a/rust/builder.go b/rust/builder.go
index e5f0ab5..baeab69 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -188,7 +188,7 @@
implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
implicits = append(implicits, deps.StaticLibs...)
- implicits = append(implicits, deps.SharedLibs...)
+ implicits = append(implicits, deps.SharedLibDeps...)
implicits = append(implicits, deps.srcProviderFiles...)
if deps.CrtBegin.Valid() {
diff --git a/rust/rust.go b/rust/rust.go
index 1fa97af..83add87 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -267,14 +267,15 @@
}
type PathDeps struct {
- DyLibs RustLibraries
- RLibs RustLibraries
- SharedLibs android.Paths
- StaticLibs android.Paths
- ProcMacros RustLibraries
- linkDirs []string
- depFlags []string
- linkObjects []string
+ DyLibs RustLibraries
+ RLibs RustLibraries
+ SharedLibs android.Paths
+ SharedLibDeps android.Paths
+ StaticLibs android.Paths
+ ProcMacros RustLibraries
+ linkDirs []string
+ depFlags []string
+ linkObjects []string
//ReexportedDeps android.Paths
// Used by bindgen modules which call clang
@@ -952,9 +953,15 @@
staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
}
+ var sharedLibFiles android.Paths
var sharedLibDepFiles android.Paths
for _, dep := range directSharedLibDeps {
- sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
+ if dep.Toc().Valid() {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+ } else {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ }
}
var srcProviderDepFiles android.Paths
@@ -970,12 +977,14 @@
depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+ depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
// Dedup exported flags from dependencies
depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+ depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
new file mode 100644
index 0000000..e0da602
--- /dev/null
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -0,0 +1,55 @@
+#!/bin/bash -e
+
+# Copyright 2020 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generates NDK API txt file used by Mainline modules. NDK APIs would have value
+# "UND" in Ndx column and have suffix "@LIB_NAME" in Name column.
+# For example, current line llvm-readelf output is:
+# 1: 00000000 0 FUNC GLOBAL DEFAULT UND dlopen@LIBC
+# After the parse function below "dlopen" would be write to the output file.
+printHelp() {
+ echo "**************************** Usage Instructions ****************************"
+ echo "This script is used to generate the Mainline modules backed-by NDK symbols."
+ echo ""
+ echo "To run this script use: ./ndk_backedby_module.sh \$BINARY_IMAGE_DIRECTORY \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST"
+ echo "For example: If all the module image files that you would like to run is under directory '/myModule' and output write to /backedby.txt then the command would be:"
+ echo "./ndk_usedby_module.sh /myModule /backedby.txt /ndkLibList.txt"
+ echo "If the module1 is backing lib1 then the backedby.txt would contains: "
+ echo "lib1"
+}
+
+genBackedByList() {
+ dir="$1"
+ [[ ! -e "$2" ]] && echo "" >> "$2"
+ while IFS= read -r line
+ do
+ soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
+ if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
+ then
+ find "$dir" -type f -name "$soFileName" -exec echo "$soFileName" >> "$2" \;
+ fi
+ done < "$3"
+}
+
+if [[ "$1" == "help" ]]
+then
+ printHelp
+elif [[ "$#" -ne 3 ]]
+then
+ echo "Wrong argument length. Expecting 3 argument representing image file directory, output path, path to ndk library list."
+else
+ [[ -e "$2" ]] && rm "$2"
+ genBackedByList "$1" "$2" "$3"
+fi
diff --git a/scripts/unpack-prebuilt-apex.sh b/scripts/unpack-prebuilt-apex.sh
new file mode 100755
index 0000000..1acdeb5
--- /dev/null
+++ b/scripts/unpack-prebuilt-apex.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+set -eu
+
+# Copyright 2020 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Tool to unpack an apex file and verify that the required files were extracted.
+if [ $# -lt 5 ]; then
+ echo "usage: $0 <deapaxer_path> <debugfs_path> <apex file> <output_dir> <required_files>+" >&2
+ exit 1
+fi
+
+DEAPEXER_PATH=$1
+DEBUGFS_PATH=$2
+APEX_FILE=$3
+OUTPUT_DIR=$4
+shift 4
+REQUIRED_PATHS=$@
+
+set -x 1
+
+rm -fr $OUTPUT_DIR
+mkdir -p $OUTPUT_DIR
+
+# Unpack the apex file contents.
+$DEAPEXER_PATH --debugfs_path $DEBUGFS_PATH extract $APEX_FILE $OUTPUT_DIR
+
+# Verify that the files that the build expects to be in the .apex file actually
+# exist, and make sure they have a fresh mtime to not confuse ninja.
+typeset -i FAILED=0
+for r in $REQUIRED_PATHS; do
+ if [ ! -f $r ]; then
+ echo "Required file $r not present in apex $APEX_FILE" >&2
+ FAILED=$FAILED+1
+ else
+ # TODO(http:/b/177646343) - deapexer extracts the files with a timestamp of 1 Jan 1970.
+ # touch the file so that ninja knows it has changed.
+ touch $r
+ fi
+done
+
+if [ $FAILED -gt 0 ]; then
+ echo "$FAILED required files were missing from $APEX_FILE" >&2
+ echo "Available files are:" >&2
+ find $OUTPUT_DIR -type f | sed "s|^| |" >&2
+ exit 1
+fi
diff --git a/sdk/testing.go b/sdk/testing.go
index 38755a9..1ac873b 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -107,12 +107,7 @@
ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
// from java package
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterSdkLibraryBuildComponents(ctx)
- java.RegisterPrebuiltApisBuildComponents(ctx)
- java.RegisterStubsBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
// from cc package
cc.RegisterRequiredBuildComponentsForTest(ctx)
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 38306ad..2ccce15 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -19,6 +19,7 @@
import (
"fmt"
"io"
+ "os"
"path"
"sync"
@@ -125,8 +126,8 @@
properties syspropLibraryProperties
checkApiFileTimeStamp android.WritablePath
- latestApiFile android.Path
- currentApiFile android.Path
+ latestApiFile android.OptionalPath
+ currentApiFile android.OptionalPath
dumpedApiFile android.WritablePath
}
@@ -224,7 +225,7 @@
return proptools.Bool(m.properties.Public_stub)
}
-func (m *syspropLibrary) CurrentSyspropApiFile() android.Path {
+func (m *syspropLibrary) CurrentSyspropApiFile() android.OptionalPath {
return m.currentApiFile
}
@@ -243,8 +244,11 @@
return
}
- m.currentApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-current.txt")
- m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
+ apiDirectoryPath := path.Join(ctx.ModuleDir(), "api")
+ currentApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-current.txt")
+ latestApiFilePath := path.Join(apiDirectoryPath, baseModuleName+"-latest.txt")
+ m.currentApiFile = android.ExistentPathForSource(ctx, currentApiFilePath)
+ m.latestApiFile = android.ExistentPathForSource(ctx, latestApiFilePath)
// dump API rule
rule := android.NewRuleBuilder(pctx, ctx)
@@ -258,19 +262,37 @@
// check API rule
rule = android.NewRuleBuilder(pctx, ctx)
+ // We allow that the API txt files don't exist, when the sysprop_library only contains internal
+ // properties. But we have to feed current api file and latest api file to the rule builder.
+ // Currently we can't get android.Path representing the null device, so we add any existing API
+ // txt files to implicits, and then directly feed string paths, rather than calling Input(Path)
+ // method.
+ var apiFileList android.Paths
+ currentApiArgument := os.DevNull
+ if m.currentApiFile.Valid() {
+ apiFileList = append(apiFileList, m.currentApiFile.Path())
+ currentApiArgument = m.currentApiFile.String()
+ }
+
+ latestApiArgument := os.DevNull
+ if m.latestApiFile.Valid() {
+ apiFileList = append(apiFileList, m.latestApiFile.Path())
+ latestApiArgument = m.latestApiFile.String()
+ }
+
// 1. compares current.txt to api-dump.txt
// current.txt should be identical to api-dump.txt.
msg := fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with current.txt\n`+
`Please update current.txt by:\n`+
- `m %s-dump-api && rm -rf %q && cp -f %q %q\n`+
+ `m %s-dump-api && mkdir -p %q && rm -rf %q && cp -f %q %q\n`+
`******************************\n`, baseModuleName, baseModuleName,
- m.currentApiFile.String(), m.dumpedApiFile.String(), m.currentApiFile.String())
+ apiDirectoryPath, currentApiFilePath, m.dumpedApiFile.String(), currentApiFilePath)
rule.Command().
Text("( cmp").Flag("-s").
Input(m.dumpedApiFile).
- Input(m.currentApiFile).
+ Text(currentApiArgument).
Text("|| ( echo").Flag("-e").
Flag(`"` + msg + `"`).
Text("; exit 38) )")
@@ -285,11 +307,12 @@
rule.Command().
Text("( ").
BuiltTool("sysprop_api_checker").
- Input(m.latestApiFile).
- Input(m.currentApiFile).
+ Text(latestApiArgument).
+ Text(currentApiArgument).
Text(" || ( echo").Flag("-e").
Flag(`"` + msg + `"`).
- Text("; exit 38) )")
+ Text("; exit 38) )").
+ Implicits(apiFileList)
m.checkApiFileTimeStamp = android.PathForModuleOut(ctx, "check_api.timestamp")
@@ -393,31 +416,6 @@
ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
}
- missingApi := false
-
- for _, txt := range []string{"-current.txt", "-latest.txt"} {
- path := path.Join(ctx.ModuleDir(), "api", m.BaseModuleName()+txt)
- file := android.ExistentPathForSource(ctx, path)
- if !file.Valid() {
- ctx.ModuleErrorf("API file %#v doesn't exist", path)
- missingApi = true
- }
- }
-
- if missingApi {
- script := "build/soong/scripts/gen-sysprop-api-files.sh"
- p := android.ExistentPathForSource(ctx, script)
-
- if !p.Valid() {
- panic(fmt.Sprintf("script file %s doesn't exist", script))
- }
-
- ctx.ModuleErrorf("One or more api files are missing. "+
- "You can create them by:\n"+
- "%s %q %q", script, ctx.ModuleDir(), m.BaseModuleName())
- return
- }
-
// ctx's Platform or Specific functions represent where this sysprop_library installed.
installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index b8b93f6..5cb9e64 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -58,9 +58,7 @@
func testContext(config android.Config) *android.TestContext {
ctx := android.NewTestArchContext(config)
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 3c4c51d..b1f8551 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -117,6 +117,7 @@
productOut("misc_info.txt"),
productOut("apex"),
productOut("kernel"),
+ productOut("kernel-*"),
productOut("data"),
productOut("skin"),
productOut("obj/NOTICE_FILES"),