Merge "Revert "Automated NDK API coverage used by Mainline modules build integration.""
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/OWNERS b/OWNERS
index 3a5a8a7..e851bf7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,10 +11,8 @@
joeo@google.com
jungjw@google.com
lberki@google.com
-patricearruda@google.com
ruperts@google.com
# To expedite LON reviews
hansson@google.com
paulduffin@google.com
-
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 4bd272d..eabb137 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -21,14 +21,19 @@
"bazel_handler.go",
"config.go",
"csuite_config.go",
+ "deapexer.go",
"defaults.go",
"defs.go",
- "depset.go",
+ "depset_generic.go",
+ "depset_paths.go",
"deptag.go",
"expand.go",
"filegroup.go",
"hooks.go",
"image.go",
+ "license.go",
+ "license_kind.go",
+ "licenses.go",
"makefile_goal.go",
"makevars.go",
"metrics.go",
@@ -55,6 +60,7 @@
"sandbox.go",
"sdk.go",
"singleton.go",
+ "singleton_module.go",
"soong_config_modules.go",
"test_suites.go",
"testing.go",
@@ -76,6 +82,9 @@
"depset_test.go",
"deptag_test.go",
"expand_test.go",
+ "license_kind_test.go",
+ "license_test.go",
+ "licenses_test.go",
"module_test.go",
"mutator_test.go",
"namespace_test.go",
@@ -88,6 +97,7 @@
"paths_test.go",
"prebuilt_test.go",
"rule_builder_test.go",
+ "singleton_module_test.go",
"soong_config_modules_test.go",
"util_test.go",
"variable_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 42c5d00..5856851 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -28,6 +28,7 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"sort"
"strings"
@@ -141,7 +142,7 @@
}
type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
-type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries)
+type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string)
// Utility funcs to manipulate Android.mk variable entries.
@@ -434,6 +435,17 @@
return generateDistContributionsForMake(distContributions)
}
+// Write the license variables to Make for AndroidMkData.Custom(..) methods that do not call WriteAndroidMkData(..)
+// It's required to propagate the license metadata even for module types that have non-standard interfaces to Make.
+func (a *AndroidMkEntries) WriteLicenseVariables(w io.Writer) {
+ fmt.Fprintln(w, "LOCAL_LICENSE_KINDS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_KINDS"], " "))
+ fmt.Fprintln(w, "LOCAL_LICENSE_CONDITIONS :=", strings.Join(a.EntryMap["LOCAL_LICENSE_CONDITIONS"], " "))
+ fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(a.EntryMap["LOCAL_NOTICE_FILE"], " "))
+ if pn, ok := a.EntryMap["LOCAL_LICENSE_PACKAGE_NAME"]; ok {
+ fmt.Fprintln(w, "LOCAL_LICENSE_PACKAGE_NAME :=", strings.Join(pn, " "))
+ }
+}
+
// fillInEntries goes through the common variable processing and calls the extra data funcs to
// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
@@ -460,6 +472,13 @@
// Collect make variable assignment entries.
a.SetString("LOCAL_PATH", filepath.Dir(bpPath))
a.SetString("LOCAL_MODULE", name+a.SubName)
+ a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
+ a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
+ a.AddStrings("LOCAL_NOTICE_FILE", amod.commonProperties.Effective_license_text...)
+ // TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
+ if amod.commonProperties.Effective_package_name != nil {
+ a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
+ }
a.SetString("LOCAL_MODULE_CLASS", a.Class)
a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
a.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
@@ -554,7 +573,7 @@
fmt.Fprintln(&a.footer, "include "+a.Include)
blueprintDir := filepath.Dir(bpPath)
for _, footerFunc := range a.ExtraFooters {
- footerFunc(&a.footer, name, prefix, blueprintDir, a)
+ footerFunc(&a.footer, name, prefix, blueprintDir)
}
}
@@ -627,7 +646,7 @@
fmt.Fprintln(buf, "LOCAL_MODULE_MAKEFILE := $(lastword $(MAKEFILE_LIST))")
- type_stats := make(map[string]int)
+ typeStats := make(map[string]int)
for _, mod := range mods {
err := translateAndroidMkModule(ctx, buf, mod)
if err != nil {
@@ -636,19 +655,19 @@
}
if amod, ok := mod.(Module); ok && ctx.PrimaryModule(amod) == amod {
- type_stats[ctx.ModuleType(amod)] += 1
+ typeStats[ctx.ModuleType(amod)] += 1
}
}
keys := []string{}
fmt.Fprintln(buf, "\nSTATS.SOONG_MODULE_TYPE :=")
- for k := range type_stats {
+ for k := range typeStats {
keys = append(keys, k)
}
sort.Strings(keys)
for _, mod_type := range keys {
fmt.Fprintln(buf, "STATS.SOONG_MODULE_TYPE +=", mod_type)
- fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, type_stats[mod_type])
+ fmt.Fprintf(buf, "STATS.SOONG_MODULE_TYPE.%s := %d\n", mod_type, typeStats[mod_type])
}
// Don't write to the file if it hasn't changed
@@ -682,6 +701,7 @@
}
}()
+ // Additional cases here require review for correct license propagation to make.
switch x := mod.(type) {
case AndroidMkDataProvider:
return translateAndroidModule(ctx, w, mod, x)
@@ -690,6 +710,7 @@
case AndroidMkEntriesProvider:
return translateAndroidMkEntriesModule(ctx, w, mod, x)
default:
+ // Not exported to make so no make variables to set.
return nil
}
}
@@ -703,6 +724,10 @@
fmt.Fprintln(w, ".PHONY:", name)
fmt.Fprintln(w, name+":", goBinary.InstallPath())
fmt.Fprintln(w, "")
+ // Assuming no rules in make include go binaries in distributables.
+ // If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
+ // In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
+ // `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
return nil
}
@@ -768,6 +793,25 @@
blueprintDir := filepath.Dir(ctx.BlueprintFile(mod))
if data.Custom != nil {
+ // List of module types allowed to use .Custom(...)
+ // Additions to the list require careful review for proper license handling.
+ switch reflect.TypeOf(mod).String() { // ctx.ModuleType(mod) doesn't work: aidl_interface creates phony without type
+ case "*aidl.aidlApi": // writes non-custom before adding .phony
+ case "*aidl.aidlMapping": // writes non-custom before adding .phony
+ case "*android.customModule": // appears in tests only
+ case "*apex.apexBundle": // license properties written
+ case "*bpf.bpf": // license properties written (both for module and objs)
+ case "*genrule.Module": // writes non-custom before adding .phony
+ case "*java.SystemModules": // doesn't go through base_rules
+ case "*java.systemModulesImport": // doesn't go through base_rules
+ case "*phony.phony": // license properties written
+ case "*selinux.selinuxContextsModule": // license properties written
+ case "*sysprop.syspropLibrary": // license properties written
+ default:
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
+ }
+ }
data.Custom(w, name, prefix, blueprintDir, data)
} else {
WriteAndroidMkData(w, data)
@@ -804,6 +848,7 @@
return nil
}
+ // Any new or special cases here need review to verify correct propagation of license information.
for _, entries := range provider.AndroidMkEntries() {
entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
entries.write(w)
@@ -820,7 +865,7 @@
}
return !module.Enabled() ||
- module.commonProperties.SkipInstall ||
+ module.commonProperties.HideFromMake ||
// Make does not understand LinuxBionic
module.Os() == LinuxBionic
}
diff --git a/android/apex.go b/android/apex.go
index a4ff0f9..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")
@@ -153,13 +161,12 @@
// run.
DirectlyInAnyApex() bool
- // Returns true in the primary variant of a module if _any_ variant of the module is
- // directly in any apex. This includes host, arch, asan, etc. variants. It is unused in any
- // variant that is not the primary variant. Ideally this wouldn't be used, as it incorrectly
- // mixes arch variants if only one arch is in an apex, but a few places depend on it, for
- // example when an ASAN variant is created before the apexMutator. Call this after
- // apex.apexMutator is run.
- AnyVariantDirectlyInAnyApex() bool
+ // NotInPlatform tells whether or not this module is included in an APEX and therefore
+ // shouldn't be exposed to the platform (i.e. outside of the APEX) directly. A module is
+ // considered to be included in an APEX either when there actually is an APEX that
+ // explicitly has the module as its dependency or the module is not available to the
+ // platform, which indicates that the module belongs to at least one or more other APEXes.
+ NotInPlatform() bool
// Tests if this module could have APEX variants. Even when a module type implements
// ApexModule interface, APEX variants are created only for the module instances that return
@@ -221,7 +228,12 @@
// See ApexModule.DirectlyInAnyApex()
DirectlyInAnyApex bool `blueprint:"mutated"`
- // See ApexModule.AnyVariantDirectlyInAnyApex()
+ // AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant
+ // of the module is directly in any apex. This includes host, arch, asan, etc. variants. It
+ // is unused in any variant that is not the primary variant. Ideally this wouldn't be used,
+ // as it incorrectly mixes arch variants if only one arch is in an apex, but a few places
+ // depend on it, for example when an ASAN variant is created before the apexMutator. Call
+ // this after apex.apexMutator is run.
AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
// See ApexModule.NotAvailableForPlatform()
@@ -249,6 +261,12 @@
CopyDirectlyInAnyApex()
}
+// Interface that identifies dependencies to skip Apex dependency check
+type SkipApexAllowedDependenciesCheck interface {
+ // Returns true to skip the Apex dependency check, which limits the allowed dependency in build.
+ SkipApexAllowedDependenciesCheck() bool
+}
+
// ApexModuleBase provides the default implementation for the ApexModule interface. APEX-aware
// modules are expected to include this struct and call InitApexModule().
type ApexModuleBase struct {
@@ -257,7 +275,7 @@
canHaveApexVariants bool
apexInfos []ApexInfo
- apexInfosLock sync.Mutex // protects apexInfos during parallel apexDepsMutator
+ apexInfosLock sync.Mutex // protects apexInfos during parallel apexInfoMutator
}
// Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase)
@@ -302,8 +320,8 @@
}
// Implements ApexModule
-func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
- return m.ApexProperties.AnyVariantDirectlyInAnyApex
+func (m *ApexModuleBase) NotInPlatform() bool {
+ return m.ApexProperties.AnyVariantDirectlyInAnyApex || !m.AvailableFor(AvailableToPlatform)
}
// Implements ApexModule
@@ -402,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 {
@@ -442,7 +470,7 @@
} else {
apexInfos = base.apexInfos
}
- // base.apexInfos is only needed to propagate the list of apexes from apexDepsMutator to
+ // base.apexInfos is only needed to propagate the list of apexes from apexInfoMutator to
// apexMutator. It is no longer accurate after mergeApexVariations, and won't be copied to
// all but the first created variant. Clear it so it doesn't accidentally get used later.
base.apexInfos = nil
@@ -572,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 7d8d12f..4a25119 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,6 +27,9 @@
"sync"
"github.com/google/blueprint/bootstrap"
+
+ "android/soong/bazel"
+ "android/soong/shared"
)
type CqueryRequestType int
@@ -58,6 +61,12 @@
// Returns true if bazel is enabled for the given configuration.
BazelEnabled() bool
+
+ // Returns the bazel output base (the root directory for all bazel intermediate outputs).
+ OutputBase() string
+
+ // Returns build statements which should get registered to reflect Bazel's outputs.
+ BuildStatementsToRegister() []bazel.BuildStatement
}
// A context object which tracks queued requests that need to be made to Bazel,
@@ -68,11 +77,15 @@
outputBase string
workspaceDir string
buildDir string
+ metricsDir string
requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
+
+ // Build statements which should get registered to reflect Bazel's outputs.
+ buildStatements []bazel.BuildStatement
}
var _ BazelContext = &bazelContext{}
@@ -100,6 +113,14 @@
return true
}
+func (m MockBazelContext) OutputBase() string {
+ return "outputbase"
+}
+
+func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
var _ BazelContext = MockBazelContext{}
func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
@@ -120,10 +141,18 @@
panic("unimplemented")
}
+func (m noopBazelContext) OutputBase() string {
+ return ""
+}
+
func (n noopBazelContext) BazelEnabled() bool {
return false
}
+func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
@@ -153,6 +182,11 @@
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
}
+ if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
+ bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
+ } else {
+ missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
+ }
if len(missingEnvVars) > 0 {
return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
} else {
@@ -160,6 +194,10 @@
}
}
+func (context *bazelContext) BazelMetricsDir() string {
+ return context.metricsDir
+}
+
func (context *bazelContext) BazelEnabled() bool {
return true
}
@@ -189,12 +227,13 @@
return ""
}
-func (context *bazelContext) issueBazelCommand(command string, labels []string,
+func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
extraFlags ...string) (string, error) {
cmdFlags := []string{"--output_base=" + context.outputBase, command}
cmdFlags = append(cmdFlags, labels...)
cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
+ cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
cmdFlags = append(cmdFlags, extraFlags...)
bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
@@ -228,14 +267,32 @@
func (context *bazelContext) mainBzlFileContents() []byte {
contents := `
+#####################################################
# This file is generated by soong_build. Do not edit.
+#####################################################
+
def _mixed_build_root_impl(ctx):
return [DefaultInfo(files = depset(ctx.files.deps))]
+# Rule representing the root of the build, to depend on all Bazel targets that
+# are required for the build. Building this target will build the entire Bazel
+# build tree.
mixed_build_root = rule(
implementation = _mixed_build_root_impl,
attrs = {"deps" : attr.label_list()},
)
+
+def _phony_root_impl(ctx):
+ return []
+
+# Rule to depend on other targets but build nothing.
+# This is useful as follows: building a target of this rule will generate
+# symlink forests for all dependencies of the target, without executing any
+# actions of the build.
+phony_root = rule(
+ implementation = _phony_root_impl,
+ attrs = {"deps" : attr.label_list()},
+)
`
return []byte(contents)
}
@@ -255,11 +312,15 @@
func (context *bazelContext) mainBuildFileContents() []byte {
formatString := `
# This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root")
+load(":main.bzl", "mixed_build_root", "phony_root")
mixed_build_root(name = "buildroot",
deps = [%s],
)
+
+phony_root(name = "phonyroot",
+ deps = [":buildroot"],
+)
`
var buildRootDeps []string = nil
for val, _ := range context.requests {
@@ -310,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
}
@@ -326,25 +391,25 @@
if err != nil {
return err
}
- cquery_file_relpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
+ cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
err = ioutil.WriteFile(
- absolutePath(cquery_file_relpath),
+ absolutePath(cqueryFileRelpath),
context.cqueryStarlarkFileContents(), 0666)
if err != nil {
return err
}
- workspace_file_relpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
+ workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
err = ioutil.WriteFile(
- absolutePath(workspace_file_relpath),
+ absolutePath(workspaceFileRelpath),
context.workspaceFileContents(), 0666)
if err != nil {
return err
}
- buildroot_label := "//:buildroot"
- cqueryOutput, err = context.issueBazelCommand("cquery",
- []string{fmt.Sprintf("deps(%s)", buildroot_label)},
+ buildrootLabel := "//:buildroot"
+ cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
+ []string{fmt.Sprintf("deps(%s)", buildrootLabel)},
"--output=starlark",
- "--starlark:file="+cquery_file_relpath)
+ "--starlark:file="+cqueryFileRelpath)
if err != nil {
return err
@@ -366,22 +431,49 @@
}
}
- // Issue a build command.
- // TODO(cparsons): Invoking bazel execution during soong_build should be avoided;
- // bazel actions should either be added to the Ninja file and executed later,
- // or bazel should handle execution.
+ // Issue an aquery command to retrieve action information about the bazel build tree.
+ //
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
- _, err = context.issueBazelCommand("build", []string{buildroot_label})
+ var aqueryOutput string
+ aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
+ []string{fmt.Sprintf("deps(%s)", buildrootLabel),
+ // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+ // proto sources, which would add a number of unnecessary dependencies.
+ "--output=jsonproto"})
if err != nil {
return err
}
+ 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,
+ // but some of symlinks may be required to resolve source dependencies of the build.
+ _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
+ []string{"//:phonyroot"})
+
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Build statements %s", context.buildStatements)
// Clear requests.
context.requests = map[cqueryKey]bool{}
return nil
}
+func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return context.buildStatements
+}
+
+func (context *bazelContext) OutputBase() string {
+ return context.outputBase
+}
+
// Singleton used for registering BUILD file ninja dependencies (needed
// for correctness of builds which use Bazel.
func BazelSingleton() Singleton {
@@ -391,18 +483,45 @@
type bazelSingleton struct{}
func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
- if ctx.Config().BazelContext.BazelEnabled() {
- bazelBuildList := absolutePath(filepath.Join(
- filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
- ctx.AddNinjaFileDeps(bazelBuildList)
+ // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
+ if !ctx.Config().BazelContext.BazelEnabled() {
+ return
+ }
- data, err := ioutil.ReadFile(bazelBuildList)
- if err != nil {
- ctx.Errorf(err.Error())
+ // Add ninja file dependencies for files which all bazel invocations require.
+ bazelBuildList := absolutePath(filepath.Join(
+ filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
+ ctx.AddNinjaFileDeps(bazelBuildList)
+
+ data, err := ioutil.ReadFile(bazelBuildList)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+ files := strings.Split(strings.TrimSpace(string(data)), "\n")
+ for _, file := range files {
+ ctx.AddNinjaFileDeps(file)
+ }
+
+ // Register bazel-owned build statements (obtained from the aquery invocation).
+ for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+ rule := NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command()
+ cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
+ ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+ for _, outputPath := range buildStatement.OutputPaths {
+ cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
}
- files := strings.Split(strings.TrimSpace(string(data)), "\n")
- for _, file := range files {
- ctx.AddNinjaFileDeps(file)
+ for _, inputPath := range buildStatement.InputPaths {
+ cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
+
+ // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
+ // some Bazel builtins (such as files in the bazel_tools directory) have far-future
+ // timestamps. Without restat, Ninja would emit warnings that the input files of a
+ // build statement have later timestamps than the outputs.
+ rule.Restat()
+
+ rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
}
}
diff --git a/android/config.go b/android/config.go
index 9882d55..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] {
@@ -1053,6 +1057,10 @@
return String(c.config.productVariables.DeviceVndkVersion)
}
+func (c *deviceConfig) RecoverySnapshotVersion() string {
+ return String(c.config.productVariables.RecoverySnapshotVersion)
+}
+
func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
}
@@ -1252,6 +1260,27 @@
return HasAnyPrefix(path, c.productVariables.CFIIncludePaths)
}
+func (c *config) MemtagHeapDisabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapExcludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapExcludePaths)
+}
+
+func (c *config) MemtagHeapAsyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapAsyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapAsyncIncludePaths)
+}
+
+func (c *config) MemtagHeapSyncEnabledForPath(path string) bool {
+ if len(c.productVariables.MemtagHeapSyncIncludePaths) == 0 {
+ return false
+ }
+ return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths)
+}
+
func (c *config) VendorConfig(name string) VendorConfig {
return soongconfig.Config(c.productVariables.VendorVars[name])
}
@@ -1272,6 +1301,14 @@
return Bool(c.productVariables.Flatten_apex)
}
+func (c *config) ForceApexSymlinkOptimization() bool {
+ return Bool(c.productVariables.ForceApexSymlinkOptimization)
+}
+
+func (c *config) CompressedApex() bool {
+ return Bool(c.productVariables.CompressedApex)
+}
+
func (c *config) EnforceSystemCertificate() bool {
return Bool(c.productVariables.EnforceSystemCertificate)
}
@@ -1320,10 +1357,6 @@
return c.productVariables.ProductPrivateSepolicyDirs
}
-func (c *config) ProductCompatibleProperty() bool {
- return Bool(c.productVariables.ProductCompatibleProperty)
-}
-
func (c *config) MissingUsesLibraries() []string {
return c.productVariables.MissingUsesLibraries
}
@@ -1360,6 +1393,26 @@
return Bool(c.config.productVariables.BoardMoveRecoveryResourcesToVendorBoot)
}
+func (c *deviceConfig) PlatformSepolicyVersion() string {
+ return String(c.config.productVariables.PlatformSepolicyVersion)
+}
+
+func (c *deviceConfig) BoardSepolicyVers() string {
+ return String(c.config.productVariables.BoardSepolicyVers)
+}
+
+func (c *deviceConfig) BoardReqdMaskPolicy() []string {
+ 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/defaults.go b/android/defaults.go
index 44753ce..aacfbac 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -204,6 +204,9 @@
// its checking phase and parsing phase so add it to the list as a normal property.
AddVisibilityProperty(module, "visibility", &commonProperties.Visibility)
+ // The applicable licenses property for defaults is 'licenses'.
+ setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
+
base.module = module
}
diff --git a/android/defs.go b/android/defs.go
index f5bd362..38ecb05 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -20,7 +20,7 @@
"testing"
"github.com/google/blueprint"
- _ "github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
)
@@ -200,3 +200,8 @@
return content
}
+
+// GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file.
+func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) {
+ bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String())
+}
diff --git a/android/depset.go b/android/depset.go
deleted file mode 100644
index 60ebcac..0000000
--- a/android/depset.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import "fmt"
-
-// DepSet is designed to be conceptually compatible with Bazel's depsets:
-// https://docs.bazel.build/versions/master/skylark/depsets.html
-
-// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored
-// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency
-// DepSet nodes.
-//
-// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order
-// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
-// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
-// elements of children are listed after all of their parents (unless there are duplicate direct
-// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
-// duplicated element is not guaranteed).
-//
-// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents
-// and the *DepSets of dependencies. A DepSet is immutable once created.
-type DepSet struct {
- preorder bool
- reverse bool
- order DepSetOrder
- direct Paths
- transitive []*DepSet
-}
-
-// DepSetBuilder is used to create an immutable DepSet.
-type DepSetBuilder struct {
- order DepSetOrder
- direct Paths
- transitive []*DepSet
-}
-
-type DepSetOrder int
-
-const (
- PREORDER DepSetOrder = iota
- POSTORDER
- TOPOLOGICAL
-)
-
-func (o DepSetOrder) String() string {
- switch o {
- case PREORDER:
- return "PREORDER"
- case POSTORDER:
- return "POSTORDER"
- case TOPOLOGICAL:
- return "TOPOLOGICAL"
- default:
- panic(fmt.Errorf("Invalid DepSetOrder %d", o))
- }
-}
-
-// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
-func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet {
- var directCopy Paths
- transitiveCopy := make([]*DepSet, 0, len(transitive))
-
- for _, dep := range transitive {
- if dep != nil {
- if dep.order != order {
- panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
- order, dep.order))
- }
- transitiveCopy = append(transitiveCopy, dep)
- }
- }
-
- if order == TOPOLOGICAL {
- directCopy = ReversePaths(direct)
- reverseDepSetsInPlace(transitiveCopy)
- } else {
- // Use copy instead of append(nil, ...) to make a slice that is exactly the size of the input
- // slice. The DepSet is immutable, there is no need for additional capacity.
- directCopy = make(Paths, len(direct))
- copy(directCopy, direct)
- }
-
- return &DepSet{
- preorder: order == PREORDER,
- reverse: order == TOPOLOGICAL,
- order: order,
- direct: directCopy,
- transitive: transitiveCopy,
- }
-}
-
-// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order.
-func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder {
- return &DepSetBuilder{order: order}
-}
-
-// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
-// contents are to the right of any existing direct contents.
-func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder {
- b.direct = append(b.direct, direct...)
- return b
-}
-
-// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
-// transitive contents are to the right of any existing transitive contents.
-func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder {
- b.transitive = append(b.transitive, transitive...)
- return b
-}
-
-// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents
-// for creating more DepSets.
-func (b *DepSetBuilder) Build() *DepSet {
- return NewDepSet(b.order, b.direct, b.transitive)
-}
-
-// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
-// otherwise postordered.
-func (d *DepSet) walk(visit func(Paths)) {
- visited := make(map[*DepSet]bool)
-
- var dfs func(d *DepSet)
- dfs = func(d *DepSet) {
- visited[d] = true
- if d.preorder {
- visit(d.direct)
- }
- for _, dep := range d.transitive {
- if !visited[dep] {
- dfs(dep)
- }
- }
-
- if !d.preorder {
- visit(d.direct)
- }
- }
-
- dfs(d)
-}
-
-// ToList returns the DepSet flattened to a list. The order in the list is based on the order
-// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
-// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
-// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
-// its transitive dependencies, in which case the ordering of the duplicated element is not
-// guaranteed).
-func (d *DepSet) ToList() Paths {
- if d == nil {
- return nil
- }
- var list Paths
- d.walk(func(paths Paths) {
- list = append(list, paths...)
- })
- list = FirstUniquePaths(list)
- if d.reverse {
- reversePathsInPlace(list)
- }
- return list
-}
-
-// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order
-// with duplicates removed.
-func (d *DepSet) ToSortedList() Paths {
- list := d.ToList()
- return SortedUniquePaths(list)
-}
-
-func reversePathsInPlace(list Paths) {
- for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
- list[i], list[j] = list[j], list[i]
- }
-}
-
-func reverseDepSetsInPlace(list []*DepSet) {
- for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
- list[i], list[j] = list[j], list[i]
- }
-
-}
diff --git a/android/depset_generic.go b/android/depset_generic.go
new file mode 100644
index 0000000..f00e462
--- /dev/null
+++ b/android/depset_generic.go
@@ -0,0 +1,351 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// depSet is designed to be conceptually compatible with Bazel's depsets:
+// https://docs.bazel.build/versions/master/skylark/depsets.html
+
+type DepSetOrder int
+
+const (
+ PREORDER DepSetOrder = iota
+ POSTORDER
+ TOPOLOGICAL
+)
+
+func (o DepSetOrder) String() string {
+ switch o {
+ case PREORDER:
+ return "PREORDER"
+ case POSTORDER:
+ return "POSTORDER"
+ case TOPOLOGICAL:
+ return "TOPOLOGICAL"
+ default:
+ panic(fmt.Errorf("Invalid DepSetOrder %d", o))
+ }
+}
+
+// A depSet efficiently stores a slice of an arbitrary type from transitive dependencies without
+// copying. It is stored as a DAG of depSet nodes, each of which has some direct contents and a list
+// of dependency depSet nodes.
+//
+// A depSet has an order that will be used to walk the DAG when ToList() is called. The order
+// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
+// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
+// elements of children are listed after all of their parents (unless there are duplicate direct
+// elements in the depSet or any of its transitive dependencies, in which case the ordering of the
+// duplicated element is not guaranteed).
+//
+// A depSet is created by newDepSet or newDepSetBuilder.Build from the slice for direct contents
+// and the *depSets of dependencies. A depSet is immutable once created.
+//
+// This object uses reflection to remain agnostic to the type it contains. It should be replaced
+// with generics once those exist in Go. Callers should generally use a thin wrapper around depSet
+// that provides type-safe methods like DepSet for Paths.
+type depSet struct {
+ preorder bool
+ reverse bool
+ order DepSetOrder
+ direct interface{}
+ transitive []*depSet
+}
+
+type depSetInterface interface {
+ embeddedDepSet() *depSet
+}
+
+func (d *depSet) embeddedDepSet() *depSet {
+ return d
+}
+
+var _ depSetInterface = (*depSet)(nil)
+
+// newDepSet returns an immutable depSet with the given order, direct and transitive contents.
+// direct must be a slice, but is not type-safe due to the lack of generics in Go. It can be a
+// nil slice, but not a nil interface{}, i.e. []string(nil) but not nil.
+func newDepSet(order DepSetOrder, direct interface{}, transitive interface{}) *depSet {
+ var directCopy interface{}
+ transitiveDepSet := sliceToDepSets(transitive, order)
+
+ if order == TOPOLOGICAL {
+ directCopy = reverseSlice(direct)
+ reverseSliceInPlace(transitiveDepSet)
+ } else {
+ directCopy = copySlice(direct)
+ }
+
+ return &depSet{
+ preorder: order == PREORDER,
+ reverse: order == TOPOLOGICAL,
+ order: order,
+ direct: directCopy,
+ transitive: transitiveDepSet,
+ }
+}
+
+// depSetBuilder is used to create an immutable depSet.
+type depSetBuilder struct {
+ order DepSetOrder
+ direct reflect.Value
+ transitive []*depSet
+}
+
+// newDepSetBuilder returns a depSetBuilder to create an immutable depSet with the given order and
+// type, represented by a slice of type that will be in the depSet.
+func newDepSetBuilder(order DepSetOrder, typ interface{}) *depSetBuilder {
+ empty := reflect.Zero(reflect.TypeOf(typ))
+ return &depSetBuilder{
+ order: order,
+ direct: empty,
+ }
+}
+
+// sliceToDepSets converts a slice of any type that implements depSetInterface (by having a depSet
+// embedded in it) into a []*depSet.
+func sliceToDepSets(in interface{}, order DepSetOrder) []*depSet {
+ slice := reflect.ValueOf(in)
+ length := slice.Len()
+ out := make([]*depSet, length)
+ for i := 0; i < length; i++ {
+ vi := slice.Index(i)
+ depSetIntf, ok := vi.Interface().(depSetInterface)
+ if !ok {
+ panic(fmt.Errorf("element %d is a %s, not a depSetInterface", i, vi.Type()))
+ }
+ depSet := depSetIntf.embeddedDepSet()
+ if depSet.order != order {
+ panic(fmt.Errorf("incompatible order, new depSet is %s but transitive depSet is %s",
+ order, depSet.order))
+ }
+ out[i] = depSet
+ }
+ return out
+}
+
+// DirectSlice adds direct contents to the depSet being built by a depSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents. The argument must be a slice, but
+// is not type-safe due to the lack of generics in Go.
+func (b *depSetBuilder) DirectSlice(direct interface{}) *depSetBuilder {
+ b.direct = reflect.AppendSlice(b.direct, reflect.ValueOf(direct))
+ return b
+}
+
+// Direct adds direct contents to the depSet being built by a depSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents. The argument must be the same type
+// as the element of the slice passed to newDepSetBuilder, but is not type-safe due to the lack of
+// generics in Go.
+func (b *depSetBuilder) Direct(direct interface{}) *depSetBuilder {
+ b.direct = reflect.Append(b.direct, reflect.ValueOf(direct))
+ return b
+}
+
+// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
+// transitive contents are to the right of any existing transitive contents. The argument can
+// be any slice of type that has depSet embedded in it.
+func (b *depSetBuilder) Transitive(transitive interface{}) *depSetBuilder {
+ depSets := sliceToDepSets(transitive, b.order)
+ b.transitive = append(b.transitive, depSets...)
+ return b
+}
+
+// Returns the depSet being built by this depSetBuilder. The depSetBuilder retains its contents
+// for creating more depSets.
+func (b *depSetBuilder) Build() *depSet {
+ return newDepSet(b.order, b.direct.Interface(), b.transitive)
+}
+
+// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
+// otherwise postordered.
+func (d *depSet) walk(visit func(interface{})) {
+ visited := make(map[*depSet]bool)
+
+ var dfs func(d *depSet)
+ dfs = func(d *depSet) {
+ visited[d] = true
+ if d.preorder {
+ visit(d.direct)
+ }
+ for _, dep := range d.transitive {
+ if !visited[dep] {
+ dfs(dep)
+ }
+ }
+
+ if !d.preorder {
+ visit(d.direct)
+ }
+ }
+
+ dfs(d)
+}
+
+// ToList returns the depSet flattened to a list. The order in the list is based on the order
+// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
+// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
+// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
+// its transitive dependencies, in which case the ordering of the duplicated element is not
+// guaranteed).
+//
+// This method uses a reflection-based implementation to find the unique elements in slice, which
+// is around 3x slower than a concrete implementation. Type-safe wrappers around depSet can
+// provide their own implementation of ToList that calls depSet.toList with a method that
+// uses a concrete implementation.
+func (d *depSet) ToList() interface{} {
+ return d.toList(firstUnique)
+}
+
+// toList returns the depSet flattened to a list. The order in the list is based on the order
+// of the depSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
+// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
+// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
+// its transitive dependencies, in which case the ordering of the duplicated element is not
+// guaranteed). The firstUniqueFunc is used to remove duplicates from the list.
+func (d *depSet) toList(firstUniqueFunc func(interface{}) interface{}) interface{} {
+ if d == nil {
+ return nil
+ }
+ slice := reflect.Zero(reflect.TypeOf(d.direct))
+ d.walk(func(paths interface{}) {
+ slice = reflect.AppendSlice(slice, reflect.ValueOf(paths))
+ })
+ list := slice.Interface()
+ list = firstUniqueFunc(list)
+ if d.reverse {
+ reverseSliceInPlace(list)
+ }
+ return list
+}
+
+// firstUnique returns all unique elements of a slice, keeping the first copy of each. It
+// modifies the slice contents in place, and returns a subslice of the original slice. The
+// argument must be a slice, but is not type-safe due to the lack of reflection in Go.
+//
+// Performance of the reflection-based firstUnique is up to 3x slower than a concrete type
+// version such as FirstUniqueStrings.
+func firstUnique(slice interface{}) interface{} {
+ // 4 was chosen based on Benchmark_firstUnique results.
+ if reflect.ValueOf(slice).Len() > 4 {
+ return firstUniqueMap(slice)
+ }
+ return firstUniqueList(slice)
+}
+
+// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for
+// duplicates.
+func firstUniqueList(in interface{}) interface{} {
+ writeIndex := 0
+ slice := reflect.ValueOf(in)
+ length := slice.Len()
+outer:
+ for readIndex := 0; readIndex < length; readIndex++ {
+ readValue := slice.Index(readIndex)
+ for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
+ compareValue := slice.Index(compareIndex)
+ // These two Interface() calls seem to cause an allocation and significantly
+ // slow down this list-based implementation. The map implementation below doesn't
+ // have this issue because reflect.Value.MapIndex takes a Value and appears to be
+ // able to do the map lookup without an allocation.
+ if readValue.Interface() == compareValue.Interface() {
+ // The value at readIndex already exists somewhere in the output region
+ // of the slice before writeIndex, skip it.
+ continue outer
+ }
+ }
+ if readIndex != writeIndex {
+ writeValue := slice.Index(writeIndex)
+ writeValue.Set(readValue)
+ }
+ writeIndex++
+ }
+ return slice.Slice(0, writeIndex).Interface()
+}
+
+var trueValue = reflect.ValueOf(true)
+
+// firstUniqueList is an implementation of firstUnique using an O(N) hash set lookup to look for
+// duplicates.
+func firstUniqueMap(in interface{}) interface{} {
+ writeIndex := 0
+ slice := reflect.ValueOf(in)
+ length := slice.Len()
+ seen := reflect.MakeMapWithSize(reflect.MapOf(slice.Type().Elem(), trueValue.Type()), slice.Len())
+ for readIndex := 0; readIndex < length; readIndex++ {
+ readValue := slice.Index(readIndex)
+ if seen.MapIndex(readValue).IsValid() {
+ continue
+ }
+ seen.SetMapIndex(readValue, trueValue)
+ if readIndex != writeIndex {
+ writeValue := slice.Index(writeIndex)
+ writeValue.Set(readValue)
+ }
+ writeIndex++
+ }
+ return slice.Slice(0, writeIndex).Interface()
+}
+
+// reverseSliceInPlace reverses the elements of a slice in place. The argument must be a slice, but
+// is not type-safe due to the lack of reflection in Go.
+func reverseSliceInPlace(in interface{}) {
+ swapper := reflect.Swapper(in)
+ slice := reflect.ValueOf(in)
+ length := slice.Len()
+ for i, j := 0, length-1; i < j; i, j = i+1, j-1 {
+ swapper(i, j)
+ }
+}
+
+// reverseSlice returns a copy of a slice in reverse order. The argument must be a slice, but is
+// not type-safe due to the lack of reflection in Go.
+func reverseSlice(in interface{}) interface{} {
+ slice := reflect.ValueOf(in)
+ if !slice.IsValid() || slice.IsNil() {
+ return in
+ }
+ if slice.Kind() != reflect.Slice {
+ panic(fmt.Errorf("%t is not a slice", in))
+ }
+ length := slice.Len()
+ if length == 0 {
+ return in
+ }
+ out := reflect.MakeSlice(slice.Type(), length, length)
+ for i := 0; i < length; i++ {
+ out.Index(i).Set(slice.Index(length - 1 - i))
+ }
+ return out.Interface()
+}
+
+// copySlice returns a copy of a slice. The argument must be a slice, but is not type-safe due to
+// the lack of reflection in Go.
+func copySlice(in interface{}) interface{} {
+ slice := reflect.ValueOf(in)
+ if !slice.IsValid() || slice.IsNil() {
+ return in
+ }
+ length := slice.Len()
+ if length == 0 {
+ return in
+ }
+ out := reflect.MakeSlice(slice.Type(), length, length)
+ reflect.Copy(out, slice)
+ return out.Interface()
+}
diff --git a/android/depset_paths.go b/android/depset_paths.go
new file mode 100644
index 0000000..ed561ba
--- /dev/null
+++ b/android/depset_paths.go
@@ -0,0 +1,94 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+// This file implements DepSet, a thin type-safe wrapper around depSet that contains Paths.
+
+// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored
+// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency
+// DepSet nodes.
+//
+// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order
+// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
+// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
+// elements of children are listed after all of their parents (unless there are duplicate direct
+// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
+// duplicated element is not guaranteed).
+//
+// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents
+// and the *DepSets of dependencies. A DepSet is immutable once created.
+type DepSet struct {
+ depSet
+}
+
+// DepSetBuilder is used to create an immutable DepSet.
+type DepSetBuilder struct {
+ depSetBuilder
+}
+
+// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
+func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet {
+ return &DepSet{*newDepSet(order, direct, transitive)}
+}
+
+// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order.
+func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder {
+ return &DepSetBuilder{*newDepSetBuilder(order, Paths(nil))}
+}
+
+// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents.
+func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder {
+ b.depSetBuilder.DirectSlice(direct)
+ return b
+}
+
+// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
+// transitive contents are to the right of any existing transitive contents.
+func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder {
+ b.depSetBuilder.Transitive(transitive)
+ return b
+}
+
+// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents
+// for creating more DepSets.
+func (b *DepSetBuilder) Build() *DepSet {
+ return &DepSet{*b.depSetBuilder.Build()}
+}
+
+// ToList returns the DepSet flattened to a list. The order in the list is based on the order
+// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
+// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
+// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
+// its transitive dependencies, in which case the ordering of the duplicated element is not
+// guaranteed).
+func (d *DepSet) ToList() Paths {
+ if d == nil {
+ return nil
+ }
+ return d.toList(func(paths interface{}) interface{} {
+ return FirstUniquePaths(paths.(Paths))
+ }).(Paths)
+}
+
+// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order
+// with duplicates removed.
+func (d *DepSet) ToSortedList() Paths {
+ if d == nil {
+ return nil
+ }
+ paths := d.ToList()
+ return SortedUniquePaths(paths)
+}
diff --git a/android/depset_test.go b/android/depset_test.go
index c328127..955ccb0 100644
--- a/android/depset_test.go
+++ b/android/depset_test.go
@@ -17,6 +17,7 @@
import (
"fmt"
"reflect"
+ "strconv"
"strings"
"testing"
)
@@ -108,6 +109,7 @@
name: "builderReuse",
depSet: func(t *testing.T, order DepSetOrder) *DepSet {
assertEquals := func(t *testing.T, w, g Paths) {
+ t.Helper()
if !reflect.DeepEqual(w, g) {
t.Errorf("want %q, got %q", w, g)
}
@@ -302,3 +304,87 @@
})
}
}
+
+func Test_firstUnique(t *testing.T) {
+ f := func(t *testing.T, imp func([]string) []string, in, want []string) {
+ t.Helper()
+ out := imp(in)
+ if !reflect.DeepEqual(out, want) {
+ t.Errorf("incorrect output:")
+ t.Errorf(" input: %#v", in)
+ t.Errorf(" expected: %#v", want)
+ t.Errorf(" got: %#v", out)
+ }
+ }
+
+ for _, testCase := range firstUniqueStringsTestCases {
+ t.Run("list", func(t *testing.T) {
+ f(t, func(s []string) []string {
+ return firstUniqueList(s).([]string)
+ }, testCase.in, testCase.out)
+ })
+ t.Run("map", func(t *testing.T) {
+ f(t, func(s []string) []string {
+ return firstUniqueMap(s).([]string)
+ }, testCase.in, testCase.out)
+ })
+ }
+}
+
+func Benchmark_firstUnique(b *testing.B) {
+ implementations := []struct {
+ name string
+ f func([]string) []string
+ }{
+ {
+ name: "list",
+ f: func(slice []string) []string {
+ return firstUniqueList(slice).([]string)
+ },
+ },
+ {
+ name: "map",
+ f: func(slice []string) []string {
+ return firstUniqueMap(slice).([]string)
+ },
+ },
+ {
+ name: "optimal",
+ f: func(slice []string) []string {
+ return firstUnique(slice).([]string)
+ },
+ },
+ }
+ const maxSize = 1024
+ uniqueStrings := make([]string, maxSize)
+ for i := range uniqueStrings {
+ uniqueStrings[i] = strconv.Itoa(i)
+ }
+ sameString := make([]string, maxSize)
+ for i := range sameString {
+ sameString[i] = uniqueStrings[0]
+ }
+
+ f := func(b *testing.B, imp func([]string) []string, s []string) {
+ for i := 0; i < b.N; i++ {
+ b.ReportAllocs()
+ s = append([]string(nil), s...)
+ imp(s)
+ }
+ }
+
+ for n := 1; n <= maxSize; n <<= 1 {
+ b.Run(strconv.Itoa(n), func(b *testing.B) {
+ for _, implementation := range implementations {
+ b.Run(implementation.name, func(b *testing.B) {
+ b.Run("same", func(b *testing.B) {
+ f(b, implementation.f, sameString[:n])
+ })
+ b.Run("unique", func(b *testing.B) {
+ f(b, implementation.f, uniqueStrings[:n])
+ })
+ })
+ }
+ })
+ }
+}
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/license.go b/android/license.go
new file mode 100644
index 0000000..b140b55
--- /dev/null
+++ b/android/license.go
@@ -0,0 +1,82 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "github.com/google/blueprint"
+)
+
+type licenseKindDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licenseKindTag = licenseKindDependencyTag{}
+)
+
+func init() {
+ RegisterLicenseBuildComponents(InitRegistrationContext)
+}
+
+// Register the license module type.
+func RegisterLicenseBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license", LicenseFactory)
+}
+
+type licenseProperties struct {
+ // Specifies the kinds of license that apply.
+ License_kinds []string
+ // Specifies a short copyright notice to use for the license.
+ Copyright_notice *string
+ // Specifies the path or label for the text of the license.
+ License_text []string `android:"path"`
+ // Specifies the package name to which the license applies.
+ Package_name *string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseProperties
+}
+
+func (m *licenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, licenseKindTag, m.properties.License_kinds...)
+}
+
+func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseFactory() Module {
+ module := &licenseModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind.go b/android/license_kind.go
new file mode 100644
index 0000000..ddecd77
--- /dev/null
+++ b/android/license_kind.go
@@ -0,0 +1,66 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+func init() {
+ RegisterLicenseKindBuildComponents(InitRegistrationContext)
+}
+
+// Register the license_kind module type.
+func RegisterLicenseKindBuildComponents(ctx RegistrationContext) {
+ ctx.RegisterModuleType("license_kind", LicenseKindFactory)
+}
+
+type licenseKindProperties struct {
+ // Specifies the conditions for all licenses of the kind.
+ Conditions []string
+ // Specifies the url to the canonical license definition.
+ Url string
+ // Specifies where this license can be used
+ Visibility []string
+}
+
+type licenseKindModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties licenseKindProperties
+}
+
+func (m *licenseKindModule) DepsMutator(ctx BottomUpMutatorContext) {
+ // Nothing to do.
+}
+
+func (m *licenseKindModule) GenerateAndroidBuildActions(ModuleContext) {
+ // Nothing to do.
+}
+
+func LicenseKindFactory() Module {
+ module := &licenseKindModule{}
+
+ base := module.base()
+ module.AddProperties(&base.nameProperties, &module.properties)
+
+ base.generalProperties = module.GetProperties()
+ base.customizableProperties = module.GetProperties()
+
+ // The visibility property needs to be checked and parsed by the visibility module.
+ setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
+
+ initAndroidModuleBase(module)
+ InitDefaultableModule(module)
+
+ return module
+}
diff --git a/android/license_kind_test.go b/android/license_kind_test.go
new file mode 100644
index 0000000..767b64e
--- /dev/null
+++ b/android/license_kind_test.go
@@ -0,0 +1,174 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var licenseKindTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+}{
+ {
+ name: "license_kind must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_license",
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:4:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "bad license_kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_notice",
+ license_kinds: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: "other_notice" depends on undefined module "notice"`,
+ },
+ },
+ {
+ name: "good license kind",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_proprietary",
+ license_kinds: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple license kinds",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ }
+
+ mock_license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_license {
+ name: "other_rule",
+ license_kinds: ["top_by_exception_only"],
+ }`),
+ },
+ },
+}
+
+func TestLicenseKind(t *testing.T) {
+ for _, test := range licenseKindTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testLicenseKind(test.fs)
+
+ expectedErrors := test.expectedErrors
+ if expectedErrors == nil {
+ FailIfErrored(t, errs)
+ } else {
+ for _, expectedError := range expectedErrors {
+ FailIfNoMatchingErrors(t, expectedError, errs)
+ }
+ if len(errs) > len(expectedErrors) {
+ t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+ for i, expectedError := range expectedErrors {
+ t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+ }
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+ }
+ })
+ }
+}
+
+func testLicenseKind(fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as license_kind information is stored in the config.
+ config := TestArchConfig(buildDir, nil, "", fs)
+
+ ctx := NewTestArchContext(config)
+ RegisterLicenseKindBuildComponents(ctx)
+ ctx.RegisterModuleType("mock_license", newMockLicenseModule)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockLicenseProperties struct {
+ License_kinds []string
+}
+
+type mockLicenseModule struct {
+ ModuleBase
+ DefaultableModuleBase
+
+ properties mockLicenseProperties
+}
+
+func newMockLicenseModule() Module {
+ m := &mockLicenseModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type licensekindTag struct {
+ blueprint.BaseDependencyTag
+}
+
+func (j *mockLicenseModule) DepsMutator(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+ ctx.AddDependency(m, licensekindTag{}, j.properties.License_kinds...)
+}
+
+func (p *mockLicenseModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/license_test.go b/android/license_test.go
new file mode 100644
index 0000000..552bbae
--- /dev/null
+++ b/android/license_test.go
@@ -0,0 +1,233 @@
+package android
+
+import (
+ "testing"
+)
+
+var licenseTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+}{
+ {
+ name: "license must not accept licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_license",
+ visibility: ["//visibility:private"],
+ licenses: ["other_license"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
+ },
+ },
+ {
+ name: "private license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:private"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_allowed_as_notice"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_allowed_as_notice"],
+ }`),
+ },
+ expectedErrors: []string{
+ `other/Blueprints:2:5: module "arule": depends on //top:top_allowed_as_notice `+
+ `which is not visible to this module`,
+ `yetmore/Blueprints:2:5: module "//yetmore": depends on //top:top_allowed_as_notice `+
+ `which is not visible to this module`,
+ },
+ },
+ {
+ name: "must reference license_kind module",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ rule {
+ name: "top_by_exception_only",
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:6:5: module "top_proprietary": license_kinds property `+
+ `"top_by_exception_only" is not a license_kind module`,
+ },
+ },
+ {
+ name: "license_kind module must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top_notice_allowed",
+ license_kinds: ["top_notice"],
+ visibility: ["//visibility:public"],
+ }`),
+ },
+ expectedErrors: []string{
+ `top/Blueprints:2:5: "top_notice_allowed" depends on undefined module "top_notice"`,
+ },
+ },
+ {
+ name: "public license",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:private"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+ {
+ name: "multiple licenses",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }
+
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license_kind {
+ name: "top_by_exception_only",
+ conditions: ["by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+
+ license {
+ name: "top_allowed_as_notice",
+ license_kinds: ["top_notice"],
+ }
+
+ license {
+ name: "top_proprietary",
+ license_kinds: ["top_by_exception_only"],
+ visibility: ["//visibility:public"],
+ }
+ rule {
+ name: "myrule",
+ licenses: ["top_allowed_as_notice", "top_proprietary"]
+ }`),
+ "other/Blueprints": []byte(`
+ rule {
+ name: "arule",
+ licenses: ["top_proprietary"],
+ }`),
+ "yetmore/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_proprietary"],
+ }`),
+ },
+ },
+}
+
+func TestLicense(t *testing.T) {
+ for _, test := range licenseTests {
+ t.Run(test.name, func(t *testing.T) {
+ _, errs := testLicense(test.fs)
+
+ expectedErrors := test.expectedErrors
+ if expectedErrors == nil {
+ FailIfErrored(t, errs)
+ } else {
+ for _, expectedError := range expectedErrors {
+ FailIfNoMatchingErrors(t, expectedError, errs)
+ }
+ if len(errs) > len(expectedErrors) {
+ t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
+ for i, expectedError := range expectedErrors {
+ t.Errorf("expectedErrors[%d] = %s", i, expectedError)
+ }
+ for i, err := range errs {
+ t.Errorf("errs[%d] = %s", i, err)
+ }
+ }
+ }
+ })
+ }
+}
+
+func testLicense(fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as visibility information is stored in the config.
+ env := make(map[string]string)
+ env["ANDROID_REQUIRE_LICENSES"] = "1"
+ config := TestArchConfig(buildDir, env, "", fs)
+
+ ctx := NewTestArchContext(config)
+ RegisterPackageBuildComponents(ctx)
+ registerTestPrebuiltBuildComponents(ctx)
+ RegisterLicenseKindBuildComponents(ctx)
+ RegisterLicenseBuildComponents(ctx)
+ ctx.RegisterModuleType("rule", newMockRuleModule)
+ ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(RegisterLicensesPackageMapper)
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+ ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+ ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockRuleModule struct {
+ ModuleBase
+ DefaultableModuleBase
+}
+
+func newMockRuleModule() Module {
+ m := &mockRuleModule{}
+ InitAndroidModule(m)
+ InitDefaultableModule(m)
+ return m
+}
+
+func (p *mockRuleModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/licenses.go b/android/licenses.go
new file mode 100644
index 0000000..1000429
--- /dev/null
+++ b/android/licenses.go
@@ -0,0 +1,295 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "reflect"
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+// Adds cross-cutting licenses dependency to propagate license metadata through the build system.
+//
+// Stage 1 - bottom-up records package-level default_applicable_licenses property mapped by package name.
+// Stage 2 - bottom-up converts licenses property or package default_applicable_licenses to dependencies.
+// Stage 3 - bottom-up type-checks every added applicable license dependency and license_kind dependency.
+// Stage 4 - GenerateBuildActions calculates properties for the union of license kinds, conditions and texts.
+
+type licensesDependencyTag struct {
+ blueprint.BaseDependencyTag
+}
+
+var (
+ licensesTag = licensesDependencyTag{}
+)
+
+// Describes the property provided by a module to reference applicable licenses.
+type applicableLicensesProperty interface {
+ // The name of the property. e.g. default_applicable_licenses or licenses
+ getName() string
+ // The values assigned to the property. (Must reference license modules.)
+ getStrings() []string
+}
+
+type applicableLicensesPropertyImpl struct {
+ name string
+ licensesProperty *[]string
+}
+
+func newApplicableLicensesProperty(name string, licensesProperty *[]string) applicableLicensesProperty {
+ return applicableLicensesPropertyImpl{
+ name: name,
+ licensesProperty: licensesProperty,
+ }
+}
+
+func (p applicableLicensesPropertyImpl) getName() string {
+ return p.name
+}
+
+func (p applicableLicensesPropertyImpl) getStrings() []string {
+ return *p.licensesProperty
+}
+
+// Set the primary applicable licenses property for a module.
+func setPrimaryLicensesProperty(module Module, name string, licensesProperty *[]string) {
+ module.base().primaryLicensesProperty = newApplicableLicensesProperty(name, licensesProperty)
+}
+
+// Storage blob for a package's default_applicable_licenses mapped by package directory.
+type licensesContainer struct {
+ licenses []string
+}
+
+func (r licensesContainer) getLicenses() []string {
+ return r.licenses
+}
+
+var packageDefaultLicensesMap = NewOnceKey("packageDefaultLicensesMap")
+
+// The map from package dir name to default applicable licenses as a licensesContainer.
+func moduleToPackageDefaultLicensesMap(config Config) *sync.Map {
+ return config.Once(packageDefaultLicensesMap, func() interface{} {
+ return &sync.Map{}
+ }).(*sync.Map)
+}
+
+// Registers the function that maps each package to its default_applicable_licenses.
+//
+// This goes before defaults expansion so the defaults can pick up the package default.
+func RegisterLicensesPackageMapper(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPackageMapper", licensesPackageMapper).Parallel()
+}
+
+// Registers the function that gathers the license dependencies for each module.
+//
+// This goes after defaults expansion so that it can pick up default licenses and before visibility enforcement.
+func RegisterLicensesPropertyGatherer(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyGatherer", licensesPropertyGatherer).Parallel()
+}
+
+// Registers the function that verifies the licenses and license_kinds dependency types for each module.
+func RegisterLicensesDependencyChecker(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("licensesPropertyChecker", licensesDependencyChecker).Parallel()
+}
+
+// Maps each package to its default applicable licenses.
+func licensesPackageMapper(ctx BottomUpMutatorContext) {
+ p, ok := ctx.Module().(*packageModule)
+ if !ok {
+ return
+ }
+
+ licenses := getLicenses(ctx, p)
+
+ dir := ctx.ModuleDir()
+ c := makeLicensesContainer(licenses)
+ moduleToPackageDefaultLicensesMap(ctx.Config()).Store(dir, c)
+}
+
+// Copies the default_applicable_licenses property values for mapping by package directory.
+func makeLicensesContainer(propVals []string) licensesContainer {
+ licenses := make([]string, 0, len(propVals))
+ licenses = append(licenses, propVals...)
+
+ return licensesContainer{licenses}
+}
+
+// Gathers the applicable licenses into dependency references after defaults expansion.
+func licensesPropertyGatherer(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ licenses := getLicenses(ctx, m)
+
+ ctx.AddVariationDependencies(nil, licensesTag, licenses...)
+}
+
+// Verifies the license and license_kind dependencies are each the correct kind of module.
+func licensesDependencyChecker(ctx BottomUpMutatorContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if _, ok := m.(*licenseModule); ok {
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if _, ok := module.(*licenseKindModule); !ok {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if _, ok := module.(*licenseModule); !ok {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Flattens license and license_kind dependencies into calculated properties.
+//
+// Re-validates applicable licenses properties refer only to license modules and license_kinds properties refer
+// only to license_kind modules.
+func licensesPropertyFlattener(ctx ModuleContext) {
+ m, ok := ctx.Module().(Module)
+ if !ok {
+ return
+ }
+
+ // license modules have no licenses, but license_kinds must refer to license_kind modules
+ if l, ok := m.(*licenseModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
+ mergeProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, l.properties.License_text).Strings()...)
+ for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
+ if lk, ok := module.(*licenseKindModule); ok {
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, ctx.OtherModuleName(module))
+ } else {
+ ctx.ModuleErrorf("license_kinds property %q is not a license_kind module", ctx.OtherModuleName(module))
+ }
+ }
+ return
+ }
+
+ if exemptFromRequiredApplicableLicensesProperty(m) {
+ return
+ }
+
+ for _, module := range ctx.GetDirectDepsWithTag(licensesTag) {
+ if l, ok := module.(*licenseModule); ok {
+ if m.base().commonProperties.Effective_package_name == nil && l.properties.Package_name != nil {
+ m.base().commonProperties.Effective_package_name = l.properties.Package_name
+ }
+ mergeProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
+ mergeProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+ mergeProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
+ mergeProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
+ } else {
+ propertyName := "licenses"
+ primaryProperty := m.base().primaryLicensesProperty
+ if primaryProperty != nil {
+ propertyName = primaryProperty.getName()
+ }
+ ctx.ModuleErrorf("%s property %q is not a license module", propertyName, ctx.OtherModuleName(module))
+ }
+ }
+}
+
+// Update a property string array with a distinct union of its values and a list of new values.
+func mergeProps(prop *[]string, values ...string) {
+ s := make(map[string]bool)
+ for _, v := range *prop {
+ s[v] = true
+ }
+ for _, v := range values {
+ s[v] = true
+ }
+ *prop = []string{}
+ *prop = append(*prop, SortedStringKeys(s)...)
+}
+
+// Get the licenses property falling back to the package default.
+func getLicenses(ctx BaseModuleContext, module Module) []string {
+ if exemptFromRequiredApplicableLicensesProperty(module) {
+ return nil
+ }
+
+ primaryProperty := module.base().primaryLicensesProperty
+ if primaryProperty == nil {
+ if ctx.Config().IsEnvTrue("ANDROID_REQUIRE_LICENSES") {
+ ctx.ModuleErrorf("module type %q must have an applicable licenses property", ctx.OtherModuleType(module))
+ }
+ return nil
+ }
+
+ licenses := primaryProperty.getStrings()
+ if len(licenses) > 0 {
+ s := make(map[string]bool)
+ for _, l := range licenses {
+ if _, ok := s[l]; ok {
+ ctx.ModuleErrorf("duplicate %q %s", l, primaryProperty.getName())
+ }
+ s[l] = true
+ }
+ return licenses
+ }
+
+ dir := ctx.OtherModuleDir(module)
+
+ moduleToApplicableLicenses := moduleToPackageDefaultLicensesMap(ctx.Config())
+ value, ok := moduleToApplicableLicenses.Load(dir)
+ var c licensesContainer
+ if ok {
+ c = value.(licensesContainer)
+ } else {
+ c = licensesContainer{}
+ }
+ return c.getLicenses()
+}
+
+// Returns whether a module is an allowed list of modules that do not have or need applicable licenses.
+func exemptFromRequiredApplicableLicensesProperty(module Module) bool {
+ switch reflect.TypeOf(module).String() {
+ case "*android.licenseModule": // is a license, doesn't need one
+ case "*android.licenseKindModule": // is a license, doesn't need one
+ case "*android.NamespaceModule": // just partitions things, doesn't add anything
+ case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses
+ case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses
+ case "*android.soongConfigStringVariableDummyModule": // used for creating aliases
+ case "*android.SoongConfigBoolVariableDummyModule": // used for creating aliases
+ default:
+ return false
+ }
+ return true
+}
diff --git a/android/licenses_test.go b/android/licenses_test.go
new file mode 100644
index 0000000..b94add7
--- /dev/null
+++ b/android/licenses_test.go
@@ -0,0 +1,863 @@
+package android
+
+import (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+var licensesTests = []struct {
+ name string
+ fs map[string][]byte
+ expectedErrors []string
+ effectiveLicenses map[string][]string
+ effectiveInheritedLicenses map[string][]string
+ effectivePackage map[string]string
+ effectiveNotices map[string][]string
+ effectiveKinds map[string][]string
+ effectiveConditions map[string][]string
+}{
+ {
+ name: "invalid module type without licenses property",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_bad_module {
+ name: "libexample",
+ }`),
+ },
+ expectedErrors: []string{`module type "mock_bad_module" must have an applicable licenses property`},
+ },
+ {
+ name: "license must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_library {
+ name: "libexample",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"libexample" depends on undefined module "notice"`},
+ },
+ {
+ name: "all good",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "notice",
+ conditions: ["shownotice"],
+ }
+
+ license {
+ name: "top_Apache2",
+ license_kinds: ["notice"],
+ package_name: "topDog",
+ license_text: ["LICENSE", "NOTICE"],
+ }
+
+ mock_library {
+ name: "libexample1",
+ licenses: ["top_Apache2"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ mock_library {
+ name: "libnested",
+ licenses: ["top_Apache2"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ licenses: ["top_Apache2"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample1": []string{"top_Apache2"},
+ "libnested": []string{"top_Apache2"},
+ "libother": []string{"top_Apache2"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample1": []string{"notice"},
+ "libnested": []string{"notice"},
+ "libother": []string{"notice"},
+ },
+ effectivePackage: map[string]string{
+ "libexample1": "topDog",
+ "libnested": "topDog",
+ "libother": "topDog",
+ },
+ effectiveConditions: map[string][]string{
+ "libexample1": []string{"shownotice"},
+ "libnested": []string{"shownotice"},
+ "libother": []string{"shownotice"},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample1": []string{"top/LICENSE", "top/NOTICE"},
+ "libnested": []string{"top/LICENSE", "top/NOTICE"},
+ "libother": []string{"top/LICENSE", "top/NOTICE"},
+ },
+ },
+
+ // Defaults propagation tests
+ {
+ // Check that licenses is the union of the defaults modules.
+ name: "defaults union, basic",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license_kind {
+ name: "top_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "top_other",
+ license_kinds: ["top_notice"],
+ }
+
+ mock_defaults {
+ name: "libexample_defaults",
+ licenses: ["top_other"],
+ }
+ mock_library {
+ name: "libexample",
+ licenses: ["nested_other"],
+ defaults: ["libexample_defaults"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license_kind {
+ name: "nested_notice",
+ conditions: ["notice"],
+ }
+
+ license {
+ name: "nested_other",
+ license_kinds: ["nested_notice"],
+ }
+
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"nested_other", "top_other"},
+ "libsamepackage": []string{"nested_other", "top_other"},
+ "libnested": []string{"nested_other", "top_other"},
+ "libother": []string{"nested_other", "top_other"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{"nested_notice", "top_notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ effectiveConditions: map[string][]string{
+ "libexample": []string{"notice"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ },
+ },
+ {
+ name: "defaults union, multiple defaults",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ license {
+ name: "top",
+ }
+ mock_defaults {
+ name: "libexample_defaults_1",
+ licenses: ["other"],
+ }
+ mock_defaults {
+ name: "libexample_defaults_2",
+ licenses: ["top_nested"],
+ }
+ mock_library {
+ name: "libexample",
+ defaults: ["libexample_defaults_1", "libexample_defaults_2"],
+ }
+ mock_library {
+ name: "libsamepackage",
+ deps: ["libexample"],
+ }`),
+ "top/nested/Blueprints": []byte(`
+ license {
+ name: "top_nested",
+ license_text: ["LICENSE.txt"],
+ }
+ mock_library {
+ name: "libnested",
+ deps: ["libexample"],
+ }`),
+ "other/Blueprints": []byte(`
+ license {
+ name: "other",
+ }
+ mock_library {
+ name: "libother",
+ deps: ["libexample"],
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"other", "top_nested"},
+ "libsamepackage": []string{"other", "top_nested"},
+ "libnested": []string{"other", "top_nested"},
+ "libother": []string{"other", "top_nested"},
+ "liboutsider": []string{"other", "top_nested"},
+ },
+ effectiveKinds: map[string][]string{
+ "libexample": []string{},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveNotices: map[string][]string{
+ "libexample": []string{"top/nested/LICENSE.txt"},
+ "libsamepackage": []string{},
+ "libnested": []string{},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ },
+
+ // Defaults module's defaults_licenses tests
+ {
+ name: "defaults_licenses invalid",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"top_defaults" depends on undefined module "notice"`},
+ },
+ {
+ name: "defaults_licenses overrides package default",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["by_exception_only"],
+ }
+ license {
+ name: "by_exception_only",
+ }
+ license {
+ name: "notice",
+ }
+ mock_defaults {
+ name: "top_defaults",
+ licenses: ["notice"],
+ }
+ mock_library {
+ name: "libexample",
+ }
+ mock_library {
+ name: "libdefaults",
+ defaults: ["top_defaults"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"by_exception_only"},
+ "libdefaults": []string{"notice"},
+ },
+ },
+
+ // Package default_applicable_licenses tests
+ {
+ name: "package default_applicable_licenses must exist",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["notice"],
+ }`),
+ },
+ expectedErrors: []string{`"//top" depends on undefined module "notice"`},
+ },
+ {
+ // This test relies on the default licenses being legacy_public.
+ name: "package default_applicable_licenses property used when no licenses specified",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "outsider/Blueprints": []byte(`
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "liboutsider": []string{"top_notice"},
+ },
+ },
+ {
+ name: "package default_applicable_licenses not inherited to subpackages",
+ fs: map[string][]byte{
+ "top/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["top_notice"],
+ }
+ license {
+ name: "top_notice",
+ }
+ mock_library {
+ name: "libexample",
+ }`),
+ "top/nested/Blueprints": []byte(`
+ package {
+ default_applicable_licenses: ["outsider"],
+ }
+
+ mock_library {
+ name: "libnested",
+ }`),
+ "top/other/Blueprints": []byte(`
+ mock_library {
+ name: "libother",
+ }`),
+ "outsider/Blueprints": []byte(`
+ license {
+ name: "outsider",
+ }
+ mock_library {
+ name: "liboutsider",
+ deps: ["libexample", "libother", "libnested"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "libexample": []string{"top_notice"},
+ "libnested": []string{"outsider"},
+ "libother": []string{},
+ "liboutsider": []string{"top_notice", "outsider"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are included",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+ {
+ name: "verify that prebuilt dependencies are ignored for licenses reasons (preferred)",
+ fs: map[string][]byte{
+ "prebuilts/Blueprints": []byte(`
+ license {
+ name: "prebuilt"
+ }
+ prebuilt {
+ name: "module",
+ licenses: ["prebuilt"],
+ prefer: true,
+ }`),
+ "top/sources/source_file": nil,
+ "top/sources/Blueprints": []byte(`
+ license {
+ name: "top_sources"
+ }
+ source {
+ name: "module",
+ licenses: ["top_sources"],
+ }`),
+ "top/other/source_file": nil,
+ "top/other/Blueprints": []byte(`
+ source {
+ name: "other",
+ deps: [":module"],
+ }`),
+ },
+ effectiveLicenses: map[string][]string{
+ "other": []string{},
+ },
+ effectiveInheritedLicenses: map[string][]string{
+ "module": []string{"prebuilt", "top_sources"},
+ "other": []string{"prebuilt", "top_sources"},
+ },
+ },
+}
+
+func TestLicenses(t *testing.T) {
+ for _, test := range licensesTests {
+ t.Run(test.name, func(t *testing.T) {
+ ctx, errs := testLicenses(buildDir, test.fs)
+
+ CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+
+ if test.effectiveLicenses != nil {
+ checkEffectiveLicenses(t, ctx, test.effectiveLicenses)
+ }
+
+ if test.effectivePackage != nil {
+ checkEffectivePackage(t, ctx, test.effectivePackage)
+ }
+
+ if test.effectiveNotices != nil {
+ checkEffectiveNotices(t, ctx, test.effectiveNotices)
+ }
+
+ if test.effectiveKinds != nil {
+ checkEffectiveKinds(t, ctx, test.effectiveKinds)
+ }
+
+ if test.effectiveConditions != nil {
+ checkEffectiveConditions(t, ctx, test.effectiveConditions)
+ }
+
+ if test.effectiveInheritedLicenses != nil {
+ checkEffectiveInheritedLicenses(t, ctx, test.effectiveInheritedLicenses)
+ }
+ })
+ }
+}
+
+func checkEffectiveLicenses(t *testing.T, ctx *TestContext, effectiveLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualLicenses[m.Name()] = base.commonProperties.Effective_licenses
+ })
+
+ for moduleName, expectedLicenses := range effectiveLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedLicenses, licenses) {
+ t.Errorf("effective licenses mismatch for module %q: expected %q, found %q", moduleName, expectedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectiveInheritedLicenses(t *testing.T, ctx *TestContext, effectiveInheritedLicenses map[string][]string) {
+ actualLicenses := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ inherited := make(map[string]bool)
+ for _, l := range base.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ ctx.Context.Context.VisitDepsDepthFirst(m, func(c blueprint.Module) {
+ if _, ok := c.(*licenseModule); ok {
+ return
+ }
+ if _, ok := c.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := c.(*packageModule); ok {
+ return
+ }
+ cmodule, ok := c.(Module)
+ if !ok {
+ t.Errorf("%q not a module", c.Name())
+ return
+ }
+ cbase := cmodule.base()
+ if cbase == nil {
+ return
+ }
+ for _, l := range cbase.commonProperties.Effective_licenses {
+ inherited[l] = true
+ }
+ })
+ actualLicenses[m.Name()] = []string{}
+ for l := range inherited {
+ actualLicenses[m.Name()] = append(actualLicenses[m.Name()], l)
+ }
+ })
+
+ for moduleName, expectedInheritedLicenses := range effectiveInheritedLicenses {
+ licenses, ok := actualLicenses[moduleName]
+ if !ok {
+ licenses = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedInheritedLicenses, licenses) {
+ t.Errorf("effective inherited licenses mismatch for module %q: expected %q, found %q", moduleName, expectedInheritedLicenses, licenses)
+ }
+ }
+}
+
+func checkEffectivePackage(t *testing.T, ctx *TestContext, effectivePackage map[string]string) {
+ actualPackage := make(map[string]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+
+ if base.commonProperties.Effective_package_name == nil {
+ actualPackage[m.Name()] = ""
+ } else {
+ actualPackage[m.Name()] = *base.commonProperties.Effective_package_name
+ }
+ })
+
+ for moduleName, expectedPackage := range effectivePackage {
+ packageName, ok := actualPackage[moduleName]
+ if !ok {
+ packageName = ""
+ }
+ if expectedPackage != packageName {
+ t.Errorf("effective package mismatch for module %q: expected %q, found %q", moduleName, expectedPackage, packageName)
+ }
+ }
+}
+
+func checkEffectiveNotices(t *testing.T, ctx *TestContext, effectiveNotices map[string][]string) {
+ actualNotices := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualNotices[m.Name()] = base.commonProperties.Effective_license_text
+ })
+
+ for moduleName, expectedNotices := range effectiveNotices {
+ notices, ok := actualNotices[moduleName]
+ if !ok {
+ notices = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedNotices, notices) {
+ t.Errorf("effective notice files mismatch for module %q: expected %q, found %q", moduleName, expectedNotices, notices)
+ }
+ }
+}
+
+func checkEffectiveKinds(t *testing.T, ctx *TestContext, effectiveKinds map[string][]string) {
+ actualKinds := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualKinds[m.Name()] = base.commonProperties.Effective_license_kinds
+ })
+
+ for moduleName, expectedKinds := range effectiveKinds {
+ kinds, ok := actualKinds[moduleName]
+ if !ok {
+ kinds = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedKinds, kinds) {
+ t.Errorf("effective license kinds mismatch for module %q: expected %q, found %q", moduleName, expectedKinds, kinds)
+ }
+ }
+}
+
+func checkEffectiveConditions(t *testing.T, ctx *TestContext, effectiveConditions map[string][]string) {
+ actualConditions := make(map[string][]string)
+ ctx.Context.Context.VisitAllModules(func(m blueprint.Module) {
+ if _, ok := m.(*licenseModule); ok {
+ return
+ }
+ if _, ok := m.(*licenseKindModule); ok {
+ return
+ }
+ if _, ok := m.(*packageModule); ok {
+ return
+ }
+ module, ok := m.(Module)
+ if !ok {
+ t.Errorf("%q not a module", m.Name())
+ return
+ }
+ base := module.base()
+ if base == nil {
+ return
+ }
+ actualConditions[m.Name()] = base.commonProperties.Effective_license_conditions
+ })
+
+ for moduleName, expectedConditions := range effectiveConditions {
+ conditions, ok := actualConditions[moduleName]
+ if !ok {
+ conditions = []string{}
+ }
+ if !compareUnorderedStringArrays(expectedConditions, conditions) {
+ t.Errorf("effective license conditions mismatch for module %q: expected %q, found %q", moduleName, expectedConditions, conditions)
+ }
+ }
+}
+
+func compareUnorderedStringArrays(expected, actual []string) bool {
+ if len(expected) != len(actual) {
+ return false
+ }
+ s := make(map[string]int)
+ for _, v := range expected {
+ s[v] += 1
+ }
+ for _, v := range actual {
+ c, ok := s[v]
+ if !ok {
+ return false
+ }
+ if c < 1 {
+ return false
+ }
+ s[v] -= 1
+ }
+ return true
+}
+
+func testLicenses(buildDir string, fs map[string][]byte) (*TestContext, []error) {
+
+ // Create a new config per test as licenses information is stored in the config.
+ env := make(map[string]string)
+ env["ANDROID_REQUIRE_LICENSES"] = "1"
+ config := TestArchConfig(buildDir, env, "", fs)
+
+ ctx := NewTestArchContext(config)
+ ctx.RegisterModuleType("mock_bad_module", newMockLicensesBadModule)
+ ctx.RegisterModuleType("mock_library", newMockLicensesLibraryModule)
+ ctx.RegisterModuleType("mock_defaults", defaultsLicensesFactory)
+
+ // Order of the following method calls is significant.
+ RegisterPackageBuildComponents(ctx)
+ registerTestPrebuiltBuildComponents(ctx)
+ RegisterLicenseKindBuildComponents(ctx)
+ RegisterLicenseBuildComponents(ctx)
+ ctx.PreArchMutators(RegisterVisibilityRuleChecker)
+ ctx.PreArchMutators(RegisterLicensesPackageMapper)
+ ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
+ ctx.PreArchMutators(RegisterLicensesPropertyGatherer)
+ ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
+ ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
+ ctx.PostDepsMutators(RegisterLicensesDependencyChecker)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles(".")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+type mockLicensesBadProperties struct {
+ Visibility []string
+}
+
+type mockLicensesBadModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesBadProperties
+}
+
+func newMockLicensesBadModule() Module {
+ m := &mockLicensesBadModule{}
+
+ base := m.base()
+ m.AddProperties(&base.nameProperties, &m.properties)
+
+ base.generalProperties = m.GetProperties()
+ base.customizableProperties = m.GetProperties()
+
+ // The default_visibility property needs to be checked and parsed by the visibility module during
+ // its checking and parsing phases so make it the primary visibility property.
+ setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
+
+ initAndroidModuleBase(m)
+ InitDefaultableModule(m)
+
+ return m
+}
+
+func (m *mockLicensesBadModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesLibraryProperties struct {
+ Deps []string
+}
+
+type mockLicensesLibraryModule struct {
+ ModuleBase
+ DefaultableModuleBase
+ properties mockLicensesLibraryProperties
+}
+
+func newMockLicensesLibraryModule() Module {
+ m := &mockLicensesLibraryModule{}
+ m.AddProperties(&m.properties)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
+ InitDefaultableModule(m)
+ return m
+}
+
+type dependencyLicensesTag struct {
+ blueprint.BaseDependencyTag
+ name string
+}
+
+func (j *mockLicensesLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, dependencyLicensesTag{name: "mockdeps"}, j.properties.Deps...)
+}
+
+func (p *mockLicensesLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+}
+
+type mockLicensesDefaults struct {
+ ModuleBase
+ DefaultsModuleBase
+}
+
+func defaultsLicensesFactory() Module {
+ m := &mockLicensesDefaults{}
+ InitDefaultsModule(m)
+ return m
+}
diff --git a/android/makefile_goal.go b/android/makefile_goal.go
index b5d9d69..07354a6 100644
--- a/android/makefile_goal.go
+++ b/android/makefile_goal.go
@@ -80,7 +80,7 @@
Class: "ETC",
OutputFile: OptionalPathForPath(p.outputFilePath),
ExtraFooters: []AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
// Can't use Cp because inputPath() is not a valid Path.
fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath)
},
diff --git a/android/makevars.go b/android/makevars.go
index 5101436..40c0ccd 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -90,6 +90,7 @@
ModuleDir(module blueprint.Module) string
ModuleSubDir(module blueprint.Module) string
ModuleType(module blueprint.Module) string
+ ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
BlueprintFile(module blueprint.Module) string
ModuleErrorf(module blueprint.Module, format string, args ...interface{})
@@ -133,8 +134,6 @@
// SingletonMakeVarsProvider is a Singleton with an extra method to provide extra values to be exported to Make.
type SingletonMakeVarsProvider interface {
- Singleton
-
// MakeVars uses a MakeVarsContext to provide extra values to be exported to Make.
MakeVars(ctx MakeVarsContext)
}
diff --git a/android/module.go b/android/module.go
index b6729c0..68008c2 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"fmt"
"os"
"path"
@@ -331,6 +332,8 @@
type ModuleContext interface {
BaseModuleContext
+ blueprintModuleContext() blueprint.ModuleContext
+
// Deprecated: use ModuleContext.Build instead.
ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@@ -338,10 +341,51 @@
ExpandSource(srcFile, prop string) Path
ExpandOptionalSource(srcFile *string, prop string) OptionalPath
+ // InstallExecutable creates a rule to copy srcPath to name in the installPath directory,
+ // with the given additional dependencies. The file is marked executable after copying.
+ //
+ // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
+ // installed file will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+
+ // InstallFile creates a rule to copy srcPath to name in the installPath directory,
+ // with the given additional dependencies.
+ //
+ // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
+ // installed file will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
+
+ // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath
+ // directory.
+ //
+ // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
+ // installed file will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
+
+ // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name
+ // in the installPath directory.
+ //
+ // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
+ // installed file will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
+
+ // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating
+ // the rule to copy the file. This is useful to define how a module would be packaged
+ // without installing it into the global installation directories.
+ //
+ // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by
+ // TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+ // for which IsInstallDepNeeded returns true.
+ PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
+
CheckbuildFile(srcPath Path)
InstallInData() bool
@@ -409,7 +453,8 @@
InstallInRoot() bool
InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
- SkipInstall()
+ HideFromMake()
+ IsHideFromMake() bool
IsSkipInstall() bool
MakeUninstallable()
ReplacedByPrebuilt()
@@ -441,6 +486,30 @@
FilesToInstall() InstallPaths
PackagingSpecs() []PackagingSpec
+
+ // TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive
+ // dependencies with dependency tags for which IsInstallDepNeeded() returns true.
+ 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
@@ -572,6 +641,20 @@
// more details.
Visibility []string
+ // Describes the licenses applicable to this module. Must reference license modules.
+ Licenses []string
+
+ // Flattened from direct license dependencies. Equal to Licenses unless particular module adds more.
+ Effective_licenses []string `blueprint:"mutated"`
+ // Override of module name when reporting licenses
+ Effective_package_name *string `blueprint:"mutated"`
+ // Notice files
+ Effective_license_text []string `blueprint:"mutated"`
+ // License names
+ Effective_license_kinds []string `blueprint:"mutated"`
+ // License conditions
+ Effective_license_conditions []string `blueprint:"mutated"`
+
// control whether this module compiles for 32-bit, 64-bit, or both. Possible values
// are "32" (compile for 32-bit only), "64" (compile for 64-bit only), "both" (compile for both
// architectures), or "first" (compile for 64-bit on a 64-bit platform, and 32-bit on a 32-bit
@@ -704,6 +787,13 @@
// Set by osMutator.
CommonOSVariant bool `blueprint:"mutated"`
+ // When HideFromMake is set to true, no entry for this variant will be emitted in the
+ // generated Android.mk file.
+ HideFromMake bool `blueprint:"mutated"`
+
+ // When SkipInstall is set to true, calls to ctx.InstallFile, ctx.InstallExecutable,
+ // ctx.InstallSymlink and ctx.InstallAbsoluteSymlink act like calls to ctx.PackageFile
+ // and don't create a rule to install the file.
SkipInstall bool `blueprint:"mutated"`
// Whether the module has been replaced by a prebuilt
@@ -886,6 +976,10 @@
// The default_visibility property needs to be checked and parsed by the visibility module during
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
+
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(m, "licenses", &base.commonProperties.Licenses)
}
// InitAndroidArchModule initializes the Module as an Android module that is architecture-specific.
@@ -996,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
@@ -1003,12 +1100,17 @@
// The primary visibility property, may be nil, that controls access to the module.
primaryVisibilityProperty visibilityProperty
- noAddressSanitizer bool
- installFiles InstallPaths
- checkbuildFiles Paths
- packagingSpecs []PackagingSpec
- noticeFiles Paths
- phonies map[string]Paths
+ // The primary licenses property, may be nil, records license metadata for the module.
+ primaryLicensesProperty applicableLicensesProperty
+
+ noAddressSanitizer bool
+ installFiles InstallPaths
+ installFilesDepSet *installPathsDepSet
+ checkbuildFiles Paths
+ packagingSpecs []PackagingSpec
+ packagingSpecsDepSet *packagingSpecsDepSet
+ noticeFiles Paths
+ phonies map[string]Paths
// The files to copy to the dist as explicitly specified in the .bp file.
distFiles TaggedDistFiles
@@ -1306,26 +1408,39 @@
m.commonProperties.ForcedDisabled = true
}
+// HideFromMake marks this variant so that it is not emitted in the generated Android.mk file.
+func (m *ModuleBase) HideFromMake() {
+ m.commonProperties.HideFromMake = true
+}
+
+// IsHideFromMake returns true if HideFromMake was previously called.
+func (m *ModuleBase) IsHideFromMake() bool {
+ return m.commonProperties.HideFromMake == true
+}
+
+// SkipInstall marks this variant to not create install rules when ctx.Install* are called.
func (m *ModuleBase) SkipInstall() {
m.commonProperties.SkipInstall = true
}
+// IsSkipInstall returns true if this variant is marked to not create install
+// rules when ctx.Install* are called.
func (m *ModuleBase) IsSkipInstall() bool {
- return m.commonProperties.SkipInstall == true
+ return m.commonProperties.SkipInstall
}
-// Similar to SkipInstall, but if the AndroidMk entry would set
+// Similar to HideFromMake, but if the AndroidMk entry would set
// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
// rather than leaving it out altogether. That happens in cases where it would
// have other side effects, in particular when it adds a NOTICE file target,
// which other install targets might depend on.
func (m *ModuleBase) MakeUninstallable() {
- m.SkipInstall()
+ m.HideFromMake()
}
func (m *ModuleBase) ReplacedByPrebuilt() {
m.commonProperties.ReplacedByPrebuilt = true
- m.SkipInstall()
+ m.HideFromMake()
}
func (m *ModuleBase) IsReplacedByPrebuilt() bool {
@@ -1338,19 +1453,17 @@
// computeInstallDeps finds the installed paths of all dependencies that have a dependency
// tag that is annotated as needing installation via the IsInstallDepNeeded method.
-func (m *ModuleBase) computeInstallDeps(ctx blueprint.ModuleContext) InstallPaths {
- var result InstallPaths
- ctx.WalkDeps(func(child, parent blueprint.Module) bool {
- if a, ok := child.(Module); ok {
- if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
- result = append(result, a.FilesToInstall()...)
- return true
- }
+func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
+ var installDeps []*installPathsDepSet
+ var packagingSpecs []*packagingSpecsDepSet
+ ctx.VisitDirectDeps(func(dep Module) {
+ if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+ installDeps = append(installDeps, dep.base().installFilesDepSet)
+ packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
}
- return false
})
- return result
+ return installDeps, packagingSpecs
}
func (m *ModuleBase) FilesToInstall() InstallPaths {
@@ -1361,6 +1474,10 @@
return m.packagingSpecs
}
+func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
+ return m.packagingSpecsDepSet.ToList()
+}
+
func (m *ModuleBase) NoAddressSanitizer() bool {
return m.noAddressSanitizer
}
@@ -1587,11 +1704,15 @@
module: m.module,
bp: blueprintCtx,
baseModuleContext: m.baseModuleContextFactory(blueprintCtx),
- installDeps: m.computeInstallDeps(blueprintCtx),
- installFiles: m.installFiles,
variables: make(map[string]string),
}
+ dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
+ // set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
+ // of installed files of this module. It will be replaced by a depset including the installed
+ // files of this module at the end for use by modules that depend on this one.
+ m.installFilesDepSet = newInstallPathsDepSet(nil, dependencyInstallFiles)
+
// Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never
// reporting missing dependency errors in Blueprint when AllowMissingDependencies == true.
// TODO: This will be removed once defaults modules handle missing dependency errors
@@ -1663,6 +1784,11 @@
}
}
+ licensesPropertyFlattener(ctx)
+ if ctx.Failed() {
+ return
+ }
+
m.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
@@ -1700,6 +1826,9 @@
}
}
+ m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
+ m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
+
m.buildParams = ctx.buildParams
m.ruleParams = ctx.ruleParams
m.variables = ctx.variables
@@ -1874,7 +2003,6 @@
bp blueprint.ModuleContext
baseModuleContext
packagingSpecs []PackagingSpec
- installDeps InstallPaths
installFiles InstallPaths
checkbuildFiles Paths
module Module
@@ -2330,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)
@@ -2383,11 +2507,15 @@
return m.module.InstallForceOS()
}
-func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
+func (m *moduleContext) skipInstall() bool {
if m.module.base().commonProperties.SkipInstall {
return true
}
+ if m.module.base().commonProperties.HideFromMake {
+ return true
+ }
+
// We'll need a solution for choosing which of modules with the same name in different
// namespaces to install. For now, reuse the list of namespaces exported to Make as the
// list of namespaces to install in a Soong-only build.
@@ -2414,14 +2542,29 @@
return m.installFile(installPath, name, srcPath, deps, true)
}
+func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec {
+ fullInstallPath := installPath.Join(m, name)
+ return m.packageFile(fullInstallPath, srcPath, false)
+}
+
+func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
+ spec := PackagingSpec{
+ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+ srcPath: srcPath,
+ symlinkTarget: "",
+ executable: executable,
+ }
+ m.packagingSpecs = append(m.packagingSpecs, spec)
+ return spec
+}
+
func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, executable bool) InstallPath {
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
- if !m.skipInstall(fullInstallPath) {
-
- deps = append(deps, m.installDeps.Paths()...)
+ if !m.skipInstall() {
+ deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
var implicitDeps, orderOnlyDeps Paths
@@ -2451,14 +2594,10 @@
m.installFiles = append(m.installFiles, fullInstallPath)
}
- m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
- relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
- srcPath: srcPath,
- symlinkTarget: "",
- executable: executable,
- })
+ m.packageFile(fullInstallPath, srcPath, executable)
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+
return fullInstallPath
}
@@ -2470,7 +2609,7 @@
if err != nil {
panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
}
- if !m.skipInstall(fullInstallPath) {
+ if !m.skipInstall() {
m.Build(pctx, BuildParams{
Rule: Symlink,
@@ -2503,7 +2642,7 @@
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
- if !m.skipInstall(fullInstallPath) {
+ if !m.skipInstall() {
m.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
@@ -2531,6 +2670,10 @@
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
+func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
+ return m.bp
+}
+
// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input
// was not a module reference.
func SrcIsModule(s string) (module string) {
@@ -2664,7 +2807,12 @@
}
}
+// Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
+// specify that they can be used as a tool by a genrule module.
type HostToolProvider interface {
+ Module
+ // HostToolPath returns the path to the host tool for the module if it is one, or an invalid
+ // OptionalPath.
HostToolPath() OptionalPath
}
@@ -2858,3 +3006,23 @@
bpctx := ctx.blueprintBaseModuleContext()
return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents)
}
+
+// installPathsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
+// topological order.
+type installPathsDepSet struct {
+ depSet
+}
+
+// newInstallPathsDepSet returns an immutable packagingSpecsDepSet with the given direct and
+// transitive contents.
+func newInstallPathsDepSet(direct InstallPaths, transitive []*installPathsDepSet) *installPathsDepSet {
+ return &installPathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the installPathsDepSet flattened to a list in topological order.
+func (d *installPathsDepSet) ToList() InstallPaths {
+ if d == nil {
+ return nil
+ }
+ return d.depSet.ToList().(InstallPaths)
+}
diff --git a/android/mutator.go b/android/mutator.go
index 7a10477..2a2be6c 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -44,6 +44,18 @@
}
}
+// 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) {
mctx := ®isterMutatorsContext{}
@@ -110,6 +122,11 @@
//
RegisterVisibilityRuleChecker,
+ // Record the default_applicable_licenses for each package.
+ //
+ // This must run before the defaults so that defaults modules can pick up the package default.
+ RegisterLicensesPackageMapper,
+
// Apply properties from defaults modules to the referencing modules.
//
// Any mutators that are added before this will not see any modules created by
@@ -136,6 +153,12 @@
// prebuilt.
RegisterPrebuiltsPreArchMutators,
+ // Gather the licenses properties for all modules for use during expansion and enforcement.
+ //
+ // This must come after the defaults mutators to ensure that any licenses supplied
+ // in a defaults module has been successfully applied before the rules are gathered.
+ RegisterLicensesPropertyGatherer,
+
// Gather the visibility rules for all modules for us during visibility enforcement.
//
// This must come after the defaults mutators to ensure that any visibility supplied
@@ -157,6 +180,7 @@
registerPathDepsMutator,
RegisterPrebuiltsPostDepsMutators,
RegisterVisibilityRuleEnforcer,
+ RegisterLicensesDependencyChecker,
RegisterNeverallowMutator,
RegisterOverridePostDepsMutators,
}
@@ -179,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/namespace.go b/android/namespace.go
index 9d7e8ac..d137636 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -232,13 +232,14 @@
}
-func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
- if sourceNamespace.visibleNamespaces == nil {
+func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace blueprint.Namespace) (searchOrder []*Namespace) {
+ ns, ok := sourceNamespace.(*Namespace)
+ if !ok || ns.visibleNamespaces == nil {
// When handling dependencies before namespaceMutator, assume they are non-Soong Blueprint modules and give
// access to all namespaces.
return r.sortedNamespaces.sortedItems()
}
- return sourceNamespace.visibleNamespaces
+ return ns.visibleNamespaces
}
func (r *NameResolver) ModuleFromName(name string, namespace blueprint.Namespace) (group blueprint.ModuleGroup, found bool) {
@@ -252,7 +253,7 @@
container := namespace.moduleContainer
return container.ModuleFromName(moduleName, nil)
}
- for _, candidate := range r.getNamespacesToSearchForModule(namespace.(*Namespace)) {
+ for _, candidate := range r.getNamespacesToSearchForModule(namespace) {
group, found = candidate.moduleContainer.ModuleFromName(name, nil)
if found {
return group, true
diff --git a/android/neverallow.go b/android/neverallow.go
index 8b8e1ac..7455e6a 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -51,8 +51,6 @@
func init() {
AddNeverAllowRules(createIncludeDirsRules()...)
AddNeverAllowRules(createTrebleRules()...)
- AddNeverAllowRules(createLibcoreRules()...)
- AddNeverAllowRules(createMediaRules()...)
AddNeverAllowRules(createJavaDeviceForHostRules()...)
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
@@ -133,46 +131,6 @@
}
}
-func createLibcoreRules() []Rule {
- var coreLibraryProjects = []string{
- "libcore",
- "external/apache-harmony",
- "external/apache-xml",
- "external/bouncycastle",
- "external/conscrypt",
- "external/icu",
- "external/okhttp",
- "external/wycheproof",
- "prebuilts",
- }
-
- // Additional whitelisted path only used for ART testing, which needs access to core library
- // targets. This does not affect the contents of a device image (system, vendor, etc.).
- var artTests = []string{
- "art/test",
- }
-
- // Core library constraints. The sdk_version: "none" can only be used in core library projects and ART tests.
- // Access to core library targets is restricted using visibility rules.
- rules := []Rule{
- NeverAllow().
- NotIn(coreLibraryProjects...).
- NotIn(artTests...).
- With("sdk_version", "none").
- WithoutMatcher("name", Regexp("^android_.*stubs_current$")),
- }
-
- return rules
-}
-
-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 1d454e5..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(`
@@ -215,50 +202,6 @@
"java_device_for_host can only be used in allowed projects",
},
},
- // Libcore rule tests
- {
- name: "sdk_version: \"none\" inside core libraries",
- fs: map[string][]byte{
- "libcore/Android.bp": []byte(`
- java_library {
- name: "inside_core_libraries",
- sdk_version: "none",
- }`),
- },
- },
- {
- name: "sdk_version: \"none\" on android_*stubs_current stub",
- fs: map[string][]byte{
- "frameworks/base/Android.bp": []byte(`
- java_library {
- name: "android_stubs_current",
- sdk_version: "none",
- }`),
- },
- },
- {
- name: "sdk_version: \"none\" outside core libraries",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "outside_core_libraries",
- sdk_version: "none",
- }`),
- },
- expectedErrors: []string{
- "module \"outside_core_libraries\": violates neverallow",
- },
- },
- {
- name: "sdk_version: \"current\"",
- fs: map[string][]byte{
- "Android.bp": []byte(`
- java_library {
- name: "outside_core_libraries",
- sdk_version: "current",
- }`),
- },
- },
// CC sdk rule tests
{
name: `"sdk_variant_only" outside allowed list`,
diff --git a/android/override_module.go b/android/override_module.go
index f8342d5..fa08566 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -235,7 +235,7 @@
return
}
// See if there's a prebuilt module that overrides this override module with prefer flag,
- // in which case we call SkipInstall on the corresponding variant later.
+ // in which case we call HideFromMake on the corresponding variant later.
ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
prebuilt, ok := dep.(PrebuiltInterface)
if !ok {
@@ -284,7 +284,7 @@
mods[i+1].(OverridableModule).override(ctx, o)
if o.getOverriddenByPrebuilt() {
// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
- mods[i+1].SkipInstall()
+ mods[i+1].HideFromMake()
}
}
} else if o, ok := ctx.Module().(OverrideModule); ok {
diff --git a/android/package.go b/android/package.go
index 182b3ed..7012fc7 100644
--- a/android/package.go
+++ b/android/package.go
@@ -31,6 +31,8 @@
type packageProperties struct {
// Specifies the default visibility for all modules defined in this package.
Default_visibility []string
+ // Specifies the default license terms for all modules defined in this package.
+ Default_applicable_licenses []string
}
type packageModule struct {
@@ -68,5 +70,9 @@
// its checking and parsing phases so make it the primary visibility property.
setPrimaryVisibilityProperty(module, "default_visibility", &module.properties.Default_visibility)
+ // The default_applicable_licenses property needs to be checked and parsed by the licenses module during
+ // its checking and parsing phases so make it the primary licenses property.
+ setPrimaryLicensesProperty(module, "default_applicable_licenses", &module.properties.Default_applicable_licenses)
+
return module
}
diff --git a/android/package_test.go b/android/package_test.go
index ade95d4..99be13f 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -17,9 +17,11 @@
package {
name: "package",
visibility: ["//visibility:private"],
+ licenses: ["license"],
}`),
},
expectedErrors: []string{
+ `top/Blueprints:5:14: unrecognized property "licenses"`,
`top/Blueprints:3:10: unrecognized property "name"`,
`top/Blueprints:4:16: unrecognized property "visibility"`,
},
@@ -44,9 +46,10 @@
"top/Blueprints": []byte(`
package {
default_visibility: ["//visibility:private"],
+ default_applicable_licenses: ["license"],
}
- package {
+ package {
}`),
},
expectedErrors: []string{
diff --git a/android/packaging.go b/android/packaging.go
index 09432e6..da745ff 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -203,3 +203,23 @@
builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
return entries
}
+
+// packagingSpecsDepSet is a thin type-safe wrapper around the generic depSet. It always uses
+// topological order.
+type packagingSpecsDepSet struct {
+ depSet
+}
+
+// newPackagingSpecsDepSet returns an immutable packagingSpecsDepSet with the given direct and
+// transitive contents.
+func newPackagingSpecsDepSet(direct []PackagingSpec, transitive []*packagingSpecsDepSet) *packagingSpecsDepSet {
+ return &packagingSpecsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the packagingSpecsDepSet flattened to a list in topological order.
+func (d *packagingSpecsDepSet) ToList() []PackagingSpec {
+ if d == nil {
+ return nil
+ }
+ return d.depSet.ToList().([]PackagingSpec)
+}
diff --git a/android/paths.go b/android/paths.go
index 5a41cf1..592b9e1 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -542,6 +542,45 @@
return list[:k]
}
+// FirstUniqueInstallPaths returns all unique elements of an InstallPaths, keeping the first copy of each. It
+// modifies the InstallPaths slice contents in place, and returns a subslice of the original slice.
+func FirstUniqueInstallPaths(list InstallPaths) InstallPaths {
+ // 128 was chosen based on BenchmarkFirstUniquePaths results.
+ if len(list) > 128 {
+ return firstUniqueInstallPathsMap(list)
+ }
+ return firstUniqueInstallPathsList(list)
+}
+
+func firstUniqueInstallPathsList(list InstallPaths) InstallPaths {
+ k := 0
+outer:
+ for i := 0; i < len(list); i++ {
+ for j := 0; j < k; j++ {
+ if list[i] == list[j] {
+ continue outer
+ }
+ }
+ list[k] = list[i]
+ k++
+ }
+ return list[:k]
+}
+
+func firstUniqueInstallPathsMap(list InstallPaths) InstallPaths {
+ k := 0
+ seen := make(map[InstallPath]bool, len(list))
+ for i := 0; i < len(list); i++ {
+ if seen[list[i]] {
+ continue
+ }
+ seen[list[i]] = true
+ list[k] = list[i]
+ k++
+ }
+ return list[:k]
+}
+
// LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It
// modifies the Paths slice contents in place, and returns a subslice of the original slice.
func LastUniquePaths(list Paths) Paths {
@@ -1115,6 +1154,17 @@
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
+type BazelOutPath struct {
+ OutputPath
+}
+
+var _ Path = BazelOutPath{}
+var _ objPathProvider = BazelOutPath{}
+
+func (p BazelOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
// reference abi dump for the given module. This is not guaranteed to be valid.
func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
@@ -1153,6 +1203,24 @@
fileName+ext)
}
+// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
+ execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
+ execRootPath := filepath.Join(execRootPathComponents...)
+ validatedExecRootPath, err := validatePath(execRootPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ ctx.Config().BazelContext.OutputBase()}
+
+ return BazelOutPath{
+ OutputPath: outputPath.withRel(validatedExecRootPath),
+ }
+}
+
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
@@ -1674,6 +1742,19 @@
return ioutil.WriteFile(absolutePath(path.String()), data, perm)
}
+func RemoveAllOutputDir(path WritablePath) error {
+ return os.RemoveAll(absolutePath(path.String()))
+}
+
+func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
+ dir := absolutePath(path.String())
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
+ }
+}
+
func absolutePath(path string) string {
if filepath.IsAbs(path) {
return path
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 294a6e0..39d30c5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -17,6 +17,7 @@
import (
"fmt"
"reflect"
+ "strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -74,6 +75,12 @@
srcsPropertyName string
}
+// RemoveOptionalPrebuiltPrefix returns the result of removing the "prebuilt_" prefix from the
+// supplied name if it has one, or returns the name unmodified if it does not.
+func RemoveOptionalPrebuiltPrefix(name string) string {
+ return strings.TrimPrefix(name, "prebuilt_")
+}
+
func (p *Prebuilt) Name(name string) string {
return "prebuilt_" + name
}
@@ -178,6 +185,9 @@
srcPropertyName := proptools.PropertyNameForField(srcField)
srcsSupplier := func(ctx BaseModuleContext) []string {
+ if !module.Enabled() {
+ return nil
+ }
value := srcPropsValue.FieldByIndex(srcFieldIndex)
if value.Kind() == reflect.Ptr {
value = value.Elem()
@@ -279,7 +289,7 @@
})
}
} else {
- m.SkipInstall()
+ m.HideFromMake()
}
}
}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index e2555e4..516d042 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -57,7 +57,7 @@
func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
sourcePath := t.prebuilt.SingleSourcePath(ctx)
- installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
+ installedPath := PathForModuleOut(ctx, t.BaseModuleName())
deps := PathsForModuleSrc(ctx, t.properties.Deps)
var fromPath = sourcePath.String()
@@ -75,11 +75,20 @@
},
})
+ packagingDir := PathForModuleInstall(ctx, t.BaseModuleName())
+ ctx.PackageFile(packagingDir, sourcePath.String(), sourcePath)
+ for _, dep := range deps {
+ ctx.PackageFile(packagingDir, dep.String(), dep)
+ }
+
t.toolPath = OptionalPathForPath(installedPath)
}
func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+ if t.Target().Os != BuildOs {
+ return
+ }
ctx.StrictRaw(makeVar, t.toolPath.String())
}
}
diff --git a/android/queryview.go b/android/queryview.go
index 970ae01..9e3e45a 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -28,13 +28,31 @@
RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
}
+// BazelQueryViewSingleton is the singleton responsible for registering the
+// soong_build build statement that will convert the Soong module graph after
+// applying *all* mutators, enabing the feature to query the final state of the
+// Soong graph. This mode is meant for querying the build graph state, and not meant
+// for generating BUILD files to be checked in.
func BazelQueryViewSingleton() Singleton {
return &bazelQueryViewSingleton{}
}
-type bazelQueryViewSingleton struct{}
+// BazelConverterSingleton is the singleton responsible for registering the soong_build
+// build statement that will convert the Soong module graph by applying an alternate
+// pipeline of mutators, with the goal of reaching semantic equivalence between the original
+// Blueprint and final BUILD files. Using this mode, the goal is to be able to
+// build with these BUILD files directly in the source tree.
+func BazelConverterSingleton() Singleton {
+ return &bazelConverterSingleton{}
+}
-func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) {
+type bazelQueryViewSingleton struct{}
+type bazelConverterSingleton struct{}
+
+func generateBuildActionsForBazelConversion(ctx SingletonContext, converterMode bool) {
+ name := "queryview"
+ descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
+
// Create a build and rule statement, using the Bazel QueryView's WORKSPACE
// file as the output file marker.
var deps Paths
@@ -42,7 +60,7 @@
deps = append(deps, moduleListFilePath)
deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
- bazelQueryViewDirectory := PathForOutput(ctx, "queryview")
+ bazelQueryViewDirectory := PathForOutput(ctx, name)
bazelQueryViewWorkspaceFile := bazelQueryViewDirectory.Join(ctx, "WORKSPACE")
primaryBuilder := primaryBuilderPath(ctx)
bazelQueryView := ctx.Rule(pctx, "bazelQueryView",
@@ -57,7 +75,7 @@
),
CommandDeps: []string{primaryBuilder.String()},
Description: fmt.Sprintf(
- "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir",
+ descriptionTemplate,
primaryBuilder.Base()),
Deps: blueprint.DepsGCC,
Depfile: "${outDir}/.queryview-depfile.d",
@@ -73,6 +91,14 @@
},
})
- // Add a phony target for building the Bazel QueryView
- ctx.Phony("queryview", bazelQueryViewWorkspaceFile)
+ // Add a phony target for generating the workspace
+ ctx.Phony(name, bazelQueryViewWorkspaceFile)
+}
+
+func (c *bazelQueryViewSingleton) GenerateBuildActions(ctx SingletonContext) {
+ generateBuildActionsForBazelConversion(ctx, false)
+}
+
+func (c *bazelConverterSingleton) GenerateBuildActions(ctx SingletonContext) {
+ generateBuildActionsForBazelConversion(ctx, true)
}
diff --git a/android/register.go b/android/register.go
index 08e47b3..02fc97e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "reflect"
"github.com/google/blueprint"
)
@@ -26,6 +27,7 @@
}
var moduleTypes []moduleType
+var moduleTypesForDocs = map[string]reflect.Value{}
type singleton struct {
name string
@@ -69,6 +71,16 @@
func RegisterModuleType(name string, factory ModuleFactory) {
moduleTypes = append(moduleTypes, moduleType{name, factory})
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+// RegisterModuleTypeForDocs associates a module type name with a reflect.Value of the factory
+// function that has documentation for the module type. It is normally called automatically
+// by RegisterModuleType, but can be called manually after RegisterModuleType in order to
+// override the factory method used for documentation, for example if the method passed to
+// RegisterModuleType was a lambda.
+func RegisterModuleTypeForDocs(name string, factory reflect.Value) {
+ moduleTypesForDocs[name] = factory
}
func RegisterSingletonType(name string, factory SingletonFactory) {
@@ -90,6 +102,24 @@
return ctx
}
+// RegisterForBazelConversion registers an alternate shadow pipeline of
+// singletons, module types and mutators to register for converting Blueprint
+// files to semantically equivalent BUILD files.
+func (ctx *Context) RegisterForBazelConversion() {
+ for _, t := range moduleTypes {
+ ctx.RegisterModuleType(t.name, ModuleFactoryAdaptor(t.factory))
+ }
+
+ // 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, 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))
@@ -127,12 +157,17 @@
return ret
}
+func ModuleTypeFactoriesForDocs() map[string]reflect.Value {
+ return moduleTypesForDocs
+}
+
// Interface for registering build components.
//
// Provided to allow registration of build components to be shared between the runtime
// and test environments.
type RegistrationContext interface {
RegisterModuleType(name string, factory ModuleFactory)
+ RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
RegisterSingletonType(name string, factory SingletonFactory)
PreArchMutators(f RegisterMutatorFunc)
@@ -171,8 +206,9 @@
var _ RegistrationContext = (*TestContext)(nil)
type initRegistrationContext struct {
- moduleTypes map[string]ModuleFactory
- singletonTypes map[string]SingletonFactory
+ moduleTypes map[string]ModuleFactory
+ singletonTypes map[string]SingletonFactory
+ moduleTypesForDocs map[string]reflect.Value
}
func (ctx *initRegistrationContext) RegisterModuleType(name string, factory ModuleFactory) {
@@ -181,6 +217,17 @@
}
ctx.moduleTypes[name] = factory
RegisterModuleType(name, factory)
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+ // Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
+ // SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
+ // factory method.
+ RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
}
func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e2d8187..84501fe 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -32,6 +32,7 @@
const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
const sboxOutSubDir = "out"
+const sboxToolsSubDir = "tools"
const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
@@ -48,6 +49,7 @@
highmem bool
remoteable RemoteRuleSupports
outDir WritablePath
+ sboxTools bool
sboxManifestPath WritablePath
missingDeps []string
}
@@ -140,6 +142,19 @@
return r
}
+// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
+// sandbox.
+func (r *RuleBuilder) SandboxTools() *RuleBuilder {
+ if !r.sbox {
+ panic("SandboxTools() must be called after Sbox()")
+ }
+ if len(r.commands) > 0 {
+ panic("SandboxTools() may not be called after Command()")
+ }
+ r.sboxTools = true
+ return r
+}
+
// Install associates an output of the rule with an install location, which can be retrieved later using
// RuleBuilder.Installs.
func (r *RuleBuilder) Install(from Path, to string) {
@@ -468,8 +483,29 @@
manifest.OutputDepfile = proto.String(depFile.String())
}
+ // If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
+ // into the sbox directory.
+ if r.sboxTools {
+ for _, tool := range tools {
+ command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+ From: proto.String(tool.String()),
+ To: proto.String(sboxPathForToolRel(r.ctx, tool)),
+ })
+ }
+ for _, c := range r.commands {
+ for _, tool := range c.packagedTools {
+ command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+ From: proto.String(tool.srcPath.String()),
+ To: proto.String(sboxPathForPackagedToolRel(tool)),
+ Executable: proto.Bool(tool.executable),
+ })
+ tools = append(tools, tool.srcPath)
+ }
+ }
+ }
+
// Add copy rules to the manifest to copy each output file from the sbox directory.
- // to the output directory.
+ // to the output directory after running the commands.
sboxOutputs := make([]string, len(outputs))
for i, output := range outputs {
rel := Rel(r.ctx, r.outDir.String(), output.String())
@@ -582,6 +618,7 @@
symlinkOutputs WritablePaths
depFiles WritablePaths
tools Paths
+ packagedTools []PackagingSpec
rspFileInputs Paths
// spans [start,end) of the command that should not be ninja escaped
@@ -625,6 +662,79 @@
return path.String()
}
+// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
+// sandboxing are enabled.
+func SboxPathForTool(ctx BuilderContext, path Path) string {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
+}
+
+func sboxPathForToolRel(ctx BuilderContext, path Path) string {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
+ if isRelOut {
+ // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+ return filepath.Join(sboxToolsSubDir, "out", relOut)
+ }
+ // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+ return filepath.Join(sboxToolsSubDir, "src", path.String())
+}
+
+// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
+// reference the tool.
+func SboxPathForPackagedTool(spec PackagingSpec) string {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
+func sboxPathForPackagedToolRel(spec PackagingSpec) string {
+ return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
+}
+
+// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
+// if it is not. This can be used on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathForTool(path Path) string {
+ if c.rule.sbox && c.rule.sboxTools {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+ }
+ return path.String()
+}
+
+// PackagedTool adds the specified tool path to the command line. It can only be used with tool
+// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
+func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("PackagedTool() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, spec)
+ c.Text(sboxPathForPackagedToolRel(spec))
+ return c
+}
+
+// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
+// line. It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("ImplicitPackagedTool() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, spec)
+ return c
+}
+
+// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
+// line. It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("ImplicitPackagedTools() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, specs...)
+ return c
+}
+
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@@ -693,7 +803,19 @@
// RuleBuilder.Tools.
func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
c.tools = append(c.tools, path)
- return c.Text(path.String())
+ return c.Text(c.PathForTool(path))
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
+ c.tools = append(c.tools, path)
+ return c
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
+ c.tools = append(c.tools, paths...)
+ return c
}
// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index e676e4a..06ea124 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -436,6 +436,44 @@
t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
}
})
+
+ t.Run("sbox tools", func(t *testing.T) {
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
+ PathForOutput(ctx, "sbox.textproto")).SandboxTools()
+ addCommands(rule)
+
+ wantCommands := []string{
+ "__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+ "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
+ }
+
+ wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+
+ if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+ t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g)
+ }
+ if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
+ t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
+ }
+
+ if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
+ }
+ })
}
func testRuleBuilderFactory() Module {
diff --git a/android/sdk.go b/android/sdk.go
index f2cdc88..bab0ed8 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -177,6 +177,12 @@
// to the zip
CopyToSnapshot(src Path, dest string)
+ // Return the path to an empty file.
+ //
+ // This can be used by sdk member types that need to create an empty file in the snapshot, simply
+ // pass the value returned from this to the CopyToSnapshot() method.
+ EmptyFile() Path
+
// Unzip the supplied zip into the snapshot relative directory destDir.
UnzipToSnapshot(zipPath Path, destDir string)
diff --git a/android/singleton_module.go b/android/singleton_module.go
new file mode 100644
index 0000000..2351738
--- /dev/null
+++ b/android/singleton_module.go
@@ -0,0 +1,146 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/google/blueprint"
+)
+
+// A SingletonModule is halfway between a Singleton and a Module. It has access to visiting
+// other modules via its GenerateSingletonBuildActions method, but must be defined in an Android.bp
+// file and can also be depended on like a module. It must be used zero or one times in an
+// Android.bp file, and it can only have a single variant.
+//
+// The SingletonModule's GenerateAndroidBuildActions method will be called before any normal or
+// singleton module that depends on it, but its GenerateSingletonBuildActions method will be called
+// after all modules, in registration order with other singletons and singleton modules.
+// GenerateAndroidBuildActions and GenerateSingletonBuildActions will not be called if the
+// SingletonModule was not instantiated in an Android.bp file.
+//
+// Since the SingletonModule rules likely depend on the modules visited during
+// GenerateSingletonBuildActions, the GenerateAndroidBuildActions is unlikely to produce any
+// rules directly. Instead, it will probably set some providers to paths that will later have rules
+// generated to produce them in GenerateSingletonBuildActions.
+//
+// The expected use case for a SingletonModule is a module that produces files that depend on all
+// modules in the tree and will be used by other modules. For example it could produce a text
+// file that lists all modules that meet a certain criteria, and that text file could be an input
+// to another module. Care must be taken that the ninja rules produced by the SingletonModule
+// don't produce a cycle by referencing output files of rules of modules that depend on the
+// SingletonModule.
+//
+// A SingletonModule must embed a SingletonModuleBase struct, and its factory method must be
+// registered with RegisterSingletonModuleType from an init() function.
+//
+// A SingletonModule can also implement SingletonMakeVarsProvider to export values to Make.
+type SingletonModule interface {
+ Module
+ GenerateSingletonBuildActions(SingletonContext)
+ singletonModuleBase() *SingletonModuleBase
+}
+
+// SingletonModuleBase must be embedded into implementers of the SingletonModule interface.
+type SingletonModuleBase struct {
+ ModuleBase
+
+ lock sync.Mutex
+ bp string
+ variant string
+}
+
+// GenerateBuildActions wraps the ModuleBase GenerateBuildActions method, verifying it was only
+// called once to prevent multiple variants of a SingletonModule.
+func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
+ smb.lock.Lock()
+ if smb.variant != "" {
+ ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
+ }
+ smb.variant = ctx.ModuleSubDir()
+ smb.lock.Unlock()
+
+ smb.ModuleBase.GenerateBuildActions(ctx)
+}
+
+// InitAndroidSingletonModule must be called from the SingletonModule's factory function to
+// initialize SingletonModuleBase.
+func InitAndroidSingletonModule(sm SingletonModule) {
+ InitAndroidModule(sm)
+}
+
+// singletonModuleBase retrieves the embedded SingletonModuleBase from a SingletonModule.
+func (smb *SingletonModuleBase) singletonModuleBase() *SingletonModuleBase { return smb }
+
+// SingletonModuleFactory is a factory method that returns a SingletonModule.
+type SingletonModuleFactory func() SingletonModule
+
+// SingletonModuleFactoryAdaptor converts a SingletonModuleFactory into a SingletonFactory and a
+// ModuleFactory.
+func SingletonModuleFactoryAdaptor(name string, factory SingletonModuleFactory) (SingletonFactory, ModuleFactory) {
+ // The sm variable acts as a static holder of the only SingletonModule instance. Calls to the
+ // returned SingletonFactory and ModuleFactory lambdas will always return the same sm value.
+ // The SingletonFactory is only expected to be called once, but the ModuleFactory may be
+ // called multiple times if the module is replaced with a clone of itself at the end of
+ // blueprint.ResolveDependencies.
+ var sm SingletonModule
+ s := func() Singleton {
+ sm = factory()
+ return &singletonModuleSingletonAdaptor{sm}
+ }
+ m := func() Module {
+ if sm == nil {
+ panic(fmt.Errorf("Singleton %q for SingletonModule was not instantiated", name))
+ }
+
+ // Check for multiple uses of a SingletonModule in a LoadHook. Checking directly in the
+ // factory would incorrectly flag when the factory was called again when the module is
+ // replaced with a clone of itself at the end of blueprint.ResolveDependencies.
+ AddLoadHook(sm, func(ctx LoadHookContext) {
+ smb := sm.singletonModuleBase()
+ smb.lock.Lock()
+ defer smb.lock.Unlock()
+ if smb.bp != "" {
+ ctx.ModuleErrorf("Duplicate SingletonModule %q, previously used in %s", name, smb.bp)
+ }
+ smb.bp = ctx.BlueprintsFile()
+ })
+ return sm
+ }
+ return s, m
+}
+
+// singletonModuleSingletonAdaptor makes a SingletonModule into a Singleton by translating the
+// GenerateSingletonBuildActions method to Singleton.GenerateBuildActions.
+type singletonModuleSingletonAdaptor struct {
+ sm SingletonModule
+}
+
+// GenerateBuildActions calls the SingletonModule's GenerateSingletonBuildActions method, but only
+// if the module was defined in an Android.bp file.
+func (smsa *singletonModuleSingletonAdaptor) GenerateBuildActions(ctx SingletonContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ smsa.sm.GenerateSingletonBuildActions(ctx)
+ }
+}
+
+func (smsa *singletonModuleSingletonAdaptor) MakeVars(ctx MakeVarsContext) {
+ if smsa.sm.singletonModuleBase().bp != "" {
+ if makeVars, ok := smsa.sm.(SingletonMakeVarsProvider); ok {
+ makeVars.MakeVars(ctx)
+ }
+ }
+}
diff --git a/android/singleton_module_test.go b/android/singleton_module_test.go
new file mode 100644
index 0000000..9232eb4
--- /dev/null
+++ b/android/singleton_module_test.go
@@ -0,0 +1,149 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+type testSingletonModule struct {
+ SingletonModuleBase
+ ops []string
+}
+
+func (tsm *testSingletonModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ tsm.ops = append(tsm.ops, "GenerateAndroidBuildActions")
+}
+
+func (tsm *testSingletonModule) GenerateSingletonBuildActions(ctx SingletonContext) {
+ tsm.ops = append(tsm.ops, "GenerateSingletonBuildActions")
+}
+
+func (tsm *testSingletonModule) MakeVars(ctx MakeVarsContext) {
+ tsm.ops = append(tsm.ops, "MakeVars")
+}
+
+func testSingletonModuleFactory() SingletonModule {
+ tsm := &testSingletonModule{}
+ InitAndroidSingletonModule(tsm)
+ return tsm
+}
+
+func runSingletonModuleTest(bp string) (*TestContext, []error) {
+ config := TestConfig(buildDir, nil, bp, nil)
+ // Enable Kati output to test SingletonModules with MakeVars.
+ config.katiEnabled = true
+ ctx := NewTestContext(config)
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+ if len(errs) > 0 {
+ return ctx, errs
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ return ctx, errs
+}
+
+func TestSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+ ctx, errs := runSingletonModuleTest(bp)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+
+ ops := ctx.ModuleForTests("test_singleton_module", "").Module().(*testSingletonModule).ops
+ wantOps := []string{"GenerateAndroidBuildActions", "GenerateSingletonBuildActions", "MakeVars"}
+ if !reflect.DeepEqual(ops, wantOps) {
+ t.Errorf("Expected operations %q, got %q", wantOps, ops)
+ }
+}
+
+func TestDuplicateSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+
+ test_singleton_module {
+ name: "test_singleton_module2",
+ }
+ `
+ _, errs := runSingletonModuleTest(bp)
+ if len(errs) == 0 {
+ t.Fatal("expected duplicate SingletonModule error")
+ }
+ if len(errs) != 1 || !strings.Contains(errs[0].Error(), `Duplicate SingletonModule "test_singleton_module", previously used in`) {
+ t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+ }
+}
+
+func TestUnusedSingletonModule(t *testing.T) {
+ bp := ``
+ ctx, errs := runSingletonModuleTest(bp)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+
+ singleton := ctx.SingletonForTests("test_singleton_module").Singleton()
+ sm := singleton.(*singletonModuleSingletonAdaptor).sm
+ ops := sm.(*testSingletonModule).ops
+ if ops != nil {
+ t.Errorf("Expected no operations, got %q", ops)
+ }
+}
+
+func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
+ if _, ok := ctx.Module().(*testSingletonModule); ok {
+ ctx.CreateVariations("a", "b")
+ }
+}
+
+func TestVariantSingletonModule(t *testing.T) {
+ bp := `
+ test_singleton_module {
+ name: "test_singleton_module",
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+ ctx := NewTestContext(config)
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
+ })
+ ctx.RegisterSingletonModuleType("test_singleton_module", testSingletonModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+ if len(errs) == 0 {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+
+ if len(errs) == 0 {
+ t.Fatal("expected duplicate SingletonModule error")
+ }
+ if len(errs) != 1 || !strings.Contains(errs[0].Error(), `GenerateAndroidBuildActions already called for variant`) {
+ t.Fatalf("expected duplicate SingletonModule error, got %q", errs)
+ }
+}
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 142a813..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,36 +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, &boolVariable{
- baseVariable: baseVariable{
- variable: 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
@@ -200,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),
@@ -420,6 +402,8 @@
// 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.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
props = props.Elem().FieldByName(soongConfigProperty)
@@ -442,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
@@ -475,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,
@@ -485,42 +512,124 @@
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{
+ variable: name,
+ },
+ }
+}
+
func (b boolVariable) variableValuesType() reflect.Type {
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
}
@@ -529,17 +638,31 @@
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) {
+ // 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
+ }
for i := 0; i < propStruct.NumField(); i++ {
field := propStruct.Field(i)
kind := field.Kind()
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 4190016..b824c78 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -17,6 +17,8 @@
import (
"reflect"
"testing"
+
+ "github.com/google/blueprint/proptools"
)
func Test_CanonicalizeToProperty(t *testing.T) {
@@ -247,3 +249,80 @@
})
}
}
+
+type properties struct {
+ A *string
+ B bool
+}
+
+type boolVarProps struct {
+ A *string
+ B bool
+ Conditions_default *properties
+}
+
+type soongConfigVars struct {
+ Bool_var interface{}
+}
+
+func Test_PropertiesToApply(t *testing.T) {
+ 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,
+ }
+ 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{}
+ }{
+ {
+ 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{}{boolVarPositive},
+ },
+ }
+
+ for _, tc := range testCases {
+ gotProps, err := PropertiesToApply(mt, props, tc.config)
+ if err != nil {
+ t.Errorf("%s: Unexpected error in PropertiesToApply: %s", tc.name, err)
+ }
+
+ if !reflect.DeepEqual(gotProps, tc.wantProps) {
+ t.Errorf("%s: Expected %s, got %s", tc.name, tc.wantProps, gotProps)
+ }
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 6539063..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.
@@ -103,6 +119,12 @@
ctx.Context.RegisterModuleType(name, ModuleFactoryAdaptor(factory))
}
+func (ctx *TestContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+ s, m := SingletonModuleFactoryAdaptor(name, factory)
+ ctx.RegisterSingletonType(name, s)
+ ctx.RegisterModuleType(name, m)
+}
+
func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
}
@@ -463,7 +485,14 @@
//
// 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/") {
+ return p
+ }
if w, ok := path.(WritablePath); ok {
rel, err := filepath.Rel(w.buildDir(), p)
if err != nil {
diff --git a/android/util_test.go b/android/util_test.go
index 25b52ca..fa26c77 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -593,6 +593,10 @@
name: "map",
f: firstUniqueStringsMap,
},
+ {
+ name: "optimal",
+ f: FirstUniqueStrings,
+ },
}
const maxSize = 1024
uniqueStrings := make([]string, maxSize)
diff --git a/android/variable.go b/android/variable.go
index 0df5272..9b3ed17 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -126,22 +126,19 @@
}
Arc struct {
- Cflags []string
- Exclude_srcs []string
- Include_dirs []string
- Shared_libs []string
- Static_libs []string
- Srcs []string
- }
+ Cflags []string `android:"arch_variant"`
+ Exclude_srcs []string `android:"arch_variant"`
+ Include_dirs []string `android:"arch_variant"`
+ Shared_libs []string `android:"arch_variant"`
+ Static_libs []string `android:"arch_variant"`
+ Srcs []string `android:"arch_variant"`
+ Whole_static_libs []string `android:"arch_variant"`
+ } `android:"arch_variant"`
Flatten_apex struct {
Enabled *bool
}
- Experimental_mte struct {
- Cflags []string `android:"arch_variant"`
- } `android:"arch_variant"`
-
Native_coverage struct {
Src *string `android:"arch_variant"`
Srcs []string `android:"arch_variant"`
@@ -180,6 +177,8 @@
DeviceCurrentApiLevelForVendorModules *string `json:",omitempty"`
DeviceSystemSdkVersions []string `json:",omitempty"`
+ RecoverySnapshotVersion *string `json:",omitempty"`
+
DeviceSecondaryArch *string `json:",omitempty"`
DeviceSecondaryArchVariant *string `json:",omitempty"`
DeviceSecondaryCpuVariant *string `json:",omitempty"`
@@ -261,7 +260,9 @@
DisableScudo *bool `json:",omitempty"`
- Experimental_mte *bool `json:",omitempty"`
+ MemtagHeapExcludePaths []string `json:",omitempty"`
+ MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
+ MemtagHeapSyncIncludePaths []string `json:",omitempty"`
VendorPath *string `json:",omitempty"`
OdmPath *string `json:",omitempty"`
@@ -308,19 +309,28 @@
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"`
SystemExtPublicSepolicyDirs []string `json:",omitempty"`
SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
BoardSepolicyM4Defs []string `json:",omitempty"`
+ BoardSepolicyVers *string `json:",omitempty"`
+ PlatformSepolicyVersion *string `json:",omitempty"`
+
VendorVars map[string]map[string]string `json:",omitempty"`
Ndk_abis *bool `json:",omitempty"`
Exclude_draft_ndk_apis *bool `json:",omitempty"`
- Flatten_apex *bool `json:",omitempty"`
- Aml_abis *bool `json:",omitempty"`
+ Flatten_apex *bool `json:",omitempty"`
+ ForceApexSymlinkOptimization *bool `json:",omitempty"`
+ CompressedApex *bool `json:",omitempty"`
+ Aml_abis *bool `json:",omitempty"`
DexpreoptGlobalConfig *string `json:",omitempty"`
@@ -337,7 +347,6 @@
ProductPublicSepolicyDirs []string `json:",omitempty"`
ProductPrivateSepolicyDirs []string `json:",omitempty"`
- ProductCompatibleProperty *bool `json:",omitempty"`
ProductVndkVersion *string `json:",omitempty"`
@@ -358,6 +367,8 @@
BoardKernelModuleInterfaceVersions []string `json:",omitempty"`
BoardMoveRecoveryResourcesToVendorBoot *bool `json:",omitempty"`
+
+ PrebuiltHiddenApiDir *string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -429,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/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 4540a1f..f0f51bf 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -24,8 +24,8 @@
)
const (
- clear_vars = "__android_mk_clear_vars"
- include_ignored = "__android_mk_include_ignored"
+ clearVarsPath = "__android_mk_clear_vars"
+ includeIgnoredPath = "__android_mk_include_ignored"
)
type bpVariable struct {
@@ -913,7 +913,7 @@
}
func includeIgnored(args []string) []string {
- return []string{include_ignored}
+ return []string{includeIgnoredPath}
}
var moduleTypes = map[string]string{
@@ -959,7 +959,7 @@
}
func mapIncludePath(path string) (string, bool) {
- if path == clear_vars || path == include_ignored {
+ if path == clearVarsPath || path == includeIgnoredPath {
return path, true
}
module, ok := includePathToModule[path]
@@ -968,7 +968,7 @@
func androidScope() mkparser.Scope {
globalScope := mkparser.NewScope(nil)
- globalScope.Set("CLEAR_VARS", clear_vars)
+ globalScope.Set("CLEAR_VARS", clearVarsPath)
globalScope.SetFunc("my-dir", mydir)
globalScope.SetFunc("all-java-files-under", allFilesUnder("*.java"))
globalScope.SetFunc("all-proto-files-under", allFilesUnder("*.proto"))
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index f4e5fa0..2d1bbb4 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -156,9 +156,9 @@
continue
}
switch module {
- case clear_vars:
+ case clearVarsPath:
resetModule(file)
- case include_ignored:
+ case includeIgnoredPath:
// subdirs are already automatically included in Soong
continue
default:
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index 4b782a2..3c4815e 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -15,8 +15,10 @@
package parser
import (
+ "fmt"
"strings"
"unicode"
+ "unicode/utf8"
)
// A MakeString is a string that may contain variable substitutions in it.
@@ -130,8 +132,85 @@
})
}
+// Words splits MakeString into multiple makeStrings separated by whitespace.
+// Thus, " a $(X)b c " will be split into ["a", "$(X)b", "c"].
+// Splitting a MakeString consisting solely of whitespace yields empty array.
func (ms *MakeString) Words() []*MakeString {
- return ms.splitNFunc(-1, splitWords)
+ var ch rune // current character
+ const EOF = -1 // no more characters
+ const EOS = -2 // at the end of a string chunk
+
+ // Next character's chunk and position
+ iString := 0
+ iChar := 0
+
+ var words []*MakeString
+ word := SimpleMakeString("", ms.Pos())
+
+ nextChar := func() {
+ if iString >= len(ms.Strings) {
+ ch = EOF
+ } else if iChar >= len(ms.Strings[iString]) {
+ iString++
+ iChar = 0
+ ch = EOS
+ } else {
+ var w int
+ ch, w = utf8.DecodeRuneInString(ms.Strings[iString][iChar:])
+ iChar += w
+ }
+ }
+
+ appendVariableAndAdvance := func() {
+ if iString-1 < len(ms.Variables) {
+ word.appendVariable(ms.Variables[iString-1])
+ }
+ nextChar()
+ }
+
+ appendCharAndAdvance := func(c rune) {
+ if c != EOF {
+ word.appendString(string(c))
+ }
+ nextChar()
+ }
+
+ nextChar()
+ for ch != EOF {
+ // Skip whitespace
+ for ch == ' ' || ch == '\t' {
+ nextChar()
+ }
+ if ch == EOS {
+ // "... $(X)... " case. The current word should be empty.
+ if !word.Empty() {
+ panic(fmt.Errorf("%q: EOS while current word %q is not empty, iString=%d",
+ ms.Dump(), word.Dump(), iString))
+ }
+ appendVariableAndAdvance()
+ }
+ // Copy word
+ for ch != EOF {
+ if ch == ' ' || ch == '\t' {
+ words = append(words, word)
+ word = SimpleMakeString("", ms.Pos())
+ break
+ }
+ if ch == EOS {
+ // "...a$(X)..." case. Append variable to the current word
+ appendVariableAndAdvance()
+ } else {
+ if ch == '\\' {
+ appendCharAndAdvance('\\')
+ }
+ appendCharAndAdvance(ch)
+ }
+ }
+ }
+ if !word.Empty() {
+ words = append(words, word)
+ }
+ return words
}
func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string) []*MakeString {
@@ -166,9 +245,7 @@
}
}
- if !curMs.Empty() {
- ret = append(ret, curMs)
- }
+ ret = append(ret, curMs)
return ret
}
@@ -219,44 +296,6 @@
return ret
}
-func splitWords(s string, n int) []string {
- ret := []string{}
- preserve := ""
- for n == -1 || n > 1 {
- index := strings.IndexAny(s, " \t")
- if index == 0 && len(preserve) == 0 {
- s = s[1:]
- } else if index >= 0 {
- escapeCount := 0
- for i := index - 1; i >= 0; i-- {
- if s[i] != '\\' {
- break
- }
- escapeCount += 1
- }
-
- if escapeCount%2 == 1 {
- preserve += s[0 : index+1]
- s = s[index+1:]
- continue
- }
-
- ret = append(ret, preserve+s[0:index])
- s = s[index+1:]
- preserve = ""
- if n > 0 {
- n--
- }
- } else {
- break
- }
- }
- if preserve != "" || s != "" || len(ret) == 0 {
- ret = append(ret, preserve+s)
- }
- return ret
-}
-
func unescape(s string) string {
ret := ""
for {
diff --git a/androidmk/parser/make_strings_test.go b/androidmk/parser/make_strings_test.go
index 6995e89..fbb289b 100644
--- a/androidmk/parser/make_strings_test.go
+++ b/androidmk/parser/make_strings_test.go
@@ -26,64 +26,53 @@
n int
}{
{
- in: &MakeString{
- Strings: []string{
- "a b c",
- "d e f",
- " h i j",
- },
- Variables: []Variable{
- Variable{Name: SimpleMakeString("var1", NoPos)},
- Variable{Name: SimpleMakeString("var2", NoPos)},
- },
- },
+ // "a b c$(var1)d e f$(var2) h i j"
+ in: genMakeString("a b c", "var1", "d e f", "var2", " h i j"),
sep: " ",
n: -1,
expected: []*MakeString{
- SimpleMakeString("a", NoPos),
- SimpleMakeString("b", NoPos),
- &MakeString{
- Strings: []string{"c", "d"},
- Variables: []Variable{
- Variable{Name: SimpleMakeString("var1", NoPos)},
- },
- },
- SimpleMakeString("e", NoPos),
- &MakeString{
- Strings: []string{"f", ""},
- Variables: []Variable{
- Variable{Name: SimpleMakeString("var2", NoPos)},
- },
- },
- SimpleMakeString("h", NoPos),
- SimpleMakeString("i", NoPos),
- SimpleMakeString("j", NoPos),
+ genMakeString("a"),
+ genMakeString("b"),
+ genMakeString("c", "var1", "d"),
+ genMakeString("e"),
+ genMakeString("f", "var2", ""),
+ genMakeString("h"),
+ genMakeString("i"),
+ genMakeString("j"),
},
},
{
- in: &MakeString{
- Strings: []string{
- "a b c",
- "d e f",
- " h i j",
- },
- Variables: []Variable{
- Variable{Name: SimpleMakeString("var1", NoPos)},
- Variable{Name: SimpleMakeString("var2", NoPos)},
- },
- },
+ // "a b c$(var1)d e f$(var2) h i j"
+ in: genMakeString("a b c", "var1", "d e f", "var2", " h i j"),
sep: " ",
n: 3,
expected: []*MakeString{
- SimpleMakeString("a", NoPos),
- SimpleMakeString("b", NoPos),
- &MakeString{
- Strings: []string{"c", "d e f", " h i j"},
- Variables: []Variable{
- Variable{Name: SimpleMakeString("var1", NoPos)},
- Variable{Name: SimpleMakeString("var2", NoPos)},
- },
- },
+ genMakeString("a"),
+ genMakeString("b"),
+ genMakeString("c", "var1", "d e f", "var2", " h i j"),
+ },
+ },
+ {
+ // "$(var1) $(var2)"
+ in: genMakeString("", "var1", " ", "var2", ""),
+ sep: " ",
+ n: -1,
+ expected: []*MakeString{
+ genMakeString("", "var1", ""),
+ genMakeString("", "var2", ""),
+ },
+ },
+ {
+ // "a,,b,c,"
+ in: genMakeString("a,,b,c,"),
+ sep: ",",
+ n: -1,
+ expected: []*MakeString{
+ genMakeString("a"),
+ genMakeString(""),
+ genMakeString("b"),
+ genMakeString("c"),
+ genMakeString(""),
},
},
}
@@ -104,15 +93,15 @@
expected string
}{
{
- in: SimpleMakeString("a b", NoPos),
+ in: genMakeString("a b"),
expected: "a b",
},
{
- in: SimpleMakeString("a\\ \\\tb\\\\", NoPos),
+ in: genMakeString("a\\ \\\tb\\\\"),
expected: "a \tb\\",
},
{
- in: SimpleMakeString("a\\b\\", NoPos),
+ in: genMakeString("a\\b\\"),
expected: "a\\b\\",
},
}
@@ -131,31 +120,88 @@
expected []*MakeString
}{
{
- in: SimpleMakeString("", NoPos),
+ in: genMakeString(""),
expected: []*MakeString{},
},
{
- in: SimpleMakeString(" a b\\ c d", NoPos),
+ in: genMakeString(` a b\ c d`),
expected: []*MakeString{
- SimpleMakeString("a", NoPos),
- SimpleMakeString("b\\ c", NoPos),
- SimpleMakeString("d", NoPos),
+ genMakeString("a"),
+ genMakeString(`b\ c`),
+ genMakeString("d"),
},
},
{
- in: SimpleMakeString(" a\tb\\\t\\ c d ", NoPos),
+ in: SimpleMakeString(" a\tb"+`\`+"\t"+`\ c d `, NoPos),
expected: []*MakeString{
- SimpleMakeString("a", NoPos),
- SimpleMakeString("b\\\t\\ c", NoPos),
- SimpleMakeString("d", NoPos),
+ genMakeString("a"),
+ genMakeString("b" + `\` + "\t" + `\ c`),
+ genMakeString("d"),
},
},
{
- in: SimpleMakeString(`a\\ b\\\ c d`, NoPos),
+ in: genMakeString(`a\\ b\\\ c d`),
expected: []*MakeString{
- SimpleMakeString(`a\\`, NoPos),
- SimpleMakeString(`b\\\ c`, NoPos),
- SimpleMakeString("d", NoPos),
+ genMakeString(`a\\`),
+ genMakeString(`b\\\ c`),
+ genMakeString("d"),
+ },
+ },
+ {
+ in: genMakeString(`\\ a`),
+ expected: []*MakeString{
+ genMakeString(`\\`),
+ genMakeString("a"),
+ },
+ },
+ {
+ // " "
+ in: &MakeString{
+ Strings: []string{" \t \t"},
+ Variables: nil,
+ },
+ expected: []*MakeString{},
+ },
+ {
+ // " a $(X)b c "
+ in: genMakeString(" a ", "X", "b c "),
+ expected: []*MakeString{
+ genMakeString("a"),
+ genMakeString("", "X", "b"),
+ genMakeString("c"),
+ },
+ },
+ {
+ // " a b$(X)c d"
+ in: genMakeString(" a b", "X", "c d"),
+ expected: []*MakeString{
+ genMakeString("a"),
+ genMakeString("b", "X", "c"),
+ genMakeString("d"),
+ },
+ },
+ {
+ // "$(X) $(Y)"
+ in: genMakeString("", "X", " ", "Y", ""),
+ expected: []*MakeString{
+ genMakeString("", "X", ""),
+ genMakeString("", "Y", ""),
+ },
+ },
+ {
+ // " a$(X) b"
+ in: genMakeString(" a", "X", " b"),
+ expected: []*MakeString{
+ genMakeString("a", "X", ""),
+ genMakeString("b"),
+ },
+ },
+ {
+ // "a$(X) b$(Y) "
+ in: genMakeString("a", "X", " b", "Y", " "),
+ expected: []*MakeString{
+ genMakeString("a", "X", ""),
+ genMakeString("b", "Y", ""),
},
},
}
@@ -180,3 +226,20 @@
return strings.Join(ret, "|||")
}
+
+// generates MakeString from alternating string chunks and variable names,
+// e.g., genMakeString("a", "X", "b") returns MakeString for "a$(X)b"
+func genMakeString(items ...string) *MakeString {
+ n := len(items) / 2
+ if len(items) != (2*n + 1) {
+ panic("genMakeString expects odd number of arguments")
+ }
+
+ ms := &MakeString{Strings: make([]string, n+1), Variables: make([]Variable, n)}
+ ms.Strings[0] = items[0]
+ for i := 1; i <= n; i++ {
+ ms.Variables[i-1] = Variable{Name: SimpleMakeString(items[2*i-1], NoPos)}
+ ms.Strings[i] = items[2*i]
+ }
+ return ms
+}
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index c14910a..5afef65 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -553,12 +553,14 @@
"else",
"endef",
"endif",
+ "export",
"ifdef",
"ifeq",
"ifndef",
"ifneq",
"include",
"-include",
+ "unexport",
}
var functions = [...]string{
diff --git a/apex/Android.bp b/apex/Android.bp
index 9e8c30d..77dde72 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
"soong-android",
"soong-bpf",
"soong-cc",
+ "soong-filesystem",
"soong-java",
"soong-python",
"soong-rust",
@@ -17,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 5b8563d..c4ea381 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -76,6 +76,7 @@
androidx.customview_customview(minSdkVersion:14)
androidx.documentfile_documentfile(minSdkVersion:14)
androidx.drawerlayout_drawerlayout(minSdkVersion:14)
+androidx.dynamicanimation_dynamicanimation(minSdkVersion:14)
androidx.fragment_fragment(minSdkVersion:14)
androidx.fragment_fragment-ktx(minSdkVersion:14)
androidx.interpolator_interpolator(minSdkVersion:14)
@@ -129,9 +130,11 @@
bionic_libc_platform_headers(minSdkVersion:29)
boringssl_self_test(minSdkVersion:29)
bouncycastle_ike_digests(minSdkVersion:current)
+bpf_syscall_wrappers(minSdkVersion:30)
brotli-java(minSdkVersion:current)
captiveportal-lib(minSdkVersion:29)
car-ui-lib(minSdkVersion:28)
+car-ui-lib-overlayable(minSdkVersion:28)
CellBroadcastApp(minSdkVersion:29)
CellBroadcastServiceModule(minSdkVersion:29)
codecs_g711dec(minSdkVersion:29)
@@ -168,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)
@@ -208,6 +214,7 @@
libadbd(minSdkVersion:(no version))
libadbd_core(minSdkVersion:(no version))
libadbd_services(minSdkVersion:(no version))
+liballoc.rust_sysroot(minSdkVersion:29)
libamrextractor(minSdkVersion:29)
libapp_processes_protos_lite(minSdkVersion:(no version))
libarect(minSdkVersion:29)
@@ -222,6 +229,8 @@
libavcenc(minSdkVersion:29)
libavservices_minijail(minSdkVersion:29)
libbacktrace_headers(minSdkVersion:apex_inherit)
+libbacktrace_rs.rust_sysroot(minSdkVersion:29)
+libbacktrace_sys.rust_sysroot(minSdkVersion:29)
libbase(minSdkVersion:29)
libbase_headers(minSdkVersion:29)
libbinder_headers(minSdkVersion:29)
@@ -237,6 +246,8 @@
libc_headers(minSdkVersion:apex_inherit)
libc_headers_arch(minSdkVersion:apex_inherit)
libcap(minSdkVersion:29)
+libcfg_if(minSdkVersion:29)
+libcfg_if.rust_sysroot(minSdkVersion:29)
libclang_rt.hwasan-aarch64-android.llndk(minSdkVersion:(no version))
libcodec2(minSdkVersion:29)
libcodec2_headers(minSdkVersion:29)
@@ -275,12 +286,15 @@
libcodec2_soft_vp9dec(minSdkVersion:29)
libcodec2_soft_vp9enc(minSdkVersion:29)
libcodec2_vndk(minSdkVersion:29)
+libcompiler_builtins.rust_sysroot(minSdkVersion:29)
+libcore.rust_sysroot(minSdkVersion:29)
libcrypto(minSdkVersion:29)
libcrypto_static(minSdkVersion:(no version))
libcrypto_utils(minSdkVersion:(no version))
libcutils(minSdkVersion:29)
libcutils_headers(minSdkVersion:29)
libcutils_sockets(minSdkVersion:29)
+libderive_sdk(minSdkVersion:30)
libdiagnose_usb(minSdkVersion:(no version))
libdl(minSdkVersion:(no version))
libdmabufheap(minSdkVersion:29)
@@ -294,7 +308,9 @@
libfmq-base(minSdkVersion:29)
libFraunhoferAAC(minSdkVersion:29)
libgav1(minSdkVersion:29)
+libgcc(minSdkVersion:(no version))
libgcc_stripped(minSdkVersion:(no version))
+libgetopts(minSdkVersion:29)
libgralloctypes(minSdkVersion:29)
libgrallocusage(minSdkVersion:29)
libgsm(minSdkVersion:apex_inherit)
@@ -303,6 +319,7 @@
libgui_headers(minSdkVersion:29)
libhardware(minSdkVersion:29)
libhardware_headers(minSdkVersion:29)
+libhashbrown.rust_sysroot(minSdkVersion:29)
libhevcdec(minSdkVersion:29)
libhevcenc(minSdkVersion:29)
libhidlbase(minSdkVersion:29)
@@ -312,9 +329,14 @@
libion(minSdkVersion:29)
libjavacrypto(minSdkVersion:29)
libjsoncpp(minSdkVersion:29)
+liblazy_static(minSdkVersion:29)
+liblibc(minSdkVersion:29)
+liblibc.rust_sysroot(minSdkVersion:29)
libLibGuiProperties(minSdkVersion:29)
+liblibm(minSdkVersion:29)
liblog(minSdkVersion:(no version))
liblog_headers(minSdkVersion:29)
+liblog_rust(minSdkVersion:29)
liblua(minSdkVersion:(no version))
liblz4(minSdkVersion:(no version))
libm(minSdkVersion:(no version))
@@ -333,6 +355,7 @@
libminijail_gen_syscall_obj(minSdkVersion:29)
libminijail_generated(minSdkVersion:29)
libmkvextractor(minSdkVersion:29)
+libmodules-utils-build(minSdkVersion:29)
libmp3extractor(minSdkVersion:29)
libmp4extractor(minSdkVersion:29)
libmpeg2dec(minSdkVersion:29)
@@ -344,23 +367,33 @@
libnetd_resolv(minSdkVersion:29)
libnetdbinder_utils_headers(minSdkVersion:29)
libnetdutils(minSdkVersion:29)
+libnetjniutils(minSdkVersion:29)
libnetworkstackutilsjni(minSdkVersion:29)
libneuralnetworks(minSdkVersion:(no version))
libneuralnetworks_common(minSdkVersion:(no version))
libneuralnetworks_headers(minSdkVersion:(no version))
liboggextractor(minSdkVersion:29)
+libonce_cell(minSdkVersion:29)
libopus(minSdkVersion:29)
+libpanic_unwind.rust_sysroot(minSdkVersion:29)
libprocessgroup(minSdkVersion:29)
libprocessgroup_headers(minSdkVersion:29)
libprocpartition(minSdkVersion:(no version))
+libprofiler_builtins.rust_sysroot(minSdkVersion:29)
libprotobuf-cpp-lite(minSdkVersion:29)
libprotobuf-java-lite(minSdkVersion:current)
libprotobuf-java-nano(minSdkVersion:9)
libprotoutil(minSdkVersion:(no version))
libqemu_pipe(minSdkVersion:(no version))
+libquiche_ffi(minSdkVersion:29)
+libring(minSdkVersion:29)
+libring-core(minSdkVersion:29)
+librustc_demangle.rust_sysroot(minSdkVersion:29)
+libsdk_proto(minSdkVersion:30)
libsfplugin_ccodec_utils(minSdkVersion:29)
libsonivoxwithoutjet(minSdkVersion:29)
libspeexresampler(minSdkVersion:29)
+libspin(minSdkVersion:29)
libssl(minSdkVersion:29)
libstagefright_amrnb_common(minSdkVersion:29)
libstagefright_amrnbdec(minSdkVersion:29)
@@ -381,6 +414,7 @@
libstagefright_m4vh263enc(minSdkVersion:29)
libstagefright_metadatautils(minSdkVersion:29)
libstagefright_mp3dec(minSdkVersion:29)
+libstagefright_mp3dec_headers(minSdkVersion:29)
libstagefright_mpeg2extractor(minSdkVersion:29)
libstagefright_mpeg2support_nocrypto(minSdkVersion:29)
libstats_jni(minSdkVersion:(no version))
@@ -390,8 +424,11 @@
libstatspush_compat(minSdkVersion:29)
libstatssocket(minSdkVersion:(no version))
libstatssocket_headers(minSdkVersion:29)
+libstd(minSdkVersion:29)
libsystem_headers(minSdkVersion:apex_inherit)
libsysutils(minSdkVersion:apex_inherit)
+libterm(minSdkVersion:29)
+libtest(minSdkVersion:29)
libtetherutilsjni(minSdkVersion:30)
libtetherutilsjni(minSdkVersion:current)
libtextclassifier(minSdkVersion:(no version))
@@ -402,6 +439,9 @@
libtflite_static(minSdkVersion:(no version))
libui(minSdkVersion:29)
libui_headers(minSdkVersion:29)
+libunicode_width.rust_sysroot(minSdkVersion:29)
+libuntrusted(minSdkVersion:29)
+libunwind.rust_sysroot(minSdkVersion:29)
libunwind_llvm(minSdkVersion:apex_inherit)
libutf(minSdkVersion:(no version))
libutils(minSdkVersion:apex_inherit)
@@ -418,6 +458,8 @@
media_plugin_headers(minSdkVersion:29)
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))
@@ -431,7 +473,10 @@
ndk_libunwind(minSdkVersion:16)
net-utils-device-common(minSdkVersion:29)
net-utils-framework-common(minSdkVersion:current)
+netd-client(minSdkVersion:29)
+netd_aidl_interface-java(minSdkVersion:29)
netd_aidl_interface-unstable-java(minSdkVersion:29)
+netd_event_listener_interface-java(minSdkVersion:29)
netd_event_listener_interface-ndk_platform(minSdkVersion:29)
netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
netlink-client(minSdkVersion:29)
@@ -446,6 +491,9 @@
neuralnetworks_utils_hal_1_2(minSdkVersion:30)
neuralnetworks_utils_hal_1_3(minSdkVersion:30)
neuralnetworks_utils_hal_common(minSdkVersion:30)
+neuralnetworks_utils_hal_service(minSdkVersion:30)
+note_memtag_heap_async(minSdkVersion:16)
+note_memtag_heap_sync(minSdkVersion:16)
PermissionController(minSdkVersion:28)
permissioncontroller-statsd(minSdkVersion:current)
philox_random(minSdkVersion:(no version))
@@ -471,6 +519,7 @@
prebuilt_androidx.customview_customview-nodeps(minSdkVersion:(no version))
prebuilt_androidx.documentfile_documentfile-nodeps(minSdkVersion:(no version))
prebuilt_androidx.drawerlayout_drawerlayout-nodeps(minSdkVersion:(no version))
+prebuilt_androidx.dynamicanimation_dynamicanimation-nodeps(minSdkVersion:(no version))
prebuilt_androidx.fragment_fragment-ktx-nodeps(minSdkVersion:(no version))
prebuilt_androidx.fragment_fragment-nodeps(minSdkVersion:(no version))
prebuilt_androidx.interpolator_interpolator-nodeps(minSdkVersion:(no version))
@@ -524,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 61b9dad..4408283 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -63,6 +63,16 @@
}
}
+// Return the full module name for a dependency module, which appends the apex module name unless re-using a system lib.
+func (a *apexBundle) fullModuleName(apexBundleName string, fi *apexFile) string {
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
+
+ if linkToSystemLib {
+ return fi.androidMkModuleName
+ }
+ return fi.androidMkModuleName + "." + apexBundleName + a.suffix
+}
+
func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
@@ -114,15 +124,20 @@
linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
- var moduleName string
- if linkToSystemLib {
- moduleName = fi.androidMkModuleName
- } else {
- moduleName = fi.androidMkModuleName + "." + apexBundleName + a.suffix
- }
+ 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 {
@@ -311,14 +326,16 @@
return moduleNames
}
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
var required []string
var targetRequired []string
var hostRequired []string
+ installMapSet := make(map[string]bool) // set of dependency module:location mappings
for _, fi := range a.filesInfo {
required = append(required, fi.requiredModuleNames...)
targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
+ installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
}
if len(required) > 0 {
@@ -330,6 +347,11 @@
if len(hostRequired) > 0 {
fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
}
+ if len(installMapSet) > 0 {
+ var installs []string
+ installs = append(installs, android.SortedStringKeys(installMapSet)...)
+ fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
+ }
}
func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -347,20 +369,26 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
if len(moduleNames) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
} else {
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
+ stemSuffix := apexType.suffix()
+ if a.isCompressed {
+ stemSuffix = ".capex"
+ }
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+stemSuffix)
fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
// Because apex writes .mk with Custom(), we need to write manually some common properties
@@ -385,7 +413,7 @@
if len(a.requiredDeps) > 0 {
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
}
- a.writeRequiredModules(w)
+ a.writeRequiredModules(w, name)
var postInstallCommands []string
if a.prebuiltFileToDelete != "" {
postInstallCommands = append(postInstallCommands, "rm -rf "+
diff --git a/apex/apex.go b/apex/apex.go
index 1d4f7b2..507d3ed 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,7 +29,9 @@
"android/soong/android"
"android/soong/bpf"
"android/soong/cc"
+ "android/soong/dexpreopt"
prebuilt_etc "android/soong/etc"
+ "android/soong/filesystem"
"android/soong/java"
"android/soong/python"
"android/soong/rust"
@@ -55,7 +57,7 @@
}
func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
+ ctx.TopDown("apex_info", apexInfoMutator).Parallel()
ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
@@ -96,6 +98,9 @@
// List of BPF programs inside this APEX bundle.
Bpfs []string
+ // List of filesystem images that are embedded inside this APEX bundle.
+ Filesystems []string
+
// Name of the apex_key module that provides the private key to sign this APEX bundle.
Key *string
@@ -120,6 +125,12 @@
// Default: true.
Installable *bool
+ // Whether this APEX can be compressed or not. Setting this property to false means this
+ // APEX will never be compressed. When set to true, APEX will be compressed if other
+ // conditions, e.g, target device needs to support APEX compression, are also fulfilled.
+ // Default: true.
+ Compressible *bool
+
// For native libraries and binaries, use the vendor variant instead of the core (platform)
// variant. Default is false. DO NOT use this for APEXes that are installed to the system or
// system_ext partition.
@@ -158,6 +169,10 @@
// used in tests.
Test_only_unsigned_payload *bool
+ // Whenever apex should be compressed, regardless of product flag used. Should be only
+ // used in tests.
+ Test_only_force_compression *bool
+
IsCoverageVariant bool `blueprint:"mutated"`
// List of sanitizer names that this APEX is enabled for
@@ -231,6 +246,23 @@
}
}
+type apexArchBundleProperties struct {
+ Arch struct {
+ Arm struct {
+ ApexNativeDependencies
+ }
+ Arm64 struct {
+ ApexNativeDependencies
+ }
+ X86 struct {
+ ApexNativeDependencies
+ }
+ X86_64 struct {
+ ApexNativeDependencies
+ }
+ }
+}
+
// These properties can be used in override_apex to override the corresponding properties in the
// base apex.
type overridableProperties struct {
@@ -267,6 +299,7 @@
// Properties
properties apexBundleProperties
targetProperties apexTargetBundleProperties
+ archProperties apexArchBundleProperties
overridableProperties overridableProperties
vndkProperties apexVndkProperties // only for apex_vndk modules
@@ -274,12 +307,12 @@
// Inputs
// Keys for apex_paylaod.img
- public_key_file android.Path
- private_key_file android.Path
+ publicKeyFile android.Path
+ privateKeyFile android.Path
// Cert/priv-key for the zip container
- container_certificate_file android.Path
- container_private_key_file android.Path
+ containerCertificateFile android.Path
+ containerPrivateKeyFile android.Path
// Flags for special variants of APEX
testApex bool
@@ -354,6 +387,8 @@
prebuiltFileToDelete string
+ isCompressed bool
+
// Path of API coverage generate file
coverageOutputPath android.ModuleOutPath
}
@@ -406,6 +441,8 @@
transitiveDep bool
isJniLib bool
+ multilib string
+
// TODO(jiyong): remove this
module android.Module
}
@@ -425,6 +462,7 @@
ret.requiredModuleNames = module.RequiredModuleNames()
ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+ ret.multilib = module.Target().Arch.ArchType.Multilib
}
return ret
}
@@ -483,12 +521,12 @@
// 1) DepsMutator: from the properties like native_shared_libs, java_libs, etc., modules are added
// to the (direct) dependencies of this APEX bundle.
//
-// 2) apexDepsMutator: this is a post-deps mutator, so runs after DepsMutator. Its goal is to
+// 2) apexInfoMutator: this is a post-deps mutator, so runs after DepsMutator. Its goal is to
// collect modules that are direct and transitive dependencies of each APEX bundle. The collected
// modules are marked as being included in the APEX via BuildForApex().
//
-// 3) apexMutator: this is a post-deps mutator that runs after apexDepsMutator. For each module that
-// are marked by the apexDepsMutator, apex variations are created using CreateApexVariations().
+// 3) apexMutator: this is a post-deps mutator that runs after apexInfoMutator. For each module that
+// are marked by the apexInfoMutator, apex variations are created using CreateApexVariations().
type dependencyTag struct {
blueprint.BaseDependencyTag
@@ -504,6 +542,7 @@
bpfTag = dependencyTag{name: "bpf", payload: true}
certificateTag = dependencyTag{name: "certificate"}
executableTag = dependencyTag{name: "executable", payload: true}
+ fsTag = dependencyTag{name: "filesystem", payload: true}
javaLibTag = dependencyTag{name: "javaLib", payload: true}
jniLibTag = dependencyTag{name: "jniLib", payload: true}
keyTag = dependencyTag{name: "key"}
@@ -522,11 +561,8 @@
if ctx.Device() {
binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
- libVariations = append(libVariations,
- blueprint.Variation{Mutator: "image", Variation: imageVariation},
- blueprint.Variation{Mutator: "version", Variation: ""}) // "" is the non-stub variant
- rustLibVariations = append(rustLibVariations,
- blueprint.Variation{Mutator: "image", Variation: imageVariation})
+ libVariations = append(libVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
+ rustLibVariations = append(rustLibVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
}
// Use *FarVariation* to be able to depend on modules having conflicting variations with
@@ -648,6 +684,20 @@
}
}
+ // Add native modules targeting a specific arch variant
+ switch target.Arch.ArchType {
+ case android.Arm:
+ depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
+ case android.Arm64:
+ depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+ case android.X86:
+ depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
+ case android.X86_64:
+ depsList = append(depsList, a.archProperties.Arch.X86_64.ApexNativeDependencies)
+ default:
+ panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType))
+ }
+
for _, d := range depsList {
addDependenciesForNativeModules(ctx, d, target, imageVariation)
}
@@ -672,10 +722,17 @@
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
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
@@ -721,22 +778,22 @@
Contents *android.ApexContents
}
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_deps")
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_info")
-// apexDepsMutator is responsible for collecting modules that need to have apex variants. They are
+var _ ApexInfoMutator = (*apexBundle)(nil)
+
+// ApexInfoMutator is responsible for collecting modules that need to have apex variants. They are
// identified by doing a graph walk starting from an apexBundle. Basically, all the (direct and
// indirect) dependencies are collected. But a few types of modules that shouldn't be included in
// the apexBundle (e.g. stub libraries) are not collected. Note that a single module can be depended
// on by multiple apexBundles. In that case, the module is collected for all of the apexBundles.
-func apexDepsMutator(mctx android.TopDownMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
-
- a, ok := mctx.Module().(*apexBundle)
- if !ok {
- return
- }
+//
+// For each dependency between an apex and an ApexModule an ApexInfo object describing the apex
+// is passed to that module's BuildForApex(ApexInfo) method which collates them all in a list.
+// The apexMutator uses that list to create module variants for the apexes to which it belongs.
+// The relationship between module variants and apexes is not one-to-one as variants will be
+// shared between compatible apexes.
+func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
// The VNDK APEX is special. For the APEX, the membership is described in a very different
// way. There is no dependency from the VNDK APEX to the VNDK libraries. Instead, VNDK
@@ -815,6 +872,25 @@
})
}
+type ApexInfoMutator interface {
+ // ApexInfoMutator implementations must call BuildForApex(ApexInfo) on any modules that are
+ // depended upon by an apex and which require an apex specific variant.
+ ApexInfoMutator(android.TopDownMutatorContext)
+}
+
+// apexInfoMutator delegates the work of identifying which modules need an ApexInfo and apex
+// specific variant to modules that support the ApexInfoMutator.
+func apexInfoMutator(mctx android.TopDownMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+
+ if a, ok := mctx.Module().(ApexInfoMutator); ok {
+ a.ApexInfoMutator(mctx)
+ return
+ }
+}
+
// apexUniqueVariationsMutator checks if any dependencies use unique apex variations. If so, use
// unique apex variations for this module. See android/apex.go for more about unique apex variant.
// TODO(jiyong): move this to android/apex.go?
@@ -910,7 +986,7 @@
}
// apexMutator visits each module and creates apex variations if the module was marked in the
-// previous run of apexDepsMutator.
+// previous run of apexInfoMutator.
func apexMutator(mctx android.BottomUpMutatorContext) {
if !mctx.Module().Enabled() {
return
@@ -1127,6 +1203,9 @@
// Implements cc.Coverage
func (a *apexBundle) HideFromMake() {
a.properties.HideFromMake = true
+ // This HideFromMake is shadowing the ModuleBase one, call through to it for now.
+ // TODO(ccross): untangle these
+ a.ModuleBase.HideFromMake()
}
// Implements cc.Coverage
@@ -1176,6 +1255,11 @@
return proptools.Bool(a.properties.Test_only_unsigned_payload)
}
+// See the test_only_force_compression property
+func (a *apexBundle) testOnlyShouldForceCompression() bool {
+ return proptools.Bool(a.properties.Test_only_force_compression)
+}
+
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1354,6 +1438,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)
@@ -1422,7 +1507,12 @@
return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
}
-// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+func apexFileForFilesystem(ctx android.BaseModuleContext, buildFile android.Path, fs filesystem.Filesystem) apexFile {
+ dirInApex := filepath.Join("etc", "fs")
+ return newApexFile(ctx, buildFile, buildFile.Base(), dirInApex, etc, fs)
+}
+
+// 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.
@@ -1441,6 +1531,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)
@@ -1522,6 +1615,9 @@
provideNativeLibs = append(provideNativeLibs, fi.stem())
}
return true // track transitive dependencies
+ } else if r, ok := child.(*rust.Module); ok {
+ fi := apexFileForRustLibrary(ctx, r)
+ filesInfo = append(filesInfo, fi)
} else {
propertyName := "native_shared_libs"
if isJniLib {
@@ -1547,7 +1643,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)
@@ -1593,6 +1689,12 @@
} else {
ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
}
+ case fsTag:
+ if fs, ok := child.(filesystem.Filesystem); ok {
+ filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+ } else {
+ ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+ }
case prebuiltTag:
if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
@@ -1622,16 +1724,16 @@
}
case keyTag:
if key, ok := child.(*apexKey); ok {
- a.private_key_file = key.private_key_file
- a.public_key_file = key.public_key_file
+ a.privateKeyFile = key.privateKeyFile
+ a.publicKeyFile = key.publicKeyFile
} else {
ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
}
return false
case certificateTag:
if dep, ok := child.(*java.AndroidAppCertificate); ok {
- a.container_certificate_file = dep.Certificate.Pem
- a.container_private_key_file = dep.Certificate.Key
+ a.containerCertificateFile = dep.Certificate.Pem
+ a.containerPrivateKeyFile = dep.Certificate.Key
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
}
@@ -1655,8 +1757,15 @@
}
af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
af.transitiveDep = true
+
+ // Always track transitive dependencies for host.
+ if a.Host() {
+ filesInfo = append(filesInfo, af)
+ return true
+ }
+
abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
- if !a.Host() && !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
+ if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
// If the dependency is a stubs lib, don't include it in this APEX,
// but make sure that the lib is installed on the device.
// In case no APEX is having the lib, the lib is installed to the system
@@ -1666,7 +1775,7 @@
// system libraries.
if !am.DirectlyInAnyApex() {
// we need a module name for Make
- name := cc.ImplementationModuleName(ctx)
+ name := cc.ImplementationModuleNameForMake(ctx)
if !proptools.Bool(a.properties.Use_vendor) {
// we don't use subName(.vendor) for a "use_vendor: true" apex
@@ -1681,6 +1790,25 @@
// Don't track further
return false
}
+
+ // If the dep is not considered to be in the same
+ // apex, don't add it to filesInfo so that it is not
+ // included in this APEX.
+ // TODO(jiyong): move this to at the top of the
+ // else-if clause for the indirect dependencies.
+ // Currently, that's impossible because we would
+ // like to record requiredNativeLibs even when
+ // DepIsInSameAPex is false. We also shouldn't do
+ // this for host.
+ if !am.DepIsInSameApex(ctx, am) {
+ return false
+ }
+
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ } else if rm, ok := child.(*rust.Module); ok {
+ af := apexFileForRustLibrary(ctx, rm)
+ af.transitiveDep = true
filesInfo = append(filesInfo, af)
return true // track transitive dependencies
}
@@ -1697,6 +1825,8 @@
filesInfo = append(filesInfo, af)
return true // track transitive dependencies
}
+ } else if cc.IsHeaderDepTag(depTag) {
+ // nothing
} else if java.IsJniDepTag(depTag) {
// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
return false
@@ -1720,15 +1850,15 @@
}
return false
})
- if a.private_key_file == nil {
+ if a.privateKeyFile == nil {
ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
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 {
@@ -1737,6 +1867,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
@@ -1823,9 +1958,11 @@
a.linkToSystemLib = false
}
+ forced := ctx.Config().ForceApexSymlinkOptimization()
+
// We don't need the optimization for updatable APEXes, as it might give false signal
- // to the system health when the APEXes are still bundled (b/149805758)
- if a.Updatable() && a.properties.ApexType == imageApex {
+ // to the system health when the APEXes are still bundled (b/149805758).
+ if !forced && a.Updatable() && a.properties.ApexType == imageApex {
a.linkToSystemLib = false
}
@@ -1860,7 +1997,7 @@
copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
- Input: a.public_key_file,
+ Input: a.publicKeyFile,
Output: copiedPubkey,
})
a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
@@ -1876,6 +2013,7 @@
module.AddProperties(&module.properties)
module.AddProperties(&module.targetProperties)
+ module.AddProperties(&module.archProperties)
module.AddProperties(&module.overridableProperties)
android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
@@ -2016,7 +2154,9 @@
// The dynamic linker and crash_dump tool in the runtime APEX is the only
// exception to this rule. It can't make the static dependencies dynamic
// because it can't do the dynamic linking for itself.
- if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
+ // Same rule should be applied to linkerconfig, because it should be executed
+ // only with static linked libraries before linker is available with ld.config.txt
+ if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump" || fromName == "linkerconfig") {
return false
}
@@ -2129,7 +2269,7 @@
func normalizeModuleName(moduleName string) string {
// Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
// system. Trim the prefix for the check since they are confusing
- moduleName = strings.TrimPrefix(moduleName, "prebuilt_")
+ moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
if strings.HasPrefix(moduleName, "libclang_rt.") {
// This module has many arch variants that depend on the product being built.
// We don't want to list them all
@@ -2733,7 +2873,7 @@
"libprofile-clang-extras_ndk",
"libprofile-extras",
"libprofile-extras_ndk",
- "libunwind_llvm",
+ "libunwind",
}
return m
}
@@ -2746,14 +2886,14 @@
func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
rules := make([]android.Rule, 0, len(modules_packages))
for module_name, module_packages := range modules_packages {
- permitted_packages_rule := android.NeverAllow().
+ permittedPackagesRule := android.NeverAllow().
BootclasspathJar().
With("apex_available", module_name).
WithMatcher("permitted_packages", android.NotInList(module_packages)).
Because("jars that are part of the " + module_name +
" module may only allow these packages: " + strings.Join(module_packages, ",") +
". Please jarjar or move code around.")
- rules = append(rules, permitted_packages_rule)
+ rules = append(rules, permittedPackagesRule)
}
return rules
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index cc05fd4..b1e8480 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -15,6 +15,7 @@
package apex
import (
+ "fmt"
"io/ioutil"
"os"
"path"
@@ -189,6 +190,7 @@
"testdata/baz": nil,
"AppSet.apks": nil,
"foo.rs": nil,
+ "libfoo.jar": nil,
}
cc.GatherRequiredFilesForTest(fs)
@@ -246,18 +248,15 @@
cc.RegisterRequiredBuildComponentsForTest(ctx)
rust.RegisterRequiredBuildComponentsForTest(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("cc_test", cc.TestFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory)
+ cc.RegisterVndkLibraryTxtTypes(ctx)
prebuilt_etc.RegisterPrebuiltEtcBuildComponents(ctx)
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.RegisterSdkLibraryBuildComponents(ctx)
ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
ctx.RegisterModuleType("bpf", bpf.BpfFactory)
@@ -346,6 +345,13 @@
}
}
+func ensureListNotEmpty(t *testing.T, result []string) {
+ t.Helper()
+ if len(result) == 0 {
+ t.Errorf("%q is expected to be not empty", result)
+ }
+}
+
// Minimal test
func TestBasicApex(t *testing.T) {
ctx, config := testApex(t, `
@@ -355,7 +361,10 @@
androidManifest: ":myapex.androidmanifest",
key: "myapex.key",
binaries: ["foo.rust"],
- native_shared_libs: ["mylib"],
+ native_shared_libs: [
+ "mylib",
+ "libfoo.ffi",
+ ],
rust_dyn_libs: ["libfoo.dylib.rust"],
multilib: {
both: {
@@ -392,7 +401,10 @@
cc_library {
name: "mylib",
srcs: ["mylib.cpp"],
- shared_libs: ["mylib2"],
+ shared_libs: [
+ "mylib2",
+ "libbar.ffi",
+ ],
system_shared_libs: [],
stl: "none",
// TODO: remove //apex_available:platform
@@ -444,6 +456,20 @@
apex_available: ["myapex"],
}
+ rust_ffi_shared {
+ name: "libfoo.ffi",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ apex_available: ["myapex"],
+ }
+
+ rust_ffi_shared {
+ name: "libbar.ffi",
+ srcs: ["foo.rs"],
+ crate_name: "bar",
+ apex_available: ["myapex"],
+ }
+
apex {
name: "com.android.gki.fake",
binaries: ["foo"],
@@ -559,12 +585,14 @@
ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("foo.rust"), "android_arm64_armv8-a_apex10000")
+ ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.ffi"), "android_arm64_armv8-a_shared_apex10000")
// Ensure that apex variant is created for the indirect dep
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.rlib.rust"), "android_arm64_armv8-a_rlib_dylib-std_apex10000")
ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000")
+ ensureListContains(t, ctx.ModuleVariantsForTests("libbar.ffi"), "android_arm64_armv8-a_shared_apex10000")
// Ensure that both direct and indirect deps are copied into apex
ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
@@ -572,6 +600,8 @@
ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar")
ensureContains(t, copyCmds, "image.apex/javalib/myjar_dex.jar")
ensureContains(t, copyCmds, "image.apex/lib64/libfoo.dylib.rust.dylib.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/libfoo.ffi.so")
+ ensureContains(t, copyCmds, "image.apex/lib64/libbar.ffi.so")
// .. but not for java libs
ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
@@ -1323,9 +1353,9 @@
ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
- ensureContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
+ ensureContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
for _, ver := range tc.shouldNotLink {
- ensureNotContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
+ ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
}
mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
@@ -1715,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) {
@@ -1782,6 +1812,31 @@
min_sdk_version: "30",
}
`)
+
+ testApexError(t, `module "libfoo.ffi".*: should support min_sdk_version\(29\)`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo.ffi"],
+ min_sdk_version: "29",
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ rust_ffi_shared {
+ name: "libfoo.ffi",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ apex_available: [
+ "myapex",
+ ],
+ min_sdk_version: "30",
+ }
+ `)
}
func TestApexMinSdkVersion_Okay(t *testing.T) {
@@ -2622,12 +2677,12 @@
// check the APEX keys
keys := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
- if keys.public_key_file.String() != "vendor/foo/devkeys/testkey.avbpubkey" {
- t.Errorf("public key %q is not %q", keys.public_key_file.String(),
+ if keys.publicKeyFile.String() != "vendor/foo/devkeys/testkey.avbpubkey" {
+ t.Errorf("public key %q is not %q", keys.publicKeyFile.String(),
"vendor/foo/devkeys/testkey.avbpubkey")
}
- if keys.private_key_file.String() != "vendor/foo/devkeys/testkey.pem" {
- t.Errorf("private key %q is not %q", keys.private_key_file.String(),
+ if keys.privateKeyFile.String() != "vendor/foo/devkeys/testkey.pem" {
+ t.Errorf("private key %q is not %q", keys.privateKeyFile.String(),
"vendor/foo/devkeys/testkey.pem")
}
@@ -3089,6 +3144,7 @@
"etc/vndkcore.libraries.VER.txt",
"etc/vndksp.libraries.VER.txt",
"etc/vndkprivate.libraries.VER.txt",
+ "etc/vndkproduct.libraries.VER.txt",
})
}
@@ -3155,15 +3211,15 @@
func vndkLibrariesTxtFiles(vers ...string) (result string) {
for _, v := range vers {
if v == "current" {
- for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+ for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
result += `
- vndk_libraries_txt {
+ ` + txt + `_libraries_txt {
name: "` + txt + `.libraries.txt",
}
`
}
} else {
- for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+ for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} {
result += `
prebuilt_etc {
name: "` + txt + `.libraries.` + v + `.txt",
@@ -3834,6 +3890,64 @@
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
}
+func TestApexWithArch(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ arch: {
+ arm64: {
+ native_shared_libs: ["mylib.arm64"],
+ },
+ x86_64: {
+ native_shared_libs: ["mylib.x64"],
+ },
+ }
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib.arm64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+
+ cc_library {
+ name: "mylib.x64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib.arm64"), "android_arm64_armv8-a_shared_apex10000")
+ ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.x64"), "android_arm64_armv8-a_shared_apex10000")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.arm64.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib.x64.so")
+}
+
func TestApexWithShBinary(t *testing.T) {
ctx, _ := testApex(t, `
apex {
@@ -4028,12 +4142,12 @@
apex_key := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey)
expected_pubkey := "testkey2.avbpubkey"
- actual_pubkey := apex_key.public_key_file.String()
+ actual_pubkey := apex_key.publicKeyFile.String()
if actual_pubkey != expected_pubkey {
t.Errorf("wrong public key path. expected %q. actual %q", expected_pubkey, actual_pubkey)
}
expected_privkey := "testkey2.pem"
- actual_privkey := apex_key.private_key_file.String()
+ actual_privkey := apex_key.privateKeyFile.String()
if actual_privkey != expected_privkey {
t.Errorf("wrong private key path. expected %q. actual %q", expected_privkey, actual_privkey)
}
@@ -4099,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 {
@@ -4279,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 {
@@ -4879,6 +5136,11 @@
"api/test-current.txt": nil,
"api/test-removed.txt": nil,
+ "100/public/api/foo.txt": nil,
+ "100/public/api/foo-removed.txt": nil,
+ "100/system/api/foo.txt": nil,
+ "100/system/api/foo-removed.txt": nil,
+
// For java_sdk_library_import
"a.jar": nil,
}
@@ -4903,6 +5165,11 @@
api_packages: ["foo"],
apex_available: [ "myapex" ],
}
+
+ prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["100"],
+ }
`, withFiles(filesForSdkLibrary))
// java_sdk_library installs both impl jar and permission XML
@@ -4946,6 +5213,11 @@
sdk_version: "none",
system_modules: "none",
}
+
+ prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["100"],
+ }
`, withFiles(filesForSdkLibrary))
// java_sdk_library installs both impl jar and permission XML
@@ -4992,6 +5264,11 @@
sdk_version: "none",
system_modules: "none",
}
+
+ prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["100"],
+ }
`, withFiles(filesForSdkLibrary))
// java_sdk_library installs both impl jar and permission XML
@@ -5008,7 +5285,11 @@
}
func TestJavaSDKLibrary_ImportPreferred(t *testing.T) {
- ctx, _ := testApex(t, ``,
+ ctx, _ := testApex(t, `
+ prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["100"],
+ }`,
withFiles(map[string][]byte{
"apex/a.java": nil,
"apex/apex_manifest.json": nil,
@@ -5075,7 +5356,7 @@
},
}
`),
- }),
+ }), withFiles(filesForSdkLibrary),
)
// java_sdk_library installs both impl jar and permission XML
@@ -5392,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) {
@@ -5635,6 +5916,13 @@
],
}
`
+
+ testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+}
+
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) *android.TestContext {
+ t.Helper()
+
bp += cc.GatherRequiredDepsForTest(android.Android)
bp += java.GatherRequiredDepsForTest()
bp += dexpreopt.BpToolModulesForTest()
@@ -5657,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)
@@ -5686,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) {
@@ -5788,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) {
@@ -5817,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)
@@ -5964,9 +6298,27 @@
srcs: ["mylib.cpp"],
system_shared_libs: [],
stl: "none",
- shared_libs: ["mylib", "myprivlib"],
+ shared_libs: ["mylib", "myprivlib", "mytestlib"],
test_for: ["myapex"]
}
+
+ cc_library {
+ name: "mytestlib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ shared_libs: ["mylib", "myprivlib"],
+ stl: "none",
+ test_for: ["myapex"],
+ }
+
+ cc_benchmark {
+ name: "mybench",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ shared_libs: ["mylib", "myprivlib"],
+ stl: "none",
+ test_for: ["myapex"],
+ }
`)
// the test 'mytest' is a test for the apex, therefore is linked to the
@@ -5974,6 +6326,16 @@
ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+
+ // The same should be true for cc_library
+ ldFlags = ctx.ModuleForTests("mytestlib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+ ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
+ ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+
+ // ... and for cc_benchmark
+ ldFlags = ctx.ModuleForTests("mybench", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
+ ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
+ ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
}
// TODO(jungjw): Move this to proptools
@@ -6186,6 +6548,299 @@
`)
}
+func TestCompressedApex(t *testing.T) {
+ ctx, config := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ compressible: true,
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `, func(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.CompressedApex = proptools.BoolPtr(true)
+ })
+
+ compressRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("compressRule")
+ ensureContains(t, compressRule.Output.String(), "myapex.capex.unsigned")
+
+ signApkRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Description("sign compressedApex")
+ ensureEquals(t, signApkRule.Input.String(), compressRule.Output.String())
+
+ // Make sure output of bundle is .capex
+ ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ ensureContains(t, ab.outputFile.String(), "myapex.capex")
+
+ // Verify android.mk rules
+ data := android.AndroidMkDataForTest(t, config, "", ab)
+ var builder strings.Builder
+ data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n")
+}
+
+func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
+ ctx, config := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["myapex"],
+ shared_libs: ["otherlib"],
+ system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "otherlib",
+ srcs: ["mylib.cpp"],
+ stubs: {
+ versions: ["current"],
+ },
+ }
+
+ cc_prebuilt_library_shared {
+ name: "otherlib",
+ prefer: true,
+ srcs: ["prebuilt.so"],
+ stubs: {
+ versions: ["current"],
+ },
+ }
+ `)
+
+ ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, config, "", ab)
+ var builder strings.Builder
+ data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+
+ // The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
+ // a thing there.
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherlib\n")
+}
+
+func TestExcludeDependency(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: ["myapex"],
+ shared_libs: ["mylib2"],
+ target: {
+ apex: {
+ exclude_shared_libs: ["mylib2"],
+ },
+ },
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ // Check if mylib is linked to mylib2 for the non-apex target
+ ldFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+ ensureContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+
+ // Make sure that the link doesn't occur for the apex target
+ ldFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+ ensureNotContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared_apex10000/mylib2.so")
+
+ // It shouldn't appear in the copy cmd as well.
+ copyCmds := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule").Args["copy_commands"]
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+}
+
+func TestPrebuiltStubLibDep(t *testing.T) {
+ bpBase := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["myapex"],
+ shared_libs: ["stublib"],
+ system_shared_libs: [],
+ }
+ apex {
+ name: "otherapex",
+ enabled: %s,
+ key: "myapex.key",
+ native_shared_libs: ["stublib"],
+ }
+ `
+
+ stublibSourceBp := `
+ cc_library {
+ name: "stublib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["otherapex"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ stublibPrebuiltBp := `
+ cc_prebuilt_library_shared {
+ name: "stublib",
+ srcs: ["prebuilt.so"],
+ apex_available: ["otherapex"],
+ stubs: {
+ versions: ["1"],
+ },
+ %s
+ }
+ `
+
+ tests := []struct {
+ name string
+ stublibBp string
+ usePrebuilt bool
+ modNames []string // Modules to collect AndroidMkEntries for
+ otherApexEnabled []string
+ }{
+ {
+ name: "only_source",
+ stublibBp: stublibSourceBp,
+ usePrebuilt: false,
+ modNames: []string{"stublib"},
+ otherApexEnabled: []string{"true", "false"},
+ },
+ {
+ name: "source_preferred",
+ stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, ""),
+ usePrebuilt: false,
+ modNames: []string{"stublib", "prebuilt_stublib"},
+ otherApexEnabled: []string{"true", "false"},
+ },
+ {
+ name: "prebuilt_preferred",
+ stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, "prefer: true,"),
+ usePrebuilt: true,
+ modNames: []string{"stublib", "prebuilt_stublib"},
+ otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+ },
+ {
+ name: "only_prebuilt",
+ stublibBp: fmt.Sprintf(stublibPrebuiltBp, ""),
+ usePrebuilt: true,
+ modNames: []string{"stublib"},
+ otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for _, otherApexEnabled := range test.otherApexEnabled {
+ t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
+ ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+
+ type modAndMkEntries struct {
+ mod *cc.Module
+ mkEntries android.AndroidMkEntries
+ }
+ entries := []*modAndMkEntries{}
+
+ // Gather shared lib modules that are installable
+ for _, modName := range test.modNames {
+ for _, variant := range ctx.ModuleVariantsForTests(modName) {
+ if !strings.HasPrefix(variant, "android_arm64_armv8-a_shared") {
+ continue
+ }
+ mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
+ if !mod.Enabled() || mod.IsHideFromMake() {
+ continue
+ }
+ for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
+ if ent.Disabled {
+ continue
+ }
+ entries = append(entries, &modAndMkEntries{
+ mod: mod,
+ mkEntries: ent,
+ })
+ }
+ }
+ }
+
+ var entry *modAndMkEntries = nil
+ for _, ent := range entries {
+ if strings.Join(ent.mkEntries.EntryMap["LOCAL_MODULE"], ",") == "stublib" {
+ if entry != nil {
+ t.Errorf("More than one AndroidMk entry for \"stublib\": %s and %s", entry.mod, ent.mod)
+ } else {
+ entry = ent
+ }
+ }
+ }
+
+ if entry == nil {
+ t.Errorf("AndroidMk entry for \"stublib\" missing")
+ } else {
+ isPrebuilt := entry.mod.Prebuilt() != nil
+ if isPrebuilt != test.usePrebuilt {
+ t.Errorf("Wrong module for \"stublib\" AndroidMk entry: got prebuilt %t, want prebuilt %t", isPrebuilt, test.usePrebuilt)
+ }
+ if !entry.mod.IsStubs() {
+ t.Errorf("Module for \"stublib\" AndroidMk entry isn't a stub: %s", entry.mod)
+ }
+ if entry.mkEntries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"] != nil {
+ t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
+ }
+ cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ expected := "-D__STUBLIB_API__=1"
+ if !android.InList(expected, cflags) {
+ t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
+ }
+ }
+ })
+ }
+ })
+ }
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/apex/builder.go b/apex/builder.go
index 66eaff1..e6bc3bd 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -66,6 +66,7 @@
pctx.HostBinToolVariable("extract_apks", "extract_apks")
pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
+ pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh")
}
@@ -576,8 +577,8 @@
fileContexts := a.buildFileContexts(ctx)
implicitInputs = append(implicitInputs, fileContexts)
- implicitInputs = append(implicitInputs, a.private_key_file, a.public_key_file)
- optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
+ implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile)
+ optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String())
manifestPackageName := a.getOverrideManifestPackageName(ctx)
if manifestPackageName != "" {
@@ -666,7 +667,7 @@
"manifest": a.manifestPbOut.String(),
"file_contexts": fileContexts.String(),
"canned_fs_config": cannedFsConfig.String(),
- "key": a.private_key_file.String(),
+ "key": a.privateKeyFile.String(),
"opt_flags": strings.Join(optFlags, " "),
},
})
@@ -738,7 +739,7 @@
////////////////////////////////////////////////////////////////////////////////////
// Step 4: Sign the APEX using signapk
- a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix)
+ signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix)
pem, key := a.getCertificateAndPrivateKey(ctx)
rule := java.Signapk
@@ -750,16 +751,54 @@
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
rule = java.SignapkRE
args["implicits"] = strings.Join(implicits.Strings(), ",")
- args["outCommaList"] = a.outputFile.String()
+ args["outCommaList"] = signedOutputFile.String()
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "signapk",
- Output: a.outputFile,
+ Output: signedOutputFile,
Input: unsignedOutputFile,
Implicits: implicits,
Args: args,
})
+ a.outputFile = signedOutputFile
+
+ // Process APEX compression if enabled or forced
+ if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
+ ctx.PropertyErrorf("test_only_force_compression", "not available")
+ return
+ }
+ compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
+ if apexType == imageApex && (compressionEnabled || a.testOnlyShouldForceCompression()) {
+ a.isCompressed = true
+ unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+".capex.unsigned")
+
+ compressRule := android.NewRuleBuilder(pctx, ctx)
+ compressRule.Command().
+ Text("rm").
+ FlagWithOutput("-f ", unsignedCompressedOutputFile)
+ compressRule.Command().
+ BuiltTool("apex_compression_tool").
+ Flag("compress").
+ FlagWithArg("--apex_compression_tool ", outHostBinDir+":"+prebuiltSdkToolsBinDir).
+ FlagWithInput("--input ", signedOutputFile).
+ FlagWithOutput("--output ", unsignedCompressedOutputFile)
+ 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",
+ Output: signedCompressedOutputFile,
+ Input: unsignedCompressedOutputFile,
+ Implicits: implicits,
+ Args: args,
+ })
+ a.outputFile = signedCompressedOutputFile
+ }
// Install to $OUT/soong/{target,host}/.../apex
if a.installable() {
@@ -810,8 +849,8 @@
// the zip container of this APEX. See the description of the 'certificate' property for how
// the cert and the private key are found.
func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) {
- if a.container_certificate_file != nil {
- return a.container_certificate_file, a.container_private_key_file
+ if a.containerCertificateFile != nil {
+ return a.containerCertificateFile, a.containerPrivateKeyFile
}
cert := String(a.properties.Certificate)
@@ -870,6 +909,12 @@
return !externalDep
}
+ depTag := ctx.OtherModuleDependencyTag(to)
+ if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() {
+ // Check to see if dependency been marked to skip the dependency check
+ return !externalDep
+ }
+
if info, exists := depInfos[to.Name()]; exists {
if !android.InList(from.Name(), info.From) {
info.From = append(info.From, from.Name())
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/key.go b/apex/key.go
index d9e3c10..752888d 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -36,8 +36,8 @@
properties apexKeyProperties
- public_key_file android.Path
- private_key_file android.Path
+ publicKeyFile android.Path
+ privateKeyFile android.Path
keyName string
}
@@ -69,30 +69,30 @@
// Otherwise, try to locate the key files in the default cert dir or
// in the local module dir
if android.SrcIsModule(String(m.properties.Public_key)) != "" {
- m.public_key_file = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
+ m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
} else {
- m.public_key_file = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Public_key))
+ m.publicKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Public_key))
// If not found, fall back to the local key pairs
- if !android.ExistentPathForSource(ctx, m.public_key_file.String()).Valid() {
- m.public_key_file = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
+ if !android.ExistentPathForSource(ctx, m.publicKeyFile.String()).Valid() {
+ m.publicKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Public_key))
}
}
if android.SrcIsModule(String(m.properties.Private_key)) != "" {
- m.private_key_file = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
+ m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
} else {
- m.private_key_file = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Private_key))
- if !android.ExistentPathForSource(ctx, m.private_key_file.String()).Valid() {
- m.private_key_file = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
+ m.privateKeyFile = ctx.Config().ApexKeyDir(ctx).Join(ctx, String(m.properties.Private_key))
+ if !android.ExistentPathForSource(ctx, m.privateKeyFile.String()).Valid() {
+ m.privateKeyFile = android.PathForModuleSrc(ctx, String(m.properties.Private_key))
}
}
- pubKeyName := m.public_key_file.Base()[0 : len(m.public_key_file.Base())-len(m.public_key_file.Ext())]
- privKeyName := m.private_key_file.Base()[0 : len(m.private_key_file.Base())-len(m.private_key_file.Ext())]
+ pubKeyName := m.publicKeyFile.Base()[0 : len(m.publicKeyFile.Base())-len(m.publicKeyFile.Ext())]
+ privKeyName := m.privateKeyFile.Base()[0 : len(m.privateKeyFile.Base())-len(m.privateKeyFile.Ext())]
if m.properties.Public_key != nil && m.properties.Private_key != nil && pubKeyName != privKeyName {
ctx.ModuleErrorf("public_key %q (keyname:%q) and private_key %q (keyname:%q) do not have same keyname",
- m.public_key_file.String(), pubKeyName, m.private_key_file, privKeyName)
+ m.publicKeyFile.String(), pubKeyName, m.privateKeyFile, privKeyName)
return
}
m.keyName = pubKeyName
@@ -107,20 +107,20 @@
func (s *apexKeysText) GenerateBuildActions(ctx android.SingletonContext) {
s.output = android.PathForOutput(ctx, "apexkeys.txt")
type apexKeyEntry struct {
- name string
- presigned bool
- public_key string
- private_key string
- container_certificate string
- container_private_key string
- partition string
+ name string
+ presigned bool
+ publicKey string
+ privateKey string
+ containerCertificate string
+ containerPrivateKey string
+ partition string
}
toString := func(e apexKeyEntry) string {
format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
if e.presigned {
return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
} else {
- return fmt.Sprintf(format, e.name, e.public_key, e.private_key, e.container_certificate, e.container_private_key, e.partition)
+ return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition)
}
}
@@ -129,13 +129,13 @@
if m, ok := module.(*apexBundle); ok && m.Enabled() && m.installable() {
pem, key := m.getCertificateAndPrivateKey(ctx)
apexKeyMap[m.Name()] = apexKeyEntry{
- name: m.Name() + ".apex",
- presigned: false,
- public_key: m.public_key_file.String(),
- private_key: m.private_key_file.String(),
- container_certificate: pem.String(),
- container_private_key: key.String(),
- partition: m.PartitionTag(ctx.DeviceConfig()),
+ name: m.Name() + ".apex",
+ presigned: false,
+ publicKey: m.publicKeyFile.String(),
+ privateKey: m.privateKeyFile.String(),
+ containerCertificate: pem.String(),
+ containerPrivateKey: key.String(),
+ partition: m.PartitionTag(ctx.DeviceConfig()),
}
}
})
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ce16d73..c72a9eb 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -107,7 +107,7 @@
compatSymlinks []string
}
-type PrebuiltProperties struct {
+type ApexFileProperties struct {
// the path to the prebuilt .apex file to import.
Source string `blueprint:"mutated"`
@@ -126,6 +126,39 @@
Src *string
}
}
+}
+
+func (p *ApexFileProperties) selectSource(ctx android.BottomUpMutatorContext) error {
+ // This is called before prebuilt_select and prebuilt_postdeps mutators
+ // The mutators requires that src to be set correctly for each arch so that
+ // arch variants are disabled when src is not provided for the arch.
+ if len(ctx.MultiTargets()) != 1 {
+ return fmt.Errorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ }
+ var src string
+ switch ctx.MultiTargets()[0].Arch.ArchType {
+ case android.Arm:
+ src = String(p.Arch.Arm.Src)
+ case android.Arm64:
+ src = String(p.Arch.Arm64.Src)
+ case android.X86:
+ src = String(p.Arch.X86.Src)
+ case android.X86_64:
+ src = String(p.Arch.X86_64.Src)
+ default:
+ return fmt.Errorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
+ }
+ if src == "" {
+ src = String(p.Src)
+ }
+ p.Source = src
+
+ return nil
+}
+
+type PrebuiltProperties struct {
+ ApexFileProperties
+ DeapexerProperties
Installable *bool
// Optional name for the installed apex. If unspecified, name of the
@@ -166,40 +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) {
- // This is called before prebuilt_select and prebuilt_postdeps mutators
- // The mutators requires that src to be set correctly for each arch so that
- // arch variants are disabled when src is not provided for the arch.
- if len(ctx.MultiTargets()) != 1 {
- ctx.ModuleErrorf("compile_multilib shouldn't be \"both\" for prebuilt_apex")
+ if err := p.properties.selectSource(ctx); err != nil {
+ ctx.ModuleErrorf("%s", err)
return
}
- var src string
- switch ctx.MultiTargets()[0].Arch.ArchType {
- case android.Arm:
- src = String(p.properties.Arch.Arm.Src)
- case android.Arm64:
- src = String(p.properties.Arch.Arm64.Src)
- case android.X86:
- src = String(p.properties.Arch.X86.Src)
- case android.X86_64:
- src = String(p.properties.Arch.X86_64.Src)
- default:
- ctx.ModuleErrorf("prebuilt_apex does not support %q", ctx.MultiTargets()[0].Arch.String())
- return
+
+ // 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)
}
- if src == "" {
- src = String(p.properties.Src)
+}
+
+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,
}
- p.properties.Source = src
+
+ // 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) {
@@ -218,7 +363,7 @@
})
if p.prebuiltCommon.checkForceDisable(ctx) {
- p.SkipInstall()
+ p.HideFromMake()
return
}
@@ -392,7 +537,7 @@
})
if a.prebuiltCommon.checkForceDisable(ctx) {
- a.SkipInstall()
+ a.HideFromMake()
return
}
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 27d93ee..ccf4e57 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -60,6 +60,7 @@
"etc/vndkcore.libraries.VER.txt",
"etc/vndksp.libraries.VER.txt",
"etc/vndkprivate.libraries.VER.txt",
+ "etc/vndkproduct.libraries.VER.txt",
})
}
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 0113726..d222d98 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -2,9 +2,17 @@
name: "soong-bazel",
pkgPath: "android/soong/bazel",
srcs: [
+ "aquery.go",
+ "constants.go",
"properties.go",
],
+ testSrcs: [
+ "aquery_test.go",
+ ],
pluginFor: [
"soong_build",
],
+ deps: [
+ "blueprint",
+ ],
}
diff --git a/bazel/aquery.go b/bazel/aquery.go
new file mode 100644
index 0000000..a196e8b
--- /dev/null
+++ b/bazel/aquery.go
@@ -0,0 +1,161 @@
+// 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 (
+ "encoding/json"
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// artifact contains relevant portions of Bazel's aquery proto, Artifact.
+// Represents a single artifact, whether it's a source file or a derived output file.
+type artifact struct {
+ Id int
+ PathFragmentId int
+}
+
+type pathFragment struct {
+ Id int
+ Label string
+ ParentId int
+}
+
+// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
+type KeyValuePair struct {
+ Key string
+ Value string
+}
+
+// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
+// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
+// data structure for storing large numbers of file paths.
+type depSetOfFiles struct {
+ Id int
+ // TODO(cparsons): Handle non-flat depsets.
+ DirectArtifactIds []int
+}
+
+// action contains relevant portions of Bazel's aquery proto, Action.
+// Represents a single command line invocation in the Bazel build graph.
+type action struct {
+ Arguments []string
+ EnvironmentVariables []KeyValuePair
+ InputDepSetIds []int
+ Mnemonic string
+ OutputIds []int
+}
+
+// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
+// An aquery response from Bazel contains a single ActionGraphContainer proto.
+type actionGraphContainer struct {
+ Artifacts []artifact
+ Actions []action
+ DepSetOfFiles []depSetOfFiles
+ PathFragments []pathFragment
+}
+
+// BuildStatement contains information to register a build statement corresponding (one to one)
+// with a Bazel action from Bazel's action graph.
+type BuildStatement struct {
+ Command string
+ OutputPaths []string
+ InputPaths []string
+ Env []KeyValuePair
+ Mnemonic string
+}
+
+// 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, error) {
+ buildStatements := []BuildStatement{}
+
+ var aqueryResult actionGraphContainer
+ err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
+
+ if err != nil {
+ return nil, err
+ }
+
+ pathFragments := map[int]pathFragment{}
+ for _, pathFragment := range aqueryResult.PathFragments {
+ pathFragments[pathFragment.Id] = pathFragment
+ }
+ artifactIdToPath := map[int]string{}
+ for _, artifact := range aqueryResult.Artifacts {
+ artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
+ if err != nil {
+ return nil, err
+ }
+ artifactIdToPath[artifact.Id] = artifactPath
+ }
+ depsetIdToArtifactIds := map[int][]int{}
+ for _, depset := range aqueryResult.DepSetOfFiles {
+ depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
+ }
+
+ for _, actionEntry := range aqueryResult.Actions {
+ outputPaths := []string{}
+ for _, outputId := range actionEntry.OutputIds {
+ 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 {
+ 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{
+ Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ OutputPaths: outputPaths,
+ InputPaths: inputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic}
+ buildStatements = append(buildStatements, buildStatement)
+ }
+
+ return buildStatements, nil
+}
+
+func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
+ labels := []string{}
+ currId := id
+ // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
+ for currId > 0 {
+ currFragment, ok := pathFragmentsMap[currId]
+ if !ok {
+ return "", fmt.Errorf("undefined path fragment id %d", currId)
+ }
+ labels = append([]string{currFragment.Label}, labels...)
+ currId = currFragment.ParentId
+ }
+ return filepath.Join(labels...), nil
+}
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/constants.go b/bazel/constants.go
new file mode 100644
index 0000000..15c75cf
--- /dev/null
+++ b/bazel/constants.go
@@ -0,0 +1,26 @@
+package bazel
+
+type RunName string
+
+// Below is a list bazel execution run names used through out the
+// Platform Build systems. Each run name represents an unique key
+// to query the bazel metrics.
+const (
+ // Perform a bazel build of the phony root to generate symlink forests
+ // for dependencies of the bazel build.
+ BazelBuildPhonyRootRunName = RunName("bazel-build-phony-root")
+
+ // Perform aquery of the bazel build root to retrieve action information.
+ AqueryBuildRootRunName = RunName("aquery-buildroot")
+
+ // Perform cquery of the Bazel build root and its dependencies.
+ CqueryBuildRootRunName = RunName("cquery-buildroot")
+
+ // Run bazel as a ninja executer
+ BazelNinjaExecRunName = RunName("bazel-ninja-exec")
+)
+
+// String returns the name of the run.
+func (c RunName) String() string {
+ return string(c)
+}
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
new file mode 100644
index 0000000..8007574
--- /dev/null
+++ b/bp2build/Android.bp
@@ -0,0 +1,24 @@
+bootstrap_go_package {
+ name: "soong-bp2build",
+ pkgPath: "android/soong/bp2build",
+ srcs: [
+ "androidbp_to_build_templates.go",
+ "bp2build.go",
+ "build_conversion.go",
+ "bzl_conversion.go",
+ "conversion.go",
+ ],
+ deps: [
+ "soong-android",
+ "soong-bazel",
+ ],
+ testSrcs: [
+ "build_conversion_test.go",
+ "bzl_conversion_test.go",
+ "conversion_test.go",
+ "testing.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+}
diff --git a/cmd/soong_build/queryview_templates.go b/bp2build/androidbp_to_build_templates.go
similarity index 95%
rename from cmd/soong_build/queryview_templates.go
rename to bp2build/androidbp_to_build_templates.go
index 359c0d8..9bac86b 100644
--- a/cmd/soong_build/queryview_templates.go
+++ b/bp2build/androidbp_to_build_templates.go
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package main
+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
new file mode 100644
index 0000000..75b6097
--- /dev/null
+++ b/bp2build/bp2build.go
@@ -0,0 +1,59 @@
+// 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "os"
+)
+
+// 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 Codegen(ctx CodegenContext) {
+ outputDir := android.PathForOutput(ctx, "bp2build")
+ android.RemoveAllOutputDir(outputDir)
+
+ ruleShims := CreateRuleShims(android.ModuleTypeFactories())
+
+ buildToTargets := GenerateSoongModuleTargets(ctx.Context(), true)
+
+ filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, true)
+ for _, f := range filesToWrite {
+ 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 writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
+ return writeReadOnlyFile(ctx, getOutputPath(outputDir, 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
+// are not intended to be edited by end users.
+func writeReadOnlyFile(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
+ android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
+ pathToFile := dir.Join(ctx, baseName)
+
+ // 0444 is read-only
+ err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
+
+ return err
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
new file mode 100644
index 0000000..da2fb7f
--- /dev/null
+++ b/bp2build/build_conversion.go
@@ -0,0 +1,362 @@
+// 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelAttributes struct {
+ Attrs map[string]string
+}
+
+type BazelTarget struct {
+ name string
+ content string
+}
+
+type bpToBuildContext interface {
+ ModuleName(module blueprint.Module) string
+ ModuleDir(module blueprint.Module) string
+ ModuleSubDir(module blueprint.Module) string
+ ModuleType(module blueprint.Module) string
+
+ 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
+// the generated attributes are sorted to ensure determinism.
+func propsToAttributes(props map[string]string) string {
+ var attributes string
+ for _, propName := range android.SortedStringKeys(props) {
+ if shouldGenerateAttribute(propName) {
+ attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
+ }
+ }
+ return attributes
+}
+
+func GenerateSoongModuleTargets(ctx bpToBuildContext, bp2buildEnabled bool) map[string][]BazelTarget {
+ buildFileToTargets := make(map[string][]BazelTarget)
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ dir := ctx.ModuleDir(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 {
+ props := getBuildProperties(ctx, m)
+
+ // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
+ // items, if the modules are added using different DependencyTag. Figure
+ // out the implications of that.
+ depLabels := map[string]bool{}
+ if aModule, ok := m.(android.Module); ok {
+ ctx.VisitDirectDeps(aModule, func(depModule blueprint.Module) {
+ depLabels[qualifiedTargetLabel(ctx, depModule)] = true
+ })
+ }
+ attributes := propsToAttributes(props.Attrs)
+
+ depLabelList := "[\n"
+ for depLabel, _ := range depLabels {
+ depLabelList += fmt.Sprintf(" %q,\n", depLabel)
+ }
+ depLabelList += " ]"
+
+ targetName := targetNameWithVariant(ctx, m)
+ return BazelTarget{
+ name: targetName,
+ content: fmt.Sprintf(
+ soongModuleTarget,
+ targetName,
+ ctx.ModuleName(m),
+ canonicalizeModuleType(ctx.ModuleType(m)),
+ ctx.ModuleSubDir(m),
+ depLabelList,
+ attributes),
+ }
+}
+
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+ var allProps map[string]string
+ // TODO: this omits properties for blueprint modules (blueprint_go_binary,
+ // bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
+ if aModule, ok := m.(android.Module); ok {
+ allProps = ExtractModuleProperties(aModule)
+ }
+
+ return BazelAttributes{
+ Attrs: allProps,
+ }
+}
+
+// Generically extract module properties and types into a map, keyed by the module property name.
+func ExtractModuleProperties(aModule android.Module) map[string]string {
+ ret := map[string]string{}
+
+ // Iterate over this android.Module's property structs.
+ for _, properties := range aModule.GetProperties() {
+ propertiesValue := reflect.ValueOf(properties)
+ // Check that propertiesValue is a pointer to the Properties struct, like
+ // *cc.BaseLinkerProperties or *java.CompilerProperties.
+ //
+ // propertiesValue can also be type-asserted to the structs to
+ // manipulate internal props, if needed.
+ if isStructPtr(propertiesValue.Type()) {
+ structValue := propertiesValue.Elem()
+ for k, v := range extractStructProperties(structValue, 0) {
+ ret[k] = v
+ }
+ } else {
+ panic(fmt.Errorf(
+ "properties must be a pointer to a struct, got %T",
+ propertiesValue.Interface()))
+ }
+ }
+
+ return ret
+}
+
+func isStructPtr(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// prettyPrint a property value into the equivalent Starlark representation
+// recursively.
+func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
+ if isZero(propertyValue) {
+ // A property value being set or unset actually matters -- Soong does set default
+ // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
+ //
+ // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
+ // value of unset attributes.
+ return "", nil
+ }
+
+ var ret string
+ switch propertyValue.Kind() {
+ case reflect.String:
+ ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+ case reflect.Bool:
+ ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+ case reflect.Int, reflect.Uint, reflect.Int64:
+ ret = fmt.Sprintf("%v", propertyValue.Interface())
+ case reflect.Ptr:
+ return prettyPrint(propertyValue.Elem(), indent)
+ case reflect.Slice:
+ ret = "[\n"
+ for i := 0; i < propertyValue.Len(); i++ {
+ indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
+ if err != nil {
+ return "", err
+ }
+
+ if indexedValue != "" {
+ ret += makeIndent(indent + 1)
+ ret += indexedValue
+ ret += ",\n"
+ }
+ }
+ ret += makeIndent(indent)
+ ret += "]"
+ case reflect.Struct:
+ ret = "{\n"
+ // Sort and print the struct props by the key.
+ structProps := extractStructProperties(propertyValue, indent)
+ for _, k := range android.SortedStringKeys(structProps) {
+ ret += makeIndent(indent + 1)
+ ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
+ }
+ ret += makeIndent(indent)
+ ret += "}"
+ case reflect.Interface:
+ // TODO(b/164227191): implement pretty print for interfaces.
+ // Interfaces are used for for arch, multilib and target properties.
+ return "", nil
+ default:
+ return "", fmt.Errorf(
+ "unexpected kind for property struct field: %s", propertyValue.Kind())
+ }
+ return ret, nil
+}
+
+// Converts a reflected property struct value into a map of property names and property values,
+// which each property value correctly pretty-printed and indented at the right nest level,
+// since property structs can be nested. In Starlark, nested structs are represented as nested
+// dicts: https://docs.bazel.build/skylark/lib/dict.html
+func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+ if structValue.Kind() != reflect.Struct {
+ panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+ }
+
+ ret := map[string]string{}
+ structType := structValue.Type()
+ for i := 0; i < structValue.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ fieldValue := structValue.Field(i)
+ if isZero(fieldValue) {
+ // Ignore zero-valued fields
+ continue
+ }
+
+ propertyName := proptools.PropertyNameForField(field.Name)
+ prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
+ if err != nil {
+ panic(
+ fmt.Errorf(
+ "Error while parsing property: %q. %s",
+ propertyName,
+ err))
+ }
+ if prettyPrintedValue != "" {
+ ret[propertyName] = prettyPrintedValue
+ }
+ }
+
+ return ret
+}
+
+func isZero(value reflect.Value) bool {
+ switch value.Kind() {
+ case reflect.Func, reflect.Map, reflect.Slice:
+ return value.IsNil()
+ case reflect.Array:
+ valueIsZero := true
+ for i := 0; i < value.Len(); i++ {
+ valueIsZero = valueIsZero && isZero(value.Index(i))
+ }
+ return valueIsZero
+ case reflect.Struct:
+ valueIsZero := true
+ for i := 0; i < value.NumField(); i++ {
+ if value.Field(i).CanSet() {
+ valueIsZero = valueIsZero && isZero(value.Field(i))
+ }
+ }
+ return valueIsZero
+ case reflect.Ptr:
+ if !value.IsNil() {
+ return isZero(reflect.Indirect(value))
+ } else {
+ return true
+ }
+ default:
+ zeroValue := reflect.Zero(value.Type())
+ result := value.Interface() == zeroValue.Interface()
+ return result
+ }
+}
+
+func escapeString(s string) string {
+ s = strings.ReplaceAll(s, "\\", "\\\\")
+ return strings.ReplaceAll(s, "\"", "\\\"")
+}
+
+func makeIndent(indent int) string {
+ if indent < 0 {
+ panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
+ }
+ 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) != "" {
+ // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
+ name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
+ } else {
+ name = c.ModuleName(logicModule)
+ }
+
+ return strings.Replace(name, "//", "", 1)
+}
+
+func qualifiedTargetLabel(c bpToBuildContext, logicModule blueprint.Module) string {
+ return fmt.Sprintf("//%s:%s", c.ModuleDir(logicModule), targetNameWithVariant(c, logicModule))
+}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
new file mode 100644
index 0000000..2df72bd
--- /dev/null
+++ b/bp2build/build_conversion_test.go
@@ -0,0 +1,333 @@
+// 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 bp2build
+
+import (
+ "android/soong/android"
+ "testing"
+)
+
+func TestGenerateSoongModuleTargets(t *testing.T) {
+ testCases := []struct {
+ bp string
+ expectedBazelTarget string
+ }{
+ {
+ bp: `custom {
+ name: "foo",
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ ramdisk: true,
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ ramdisk = True,
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ required = [
+ "bar",
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ target_required: ["qux", "bazqux"],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ dist: {
+ targets: ["goal_foo"],
+ tag: ".foo",
+ },
+ dists: [
+ {
+ targets: ["goal_bar"],
+ tag: ".bar",
+ },
+ ],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ dist = {
+ "tag": ".foo",
+ "targets": [
+ "goal_foo",
+ ],
+ },
+ dists = [
+ {
+ "tag": ".bar",
+ "targets": [
+ "goal_bar",
+ ],
+ },
+ ],
+)`,
+ },
+ {
+ bp: `custom {
+ name: "foo",
+ required: ["bar"],
+ target_required: ["qux", "bazqux"],
+ ramdisk: true,
+ owner: "custom_owner",
+ dists: [
+ {
+ tag: ".tag",
+ targets: ["my_goal"],
+ },
+ ],
+}
+ `,
+ expectedBazelTarget: `soong_module(
+ name = "foo",
+ module_name = "foo",
+ module_type = "custom",
+ module_variant = "",
+ module_deps = [
+ ],
+ dists = [
+ {
+ "tag": ".tag",
+ "targets": [
+ "my_goal",
+ ],
+ },
+ ],
+ owner = "custom_owner",
+ ramdisk = True,
+ required = [
+ "bar",
+ ],
+ target_required = [
+ "qux",
+ "bazqux",
+ ],
+)`,
+ },
+ }
+
+ dir := "."
+ for _, testCase := range testCases {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ 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)
+ }
+
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ 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.go b/bp2build/bzl_conversion.go
new file mode 100644
index 0000000..04c4542
--- /dev/null
+++ b/bp2build/bzl_conversion.go
@@ -0,0 +1,230 @@
+// 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 bp2build
+
+import (
+ "android/soong/android"
+ "fmt"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ // An allowlist of prop types that are surfaced from module props to rule
+ // attributes. (nested) dictionaries are notably absent here, because while
+ // Soong supports multi value typed and nested dictionaries, Bazel's rule
+ // attr() API supports only single-level string_dicts.
+ allowedPropTypes = map[string]bool{
+ "int": true, // e.g. 42
+ "bool": true, // e.g. True
+ "string_list": true, // e.g. ["a", "b"]
+ "string": true, // e.g. "a"
+ }
+)
+
+type rule struct {
+ name string
+ attrs string
+}
+
+type RuleShim struct {
+ // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
+ rules []string
+
+ // The generated string content of the bzl file.
+ content string
+}
+
+// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
+// user-specified Go plugins.
+//
+// This function reuses documentation generation APIs to ensure parity between modules-as-docs
+// and modules-as-code, including the names and types of morule properties.
+func CreateRuleShims(moduleTypeFactories map[string]android.ModuleFactory) map[string]RuleShim {
+ ruleShims := map[string]RuleShim{}
+ for pkg, rules := range generateRules(moduleTypeFactories) {
+ shim := RuleShim{
+ rules: make([]string, 0, len(rules)),
+ }
+ shim.content = "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
+
+ bzlFileName := strings.ReplaceAll(pkg, "android/soong/", "")
+ bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
+ bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
+
+ for _, r := range rules {
+ shim.content += fmt.Sprintf(moduleRuleShim, r.name, r.attrs)
+ shim.rules = append(shim.rules, r.name)
+ }
+ sort.Strings(shim.rules)
+ ruleShims[bzlFileName] = shim
+ }
+ return ruleShims
+}
+
+// Generate the content of soong_module.bzl with the rule shim load statements
+// and mapping of module_type to rule shim map for every module type in Soong.
+func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
+ var loadStmts string
+ var moduleRuleMap string
+ for _, bzlFileName := range android.SortedStringKeys(bzlLoads) {
+ loadStmt := "load(\"//build/bazel/queryview_rules:"
+ loadStmt += bzlFileName
+ loadStmt += ".bzl\""
+ ruleShim := bzlLoads[bzlFileName]
+ for _, rule := range ruleShim.rules {
+ loadStmt += fmt.Sprintf(", %q", rule)
+ moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
+ }
+ loadStmt += ")\n"
+ loadStmts += loadStmt
+ }
+
+ return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
+}
+
+func generateRules(moduleTypeFactories map[string]android.ModuleFactory) map[string][]rule {
+ // TODO: add shims for bootstrap/blueprint go modules types
+
+ rules := make(map[string][]rule)
+ // TODO: allow registration of a bzl rule when registring a factory
+ for _, moduleType := range android.SortedStringKeys(moduleTypeFactories) {
+ factory := moduleTypeFactories[moduleType]
+ factoryName := runtime.FuncForPC(reflect.ValueOf(factory).Pointer()).Name()
+ pkg := strings.Split(factoryName, ".")[0]
+ attrs := `{
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+`
+ attrs += getAttributes(factory)
+ attrs += " },"
+
+ r := rule{
+ name: canonicalizeModuleType(moduleType),
+ attrs: attrs,
+ }
+
+ rules[pkg] = append(rules[pkg], r)
+ }
+ return rules
+}
+
+type property struct {
+ name string
+ starlarkAttrType string
+ properties []property
+}
+
+const (
+ attributeIndent = " "
+)
+
+func (p *property) attributeString() string {
+ if !shouldGenerateAttribute(p.name) {
+ return ""
+ }
+
+ if _, ok := allowedPropTypes[p.starlarkAttrType]; !ok {
+ // a struct -- let's just comment out sub-props
+ s := fmt.Sprintf(attributeIndent+"# %s start\n", p.name)
+ for _, nestedP := range p.properties {
+ s += "# " + nestedP.attributeString()
+ }
+ s += fmt.Sprintf(attributeIndent+"# %s end\n", p.name)
+ return s
+ }
+ return fmt.Sprintf(attributeIndent+"%q: attr.%s(),\n", p.name, p.starlarkAttrType)
+}
+
+func extractPropertyDescriptionsFromStruct(structType reflect.Type) []property {
+ properties := make([]property, 0)
+ for i := 0; i < structType.NumField(); i++ {
+ field := structType.Field(i)
+ if shouldSkipStructField(field) {
+ continue
+ }
+
+ properties = append(properties, extractPropertyDescriptions(field.Name, field.Type)...)
+ }
+ return properties
+}
+
+func extractPropertyDescriptions(name string, t reflect.Type) []property {
+ name = proptools.PropertyNameForField(name)
+
+ // TODO: handle android:paths tags, they should be changed to label types
+
+ starlarkAttrType := fmt.Sprintf("%s", t.Name())
+ props := make([]property, 0)
+
+ switch t.Kind() {
+ case reflect.Bool, reflect.String:
+ // do nothing
+ case reflect.Uint, reflect.Int, reflect.Int64:
+ starlarkAttrType = "int"
+ case reflect.Slice:
+ if t.Elem().Kind() != reflect.String {
+ // TODO: handle lists of non-strings (currently only list of Dist)
+ return []property{}
+ }
+ starlarkAttrType = "string_list"
+ case reflect.Struct:
+ props = extractPropertyDescriptionsFromStruct(t)
+ case reflect.Ptr:
+ return extractPropertyDescriptions(name, t.Elem())
+ case reflect.Interface:
+ // Interfaces are used for for arch, multilib and target properties, which are handled at runtime.
+ // These will need to be handled in a bazel-specific version of the arch mutator.
+ return []property{}
+ }
+
+ prop := property{
+ name: name,
+ starlarkAttrType: starlarkAttrType,
+ properties: props,
+ }
+
+ return []property{prop}
+}
+
+func getPropertyDescriptions(props []interface{}) []property {
+ // there may be duplicate properties, e.g. from defaults libraries
+ propertiesByName := make(map[string]property)
+ for _, p := range props {
+ for _, prop := range extractPropertyDescriptionsFromStruct(reflect.ValueOf(p).Elem().Type()) {
+ propertiesByName[prop.name] = prop
+ }
+ }
+
+ properties := make([]property, 0, len(propertiesByName))
+ for _, key := range android.SortedStringKeys(propertiesByName) {
+ properties = append(properties, propertiesByName[key])
+ }
+
+ return properties
+}
+
+func getAttributes(factory android.ModuleFactory) string {
+ attrs := ""
+ for _, p := range getPropertyDescriptions(factory().GetProperties()) {
+ attrs += p.attributeString()
+ }
+ return attrs
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
new file mode 100644
index 0000000..01c7271
--- /dev/null
+++ b/bp2build/bzl_conversion_test.go
@@ -0,0 +1,208 @@
+// 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 bp2build
+
+import (
+ "android/soong/android"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func TestGenerateModuleRuleShims(t *testing.T) {
+ moduleTypeFactories := map[string]android.ModuleFactory{
+ "custom": customModuleFactoryBase,
+ "custom_test": customTestModuleFactoryBase,
+ "custom_defaults": customDefaultsModuleFactoryBasic,
+ }
+ ruleShims := CreateRuleShims(moduleTypeFactories)
+
+ if len(ruleShims) != 1 {
+ t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
+ }
+
+ ruleShim := ruleShims["bp2build"]
+ expectedRules := []string{
+ "custom",
+ "custom_defaults",
+ "custom_test_",
+ }
+
+ if len(ruleShim.rules) != len(expectedRules) {
+ t.Errorf("Expected %d rules, but got %d", len(expectedRules), len(ruleShim.rules))
+ }
+
+ for i, rule := range ruleShim.rules {
+ if rule != expectedRules[i] {
+ t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
+ }
+ }
+ expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
+
+def _custom_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom = rule(
+ implementation = _custom_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_defaults_impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_defaults = rule(
+ implementation = _custom_defaults_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ },
+)
+
+def _custom_test__impl(ctx):
+ return [SoongModuleInfo()]
+
+custom_test_ = rule(
+ implementation = _custom_test__impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ "bool_prop": attr.bool(),
+ "bool_ptr_prop": attr.bool(),
+ "int64_ptr_prop": attr.int(),
+ # nested_props start
+# "nested_prop": attr.string(),
+ # nested_props end
+ # nested_props_ptr start
+# "nested_prop": attr.string(),
+ # nested_props_ptr end
+ "string_list_prop": attr.string_list(),
+ "string_prop": attr.string(),
+ "string_ptr_prop": attr.string(),
+ # test_prop start
+# "test_string_prop": attr.string(),
+ # test_prop end
+ },
+)
+`
+
+ if ruleShim.content != expectedBzl {
+ t.Errorf(
+ "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
+ expectedBzl,
+ ruleShim.content)
+ }
+}
+
+func TestGenerateSoongModuleBzl(t *testing.T) {
+ ruleShims := map[string]RuleShim{
+ "file1": RuleShim{
+ rules: []string{"a", "b"},
+ content: "irrelevant",
+ },
+ "file2": RuleShim{
+ rules: []string{"c", "d"},
+ content: "irrelevant",
+ },
+ }
+ files := CreateBazelFiles(ruleShims, make(map[string][]BazelTarget), false)
+
+ var actualSoongModuleBzl BazelFile
+ for _, f := range files {
+ if f.Basename == "soong_module.bzl" {
+ actualSoongModuleBzl = f
+ }
+ }
+
+ expectedLoad := `load("//build/bazel/queryview_rules:file1.bzl", "a", "b")
+load("//build/bazel/queryview_rules:file2.bzl", "c", "d")
+`
+ expectedRuleMap := `soong_module_rule_map = {
+ "a": a,
+ "b": b,
+ "c": c,
+ "d": d,
+}`
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedLoad) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the load statement in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedLoad)
+ }
+
+ if !strings.Contains(actualSoongModuleBzl.Contents, expectedRuleMap) {
+ t.Errorf(
+ "Generated soong_module.bzl:\n\n%s\n\n"+
+ "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
+ actualSoongModuleBzl.Contents,
+ expectedRuleMap)
+ }
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
new file mode 100644
index 0000000..cccc474
--- /dev/null
+++ b/bp2build/conversion.go
@@ -0,0 +1,127 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type BazelFile struct {
+ Dir string
+ Basename string
+ Contents string
+}
+
+func CreateBazelFiles(
+ ruleShims map[string]RuleShim,
+ 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.
+ files = append(files, newFile("", "WORKSPACE", ""))
+ // Used to denote that the top level directory is a package.
+ files = append(files, newFile("", "BUILD", ""))
+
+ files = append(files, newFile(bazelRulesSubDir, "BUILD", ""))
+
+ 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, createBuildFiles(buildToTargets, bp2buildEnabled)...)
+
+ return files
+}
+
+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 {
+ content += "\n\n"
+ content += t.content
+ }
+ files = append(files, newFile(dir, "BUILD.bazel", content))
+ }
+ return files
+}
+
+func newFile(dir, basename, content string) BazelFile {
+ return BazelFile{
+ Dir: dir,
+ Basename: basename,
+ Contents: content,
+ }
+}
+
+const (
+ bazelRulesSubDir = "build/bazel/queryview_rules"
+
+ // additional files:
+ // * workspace file
+ // * base BUILD file
+ // * rules BUILD file
+ // * rules providers.bzl file
+ // * rules soong_module.bzl file
+ numAdditionalFiles = 5
+)
+
+var (
+ // Certain module property names are blocklisted/ignored here, for the reasons commented.
+ ignoredPropNames = map[string]bool{
+ "name": true, // redundant, since this is explicitly generated for every target
+ "from": true, // reserved keyword
+ "in": true, // reserved keyword
+ "arch": true, // interface prop type is not supported yet.
+ "multilib": true, // interface prop type is not supported yet.
+ "target": true, // interface prop type is not supported yet.
+ "visibility": true, // Bazel has native visibility semantics. Handle later.
+ "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
+ }
+)
+
+func shouldGenerateAttribute(prop string) bool {
+ return !ignoredPropNames[prop]
+}
+
+func shouldSkipStructField(field reflect.StructField) bool {
+ if field.PkgPath != "" {
+ // Skip unexported fields. Some properties are
+ // internal to Soong only, and these fields do not have PkgPath.
+ return true
+ }
+ // fields with tag `blueprint:"mutated"` are exported to enable modification in mutators, etc
+ // but cannot be set in a .bp file
+ if proptools.HasTag(field, "blueprint", "mutated") {
+ return true
+ }
+ return false
+}
+
+// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
+// testonly = True, forcing other rules that depend on _test rules to also be
+// marked as testonly = True. This semantic constraint is not present in Soong.
+// To work around, rename "*_test" rules to "*_test_".
+func canonicalizeModuleType(moduleName string) string {
+ if strings.HasSuffix(moduleName, "_test") {
+ return moduleName + "_"
+ }
+
+ return moduleName
+}
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
new file mode 100644
index 0000000..b40aa1b
--- /dev/null
+++ b/bp2build/conversion_test.go
@@ -0,0 +1,107 @@
+// 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 bp2build
+
+import (
+ "sort"
+ "testing"
+)
+
+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",
+ },
+ {
+ dir: "",
+ basename: "WORKSPACE",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "BUILD",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "providers.bzl",
+ },
+ {
+ dir: bazelRulesSubDir,
+ basename: "soong_module.bzl",
+ },
+ }
+
+ 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",
+ },
+ }
+
+ assertFilecountsAreEqual(t, files, expectedFilePaths)
+ sortFiles(files)
+ assertFileContent(t, files, expectedFilePaths)
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
new file mode 100644
index 0000000..4c31d2d
--- /dev/null
+++ b/bp2build/testing.go
@@ -0,0 +1,139 @@
+package bp2build
+
+import (
+ "android/soong/android"
+ "android/soong/bazel"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type nestedProps struct {
+ Nested_prop string
+}
+
+type customProps struct {
+ Bool_prop bool
+ Bool_ptr_prop *bool
+ // Ensure that properties tagged `blueprint:mutated` are omitted
+ Int_prop int `blueprint:"mutated"`
+ Int64_ptr_prop *int64
+ String_prop string
+ String_ptr_prop *string
+ String_list_prop []string
+
+ Nested_props nestedProps
+ Nested_props_ptr *nestedProps
+}
+
+type customModule struct {
+ android.ModuleBase
+
+ props customProps
+}
+
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+ return android.PathsForTesting("path" + tag), nil
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customModuleFactoryBase() android.Module {
+ module := &customModule{}
+ module.AddProperties(&module.props)
+ return module
+}
+
+func customModuleFactory() android.Module {
+ m := customModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type testProps struct {
+ Test_prop struct {
+ Test_string_prop string
+ }
+}
+
+type customTestModule struct {
+ android.ModuleBase
+
+ props customProps
+ test_props testProps
+}
+
+func (m *customTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // nothing for now.
+}
+
+func customTestModuleFactoryBase() android.Module {
+ m := &customTestModule{}
+ m.AddProperties(&m.props)
+ m.AddProperties(&m.test_props)
+ return m
+}
+
+func customTestModuleFactory() android.Module {
+ m := customTestModuleFactoryBase()
+ android.InitAndroidModule(m)
+ return m
+}
+
+type customDefaultsModule struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func customDefaultsModuleFactoryBase() android.DefaultsModule {
+ module := &customDefaultsModule{}
+ module.AddProperties(&customProps{})
+ return module
+}
+
+func customDefaultsModuleFactoryBasic() android.Module {
+ return customDefaultsModuleFactoryBase()
+}
+
+func customDefaultsModuleFactory() android.Module {
+ m := customDefaultsModuleFactoryBase()
+ android.InitDefaultsModule(m)
+ return m
+}
+
+type customBazelModuleAttributes struct {
+ Name *string
+ String_prop string
+ String_list_prop []string
+}
+
+type customBazelModule struct {
+ android.BazelTargetModuleBase
+ customBazelModuleAttributes
+}
+
+func customBazelModuleFactory() android.Module {
+ module := &customBazelModule{}
+ module.AddProperties(&module.customBazelModuleAttributes)
+ android.InitBazelTargetModule(module)
+ return module
+}
+
+func (m *customBazelModule) Name() string { return m.BaseModuleName() }
+func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+
+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/bpf/bpf.go b/bpf/bpf.go
index 297e13a..8142f10 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -120,6 +120,7 @@
names = append(names, objName)
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", objName)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
@@ -129,6 +130,7 @@
}
fmt.Fprintln(w, "include $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_MODULE := ", name)
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(names, " "))
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
},
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index faec473..94b8252 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -784,9 +784,9 @@
return nil
}
- bool_variables, ok := getLiteralListProperty(mod, "bool_variables")
+ boolVariables, ok := getLiteralListProperty(mod, "bool_variables")
if ok {
- patchList.Add(bool_variables.RBracePos.Offset, bool_variables.RBracePos.Offset, ","+boolValues.String())
+ patchList.Add(boolVariables.RBracePos.Offset, boolVariables.RBracePos.Offset, ","+boolValues.String())
} else {
patchList.Add(variables.RBracePos.Offset+2, variables.RBracePos.Offset+2,
fmt.Sprintf(`bool_variables: [%s],`, boolValues.String()))
diff --git a/build_test.bash b/build_test.bash
index accca0f..3230f2d 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -23,6 +23,11 @@
# evolve as we find interesting things to test or track performance for.
#
+# Products that are broken or otherwise don't work with multiproduct_kati
+SKIPPED_PRODUCTS=(
+ mainline_sdk
+)
+
# To track how long we took to startup. %N isn't supported on Darwin, but
# that's detected in the Go code, which skips calculating the startup time.
export TRACE_BEGIN_SOONG=$(date +%s%N)
@@ -50,4 +55,4 @@
echo
echo "Running Soong test..."
soong_build_go multiproduct_kati android/soong/cmd/multiproduct_kati
-exec "$(getoutdir)/multiproduct_kati" "$@"
+exec "$(getoutdir)/multiproduct_kati" --skip-products "$(echo "${SKIPPED_PRODUCTS[@]-}" | tr ' ' ',')" "$@"
diff --git a/cc/Android.bp b/cc/Android.bp
index 88104e2..33f3db2 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -31,6 +31,7 @@
"sanitize.go",
"sabi.go",
"sdk.go",
+ "snapshot_prebuilt.go",
"snapshot_utils.go",
"stl.go",
"strip.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 320e69b..040aa0b 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -46,7 +46,7 @@
InRamdisk() bool
InVendorRamdisk() bool
InRecovery() bool
- AnyVariantDirectlyInAnyApex() bool
+ NotInPlatform() bool
}
type subAndroidMkProvider interface {
@@ -113,7 +113,7 @@
entries.SetString("LOCAL_SOONG_VNDK_VERSION", c.VndkVersion())
// VNDK libraries available to vendor are not installed because
// they are packaged in VNDK APEX and installed by APEX packages (apex/apex.go)
- if !c.isVndkExt() {
+ if !c.IsVndkExt() {
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
}
}
@@ -130,7 +130,7 @@
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake &&
c.CcLibraryInterface() && c.Shared() {
// Using the SDK variant as a JNI library needs a copy of the .so that
@@ -232,8 +232,8 @@
if len(library.Properties.Overrides) > 0 {
entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, library.Properties.Overrides), " "))
}
- if len(library.post_install_cmds) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(library.post_install_cmds, "&& "))
+ if len(library.postInstallCmds) > 0 {
+ entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(library.postInstallCmds, "&& "))
}
})
} else if library.header() {
@@ -269,11 +269,11 @@
if library.shared() && !library.buildStubs() {
ctx.subAndroidMk(entries, library.baseInstaller)
} else {
- if library.buildStubs() {
+ if library.buildStubs() && library.stubsVersion() != "" {
entries.SubName = "." + library.stubsVersion()
}
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- // library.makeUninstallable() depends on this to bypass SkipInstall() for
+ // library.makeUninstallable() depends on this to bypass HideFromMake() for
// static libraries.
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
if library.buildStubs() {
@@ -281,10 +281,15 @@
}
})
}
- if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.AnyVariantDirectlyInAnyApex() &&
+ // If a library providing a stub is included in an APEX, the private APIs of the library
+ // is accessible only inside the APEX. From outside of the APEX, clients can only use the
+ // public APIs via the stub. To enforce this, the (latest version of the) stub gets the
+ // name of the library. The impl library instead gets the `.bootstrap` suffix to so that
+ // they can be exceptionally used directly when APEXes are not available (e.g. during the
+ // very early stage in the boot process).
+ if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() &&
!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() && !ctx.static() {
if library.buildStubs() && library.isLatestStubVersion() {
- // reference the latest version via its name without suffix when it is provided by apex
entries.SubName = ""
}
if !library.buildStubs() {
@@ -296,7 +301,7 @@
func (object *objectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
entries.ExtraFooters = append(entries.ExtraFooters,
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
out := entries.OutputFile.Path()
varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
@@ -323,8 +328,8 @@
if len(binary.Properties.Overrides) > 0 {
entries.SetString("LOCAL_OVERRIDES_MODULES", strings.Join(makeOverrideModuleNames(ctx, binary.Properties.Overrides), " "))
}
- if len(binary.post_install_cmds) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(binary.post_install_cmds, "&& "))
+ if len(binary.postInstallCmds) > 0 {
+ entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(binary.postInstallCmds, "&& "))
}
})
}
@@ -466,18 +471,9 @@
}
func (c *llndkStubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
- entries.Class = "SHARED_LIBRARIES"
- entries.OverrideName = c.implementationModuleName(ctx.BaseModuleName())
-
- entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- c.libraryDecorator.androidMkWriteExportedFlags(entries)
- _, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
-
- entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
- entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
- entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
- entries.SetString("LOCAL_SOONG_TOC", c.toc().String())
- })
+ // Don't write anything for an llndk_library module, the vendor variant of the cc_library
+ // module will write the Android.mk entries.
+ entries.Disabled = true
}
func (c *vndkPrebuiltLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
@@ -523,9 +519,7 @@
entries.SubName += ".cfi"
}
- if c.androidMkVendorSuffix {
- entries.SubName += vendorSuffix
- }
+ entries.SubName += c.androidMkSuffix
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
c.libraryDecorator.androidMkWriteExportedFlags(entries)
@@ -552,12 +546,7 @@
func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
-
- if c.androidMkVendorSuffix {
- entries.SubName = vendorSuffix
- } else {
- entries.SubName = ""
- }
+ entries.SubName = c.androidMkSuffix
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
@@ -566,15 +555,10 @@
func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
-
- if c.androidMkVendorSuffix {
- entries.SubName = vendorSuffix
- } else {
- entries.SubName = ""
- }
+ entries.SubName = c.androidMkSuffix
entries.ExtraFooters = append(entries.ExtraFooters,
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
out := entries.OutputFile.Path()
varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
diff --git a/cc/binary.go b/cc/binary.go
index fa3966f..71c865b 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -109,7 +109,7 @@
// Action command lines to run directly after the binary is installed. For example,
// may be used to symlink runtime dependencies (such as bionic) alongside installation.
- post_install_cmds []string
+ postInstallCmds []string
}
var _ linker = (*binaryDecorator)(nil)
@@ -481,11 +481,11 @@
target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), file.Base())
ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
- binary.post_install_cmds = append(binary.post_install_cmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
+ binary.postInstallCmds = append(binary.postInstallCmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
for _, symlink := range binary.symlinks {
ctx.InstallAbsoluteSymlink(dir, symlink, target)
- binary.post_install_cmds = append(binary.post_install_cmds, makeSymlinkCmd(dirOnDevice, symlink, target))
+ binary.postInstallCmds = append(binary.postInstallCmds, makeSymlinkCmd(dirOnDevice, symlink, target))
}
}
diff --git a/cc/builder.go b/cc/builder.go
index 439e372..9cd78d5 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -235,6 +235,7 @@
}, &remoteexec.REParams{
Labels: map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
ExecStrategy: "${config.REAbiDumperExecStrategy}",
+ Inputs: []string{"$sAbiLinkerLibs"},
Platform: map[string]string{
remoteexec.PoolKey: "${config.RECXXPool}",
},
@@ -568,8 +569,11 @@
ccCmd = "clang++"
moduleFlags = cppflags
moduleToolingFlags = toolingCppflags
+ case ".h", ".hpp":
+ ctx.PropertyErrorf("srcs", "Header file %s is not supported, instead use export_include_dirs or local_include_dirs.", srcFile)
+ continue
default:
- ctx.ModuleErrorf("File %s has unknown extension", srcFile)
+ ctx.PropertyErrorf("srcs", "File %s has unknown extension. Supported extensions: .s, .S, .c, .cpp, .cc, .cxx, .mm", srcFile)
continue
}
diff --git a/cc/cc.go b/cc/cc.go
index f922ea5..8755efe 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -52,17 +52,19 @@
ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
ctx.BottomUp("vendor_snapshot", VendorSnapshotMutator).Parallel()
ctx.BottomUp("vendor_snapshot_source", VendorSnapshotSourceMutator).Parallel()
+ ctx.BottomUp("recovery_snapshot", RecoverySnapshotMutator).Parallel()
+ ctx.BottomUp("recovery_snapshot_source", RecoverySnapshotSourceMutator).Parallel()
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("asan_deps", sanitizerDepsMutator(asan))
- ctx.BottomUp("asan", sanitizerMutator(asan)).Parallel()
+ ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
+ ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
ctx.TopDown("hwasan_deps", sanitizerDepsMutator(hwasan))
ctx.BottomUp("hwasan", sanitizerMutator(hwasan)).Parallel()
- ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(fuzzer))
- ctx.BottomUp("fuzzer", sanitizerMutator(fuzzer)).Parallel()
+ ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
+ ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
// cfi mutator shouldn't run before sanitizers that return true for
// incompatibleWithCfi()
@@ -79,7 +81,6 @@
ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
ctx.BottomUp("coverage", coverageMutator).Parallel()
- ctx.TopDown("vndk_deps", sabiDepsMutator)
ctx.TopDown("lto_deps", ltoDepsMutator)
ctx.BottomUp("lto", ltoMutator).Parallel()
@@ -88,6 +89,11 @@
ctx.TopDown("double_loadable", checkDoubleLoadableLibraries).Parallel()
})
+ ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ // sabi mutator needs to be run after apex mutator finishes.
+ ctx.TopDown("sabi_deps", sabiDepsMutator)
+ })
+
ctx.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
}
@@ -129,6 +135,9 @@
// Used for host bionic
LinkerFlagsFile string
DynamicLinker string
+
+ // List of libs that need to be excluded for APEX variant
+ ExcludeLibsForApex []string
}
// PathDeps is a struct containing file paths to dependencies of a module.
@@ -247,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.
@@ -327,11 +352,22 @@
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
- // different snapshots (vendor, recovery, etc.), but these properties
- // allow a partner to exclude a module normally thought of as a
- // framework module from a snapshot.
- Exclude_from_vendor_snapshot *bool
+ // different snapshots (vendor, recovery, etc.), but this property
+ // allows a partner to exclude a module normally thought of as a
+ // framework module from the vendor snapshot.
+ Exclude_from_vendor_snapshot *bool
+
+ // Normally Soong uses the directory structure to decide which modules
+ // should be included (framework) or excluded (non-framework) from the
+ // different snapshots (vendor, recovery, etc.), but this property
+ // allows a partner to exclude a module normally thought of as a
+ // framework module from the recovery snapshot.
Exclude_from_recovery_snapshot *bool
+
+ // List of APEXes that this module has private access to for testing purpose. The module
+ // can depend on libraries that are not exported by the APEXes and use private symbols
+ // from the exported libraries.
+ Test_for []string
}
type VendorProperties struct {
@@ -348,7 +384,8 @@
//
// If set to false, this module becomes inaccessible from /vendor modules.
//
- // Default value is true when vndk: {enabled: true} or vendor: true.
+ // The modules with vndk: {enabled: true} must define 'vendor_available'
+ // to 'true'.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
Vendor_available *bool
@@ -364,7 +401,12 @@
// make assumptions about the system that may not be true in the
// future.
//
- // It must be set to true by default for vndk: {enabled: true} modules.
+ // If set to false, this module becomes inaccessible from /product modules.
+ //
+ // Different from the 'vendor_available' property, the modules with
+ // vndk: {enabled: true} don't have to define 'product_available'. The VNDK
+ // library without 'product_available' may not be depended on by any other
+ // modules that has product variants including the product available VNDKs.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
// and PRODUCT_PRODUCT_VNDK_VERSION isn't set.
@@ -378,6 +420,26 @@
// explicitly marked as `double_loadable: true` by the owner, or the dependency
// from the LLNDK lib should be cut if the lib is not designed to be double loaded.
Double_loadable *bool
+
+ // IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
+ IsLLNDK bool `blueprint:"mutated"`
+
+ // IsVNDKUsingCoreVariant is true for VNDK modules if the global VndkUseCoreVariant option is
+ // set and the module is not listed in VndkMustUseVendorVariantList.
+ IsVNDKUsingCoreVariant bool `blueprint:"mutated"`
+
+ // IsVNDKCore is set if a VNDK module does not set the vndk.support_system_process property.
+ IsVNDKCore bool `blueprint:"mutated"`
+
+ // IsVNDKSP is set if a VNDK module sets the vndk.support_system_process property.
+ IsVNDKSP bool `blueprint:"mutated"`
+
+ // IsVNDKPrivate is set if a VNDK module sets the vndk.private property or an LLNDK
+ // module sets the llndk.private property.
+ IsVNDKPrivate bool `blueprint:"mutated"`
+
+ // IsVNDKProduct is set if a VNDK module sets the product_available property.
+ IsVNDKProduct bool `blueprint:"mutated"`
}
// ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -387,6 +449,7 @@
type ModuleContextIntf interface {
static() bool
staticBinary() bool
+ testBinary() bool
header() bool
binary() bool
object() bool
@@ -394,20 +457,22 @@
canUseSdk() bool
useSdk() bool
sdkVersion() string
+ minSdkVersion() string
+ isSdkVariant() bool
useVndk() bool
isNdk(config android.Config) bool
- isLlndk(config android.Config) bool
- isLlndkPublic(config android.Config) bool
- isVndkPrivate(config android.Config) bool
+ IsLlndk() bool
+ IsLlndkPublic() bool
+ isImplementationForLLNDKPublic() bool
+ IsVndkPrivate() bool
isVndk() bool
isVndkSp() bool
- isVndkExt() bool
+ IsVndkExt() bool
inProduct() bool
inVendor() bool
inRamdisk() bool
inVendorRamdisk() bool
inRecovery() bool
- shouldCreateSourceAbiDump() bool
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
@@ -421,6 +486,8 @@
mustUseVendorVariant() bool
nativeCoverage() bool
directlyInAnyApex() bool
+ isPreventInstall() bool
+ isCfiAssemblySupportEnabled() bool
}
type ModuleContext interface {
@@ -572,6 +639,12 @@
staticUnwinder bool
makeSuffix string
+
+ // Whether or not this dependency should skip the apex dependency check
+ skipApexAllowedDependenciesCheck bool
+
+ // Whether or not this dependency has to be followed for the apex variants
+ excludeInApex bool
}
// header returns true if the libraryDependencyTag is tagging a header lib dependency.
@@ -630,6 +703,7 @@
runtimeDepTag = installDependencyTag{name: "runtime lib"}
testPerSrcDepTag = dependencyTag{name: "test_per_src"}
stubImplDepTag = dependencyTag{name: "stub_impl"}
+ llndkStubDepTag = dependencyTag{name: "llndk stub"}
)
type copyDirectlyInAnyApexDependencyTag dependencyTag
@@ -734,6 +808,14 @@
hideApexVariantFromMake bool
}
+func (c *Module) SetPreventInstall() {
+ c.Properties.PreventInstall = true
+}
+
+func (c *Module) SetHideFromMake() {
+ c.Properties.HideFromMake = true
+}
+
func (c *Module) Toc() android.OptionalPath {
if c.linker != nil {
if library, ok := c.linker.(libraryInterface); ok {
@@ -961,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)
@@ -975,7 +1053,7 @@
// Returns true for dependency roots (binaries)
// TODO(ccross): also handle dlopenable libraries
-func (c *Module) isDependencyRoot() bool {
+func (c *Module) IsDependencyRoot() bool {
if root, ok := c.linker.(interface {
isDependencyRoot() bool
}); ok {
@@ -995,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 {
@@ -1013,20 +1092,42 @@
return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
}
-func (c *Module) isLlndk(config android.Config) bool {
- // Returns true for both LLNDK (public) and LLNDK-private libs.
- return isLlndkLibrary(c.BaseModuleName(), config)
+// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
+func (c *Module) IsLlndk() bool {
+ return c.VendorProperties.IsLLNDK
}
-func (c *Module) isLlndkPublic(config android.Config) bool {
- // Returns true only for LLNDK (public) libs.
- name := c.BaseModuleName()
- return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
+// IsLlndkPublic returns true only for LLNDK (public) libs.
+func (c *Module) IsLlndkPublic() bool {
+ return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsVNDKPrivate
}
-func (c *Module) isVndkPrivate(config android.Config) bool {
- // Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
- return isVndkPrivateLibrary(c.BaseModuleName(), config)
+// isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
+// and does not set llndk.vendor_available: false.
+func (c *Module) isImplementationForLLNDKPublic() bool {
+ library, _ := c.library.(*libraryDecorator)
+ return library != nil && library.hasLLNDKStubs() &&
+ (!Bool(library.Properties.Llndk.Private) ||
+ // TODO(b/170784825): until the LLNDK properties are moved into the cc_library,
+ // the non-Vendor variants of the cc_library don't know if the corresponding
+ // llndk_library set private: true. Since libft2 is the only private LLNDK
+ // library, hardcode it during the transition.
+ c.BaseModuleName() != "libft2")
+}
+
+// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
+func (c *Module) IsVndkPrivate() bool {
+ // Check if VNDK-core-private or VNDK-SP-private
+ if c.IsVndk() {
+ return Bool(c.vndkdep.Properties.Vndk.Private)
+ }
+
+ // Check if LLNDK-private
+ if library, ok := c.library.(*libraryDecorator); ok && c.IsLlndk() {
+ return Bool(library.Properties.Llndk.Private)
+ }
+
+ return false
}
func (c *Module) IsVndk() bool {
@@ -1057,7 +1158,7 @@
return false
}
-func (c *Module) isVndkExt() bool {
+func (c *Module) IsVndkExt() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndkExt()
}
@@ -1099,6 +1200,19 @@
return name
}
+// Similar to ImplementationModuleName, but uses the Make variant of the module
+// name as base name, for use in AndroidMk output. E.g. for a prebuilt module
+// where the Soong name is prebuilt_foo, this returns foo (which works in Make
+// under the premise that the prebuilt module overrides its source counterpart
+// if it is exposed to Make).
+func (c *Module) ImplementationModuleNameForMake(ctx android.BaseModuleContext) string {
+ name := c.BaseModuleName()
+ if versioned, ok := c.linker.(versionedInterface); ok {
+ name = versioned.implementationModuleName(name)
+ }
+ return name
+}
+
func (c *Module) bootstrap() bool {
return Bool(c.Properties.Bootstrap)
}
@@ -1128,7 +1242,7 @@
func isBionic(name string) bool {
switch name {
- case "libc", "libm", "libdl", "libdl_android", "linker":
+ case "libc", "libm", "libdl", "libdl_android", "linker", "linkerconfig":
return true
}
return false
@@ -1145,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
@@ -1177,8 +1296,12 @@
return ctx.mod.staticBinary()
}
+func (ctx *moduleContextImpl) testBinary() bool {
+ return ctx.mod.testBinary()
+}
+
func (ctx *moduleContextImpl) header() bool {
- return ctx.mod.header()
+ return ctx.mod.Header()
}
func (ctx *moduleContextImpl) binary() bool {
@@ -1211,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()
}
@@ -1219,16 +1365,20 @@
return ctx.mod.IsNdk(config)
}
-func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
- return ctx.mod.isLlndk(config)
+func (ctx *moduleContextImpl) IsLlndk() bool {
+ return ctx.mod.IsLlndk()
}
-func (ctx *moduleContextImpl) isLlndkPublic(config android.Config) bool {
- return ctx.mod.isLlndkPublic(config)
+func (ctx *moduleContextImpl) IsLlndkPublic() bool {
+ return ctx.mod.IsLlndkPublic()
}
-func (ctx *moduleContextImpl) isVndkPrivate(config android.Config) bool {
- return ctx.mod.isVndkPrivate(config)
+func (ctx *moduleContextImpl) isImplementationForLLNDKPublic() bool {
+ return ctx.mod.isImplementationForLLNDKPublic()
+}
+
+func (ctx *moduleContextImpl) IsVndkPrivate() bool {
+ return ctx.mod.IsVndkPrivate()
}
func (ctx *moduleContextImpl) isVndk() bool {
@@ -1247,50 +1397,14 @@
return ctx.mod.isVndkSp()
}
-func (ctx *moduleContextImpl) isVndkExt() bool {
- return ctx.mod.isVndkExt()
+func (ctx *moduleContextImpl) IsVndkExt() bool {
+ return ctx.mod.IsVndkExt()
}
func (ctx *moduleContextImpl) mustUseVendorVariant() bool {
return ctx.mod.MustUseVendorVariant()
}
-// Check whether ABI dumps should be created for this module.
-func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
- if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
- return false
- }
-
- // Coverage builds have extra symbols.
- if ctx.mod.isCoverageVariant() {
- return false
- }
-
- if ctx.ctx.Fuchsia() {
- return false
- }
-
- if sanitize := ctx.mod.sanitize; sanitize != nil {
- if !sanitize.isVariantOnProductionDevice() {
- return false
- }
- }
- if !ctx.ctx.Device() {
- // Host modules do not need ABI dumps.
- return false
- }
- if ctx.isNDKStubLibrary() {
- // Stubs do not need ABI dumps.
- return false
- }
- if lib := ctx.mod.library; lib != nil && lib.buildStubs() {
- // Stubs do not need ABI dumps.
- return false
- }
-
- return true
-}
-
func (ctx *moduleContextImpl) selectedStl() string {
if stl := ctx.mod.stl; stl != nil {
return stl.Properties.SelectedStl
@@ -1334,6 +1448,14 @@
return ctx.mod.DirectlyInAnyApex()
}
+func (ctx *moduleContextImpl) isPreventInstall() bool {
+ 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,
@@ -1363,6 +1485,10 @@
return nil
}
+func (c *Module) IsPrebuilt() bool {
+ return c.Prebuilt() != nil
+}
+
func (c *Module) Name() string {
name := c.ModuleBase.Name()
if p, ok := c.linker.(interface {
@@ -1401,7 +1527,7 @@
// "current", it will append the VNDK version to the name suffix.
var vndkVersion string
var nameSuffix string
- if c.inProduct() {
+ if c.InProduct() {
vndkVersion = ctx.DeviceConfig().ProductVndkVersion()
nameSuffix = productSuffix
} else {
@@ -1411,7 +1537,7 @@
if vndkVersion == "current" {
vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
}
- if c.Properties.VndkVersion != vndkVersion {
+ if c.Properties.VndkVersion != vndkVersion && c.Properties.VndkVersion != "" {
// add version suffix only if the module is using different vndk version than the
// version in product or vendor partition.
nameSuffix += "." + c.Properties.VndkVersion
@@ -1435,7 +1561,7 @@
c.hideApexVariantFromMake = true
}
- c.makeLinkType = c.getMakeLinkType(actx)
+ c.makeLinkType = GetMakeLinkType(actx, c)
c.Properties.SubName = ""
@@ -1443,7 +1569,7 @@
c.Properties.SubName += nativeBridgeSuffix
}
- _, llndk := c.linker.(*llndkStubDecorator)
+ llndk := c.IsLlndk()
_, llndkHeader := c.linker.(*llndkHeadersDecorator)
if llndk || llndkHeader || (c.UseVndk() && c.HasNonSystemVariants()) {
// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
@@ -1562,39 +1688,47 @@
}
c.outputFile = android.OptionalPathForPath(outputFile)
- // If a lib is directly included in any of the APEXes, unhide the stubs
- // variant having the latest version gets visible to make. In addition,
- // the non-stubs variant is renamed to <libname>.bootstrap. This is to
- // force anything in the make world to link against the stubs library.
- // (unless it is explicitly referenced via .bootstrap suffix or the
- // module is marked with 'bootstrap: true').
- if c.HasStubsVariants() && c.AnyVariantDirectlyInAnyApex() && !c.InRamdisk() &&
+ // If a lib is directly included in any of the APEXes or is not available to the
+ // platform (which is often the case when the stub is provided as a prebuilt),
+ // unhide the stubs variant having the latest version gets visible to make. In
+ // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
+ // force anything in the make world to link against the stubs library. (unless it
+ // is explicitly referenced via .bootstrap suffix or the module is marked with
+ // 'bootstrap: true').
+ if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
c.IsStubs() && !c.InVendorRamdisk() {
c.Properties.HideFromMake = false // unhide
// Note: this is still non-installable
}
- // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current.
- if i, ok := c.linker.(snapshotLibraryInterface); ok && ctx.DeviceConfig().VndkVersion() == "current" {
- if isSnapshotAware(ctx, c, apexInfo) {
+ // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
+ // RECOVERY_SNAPSHOT_VERSION is current.
+ if i, ok := c.linker.(snapshotLibraryInterface); ok {
+ if shouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
i.collectHeadersForSnapshot(ctx)
}
}
}
- if c.installable(apexInfo) {
+ if !proptools.BoolDefault(c.Properties.Installable, true) {
+ // If the module has been specifically configure to not be installed then
+ // hide from make as otherwise it will break when running inside make
+ // as the output path to install will not be specified. Not all uninstallable
+ // modules can be hidden from make as some are needed for resolving make side
+ // dependencies.
+ c.HideFromMake()
+ } else if !c.installable(apexInfo) {
+ c.SkipInstall()
+ }
+
+ // Still call c.installer.install though, the installs will be stored as PackageSpecs
+ // to allow using the outputs in a genrule.
+ if c.installer != nil && c.outputFile.Valid() {
c.installer.install(ctx, c.outputFile.Path())
if ctx.Failed() {
return
}
- } else if !proptools.BoolDefault(c.Properties.Installable, true) {
- // If the module has been specifically configure to not be installed then
- // skip the installation as otherwise it will break when running inside make
- // as the output path to install will not be specified. Not all uninstallable
- // modules can skip installation as some are needed for resolving make side
- // dependencies.
- c.SkipInstall()
}
}
@@ -1812,12 +1946,9 @@
vendorPublicLibraries := vendorPublicLibraries(actx.Config())
vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
+ recoverySnapshotSharedLibs := recoverySnapshotSharedLibs(actx.Config())
rewriteVendorLibs := func(lib string) string {
- if isLlndkLibrary(lib, ctx.Config()) {
- return lib + llndkLibrarySuffix
- }
-
// only modules with BOARD_VNDK_VERSION uses snapshot.
if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
return lib
@@ -1836,7 +1967,19 @@
for _, entry := range list {
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
- if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
+ if c.InRecovery() {
+ recoverySnapshotVersion :=
+ actx.DeviceConfig().RecoverySnapshotVersion()
+ if recoverySnapshotVersion == "current" ||
+ recoverySnapshotVersion == "" {
+ nonvariantLibs = append(nonvariantLibs, name)
+ } else if snapshot, ok := recoverySnapshotSharedLibs.get(
+ name, actx.Arch().ArchType); ok {
+ nonvariantLibs = append(nonvariantLibs, snapshot)
+ } else {
+ nonvariantLibs = append(nonvariantLibs, name)
+ }
+ } else if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
variantLibs = append(variantLibs, name+ndkLibrarySuffix)
} else if ctx.useVndk() {
nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry))
@@ -1868,13 +2011,6 @@
}
}
- buildStubs := false
- if versioned, ok := c.linker.(versionedInterface); ok {
- if versioned.buildStubs() {
- buildStubs = true
- }
- }
-
rewriteSnapshotLibs := func(lib string, snapshotMap *snapshotMap) string {
// only modules with BOARD_VNDK_VERSION uses snapshot.
if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
@@ -1888,16 +2024,38 @@
return lib
}
- vendorSnapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
+ snapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
+ snapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
+ snapshotObjects := vendorSnapshotObjects(actx.Config())
+
+ if c.InRecovery() {
+ rewriteSnapshotLibs = func(lib string, snapshotMap *snapshotMap) string {
+ recoverySnapshotVersion :=
+ actx.DeviceConfig().RecoverySnapshotVersion()
+ if recoverySnapshotVersion == "current" ||
+ recoverySnapshotVersion == "" {
+ return lib
+ } else if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok {
+ return snapshot
+ }
+
+ return lib
+ }
+
+ snapshotHeaderLibs = recoverySnapshotHeaderLibs(actx.Config())
+ snapshotStaticLibs = recoverySnapshotStaticLibs(actx.Config())
+ snapshotObjects = recoverySnapshotObjects(actx.Config())
+ }
+
for _, lib := range deps.HeaderLibs {
depTag := libraryDependencyTag{Kind: headerLibraryDependency}
if inList(lib, deps.ReexportHeaderLibHeaders) {
depTag.reexportFlags = true
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
+ lib = rewriteSnapshotLibs(lib, snapshotHeaderLibs)
- if buildStubs {
+ if c.IsStubs() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
depTag, lib)
} else {
@@ -1905,19 +2063,12 @@
}
}
- if buildStubs {
- // Stubs lib does not have dependency to other static/shared libraries.
- // Don't proceed.
- return
- }
-
// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
// C++ implementation library and one Java implementation library. When a module links against
// sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
// map from sysprop_library to implementation library; it will be used in whole_static_libs,
// static_libs, and shared_libs.
syspropImplLibraries := syspropImplLibraries(actx.Config())
- vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
for _, lib := range deps.WholeStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true, reexportFlags: true}
@@ -1925,7 +2076,7 @@
lib = impl
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+ lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -1937,12 +2088,15 @@
if inList(lib, deps.ReexportStaticLibHeaders) {
depTag.reexportFlags = true
}
+ if inList(lib, deps.ExcludeLibsForApex) {
+ depTag.excludeInApex = true
+ }
if impl, ok := syspropImplLibraries[lib]; ok {
lib = impl
}
- lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+ lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
@@ -1956,14 +2110,14 @@
depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
+ }, depTag, rewriteSnapshotLibs(staticUnwinder(actx), snapshotStaticLibs))
}
for _, lib := range deps.LateStaticLibs {
depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
- }, depTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
+ }, depTag, rewriteSnapshotLibs(lib, snapshotStaticLibs))
}
// shared lib names without the #version suffix
@@ -1974,6 +2128,9 @@
if inList(lib, deps.ReexportSharedLibHeaders) {
depTag.reexportFlags = true
}
+ if inList(lib, deps.ExcludeLibsForApex) {
+ depTag.excludeInApex = true
+ }
if impl, ok := syspropImplLibraries[lib]; ok {
lib = impl
@@ -2020,17 +2177,15 @@
actx.AddDependency(c, depTag, gen)
}
- vendorSnapshotObjects := vendorSnapshotObjects(actx.Config())
-
crtVariations := GetCrtVariations(ctx, c)
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
if deps.CrtBegin != "" {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
+ rewriteSnapshotLibs(deps.CrtBegin, snapshotObjects))
}
if deps.CrtEnd != "" {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
+ rewriteSnapshotLibs(deps.CrtEnd, snapshotObjects))
}
if deps.LinkerFlagsFile != "" {
actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2084,7 +2239,7 @@
return
}
- if from.Module().Target().Os != android.Android {
+ if from.Target().Os != android.Android {
// Host code is not restricted
return
}
@@ -2098,6 +2253,11 @@
if ccFrom.vndkdep != nil {
ccFrom.vndkdep.vndkCheckLinkType(ctx, ccTo, tag)
}
+ } else if linkableMod, ok := to.(LinkableInterface); ok {
+ // Static libraries from other languages can be linked
+ if !linkableMod.Static() {
+ ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
+ }
} else {
ctx.ModuleErrorf("Attempting to link VNDK cc.Module with unsupported module type")
}
@@ -2224,6 +2384,19 @@
return false
}
+ // These dependencies are not excercised at runtime. Tracking these will give us
+ // false negative, so skip.
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if IsHeaderDepTag(depTag) {
+ return false
+ }
+ if depTag == staticVariantTag {
+ return false
+ }
+ if depTag == stubImplDepTag {
+ return false
+ }
+
// Even if target lib has no vendor variant, keep checking dependency
// graph in case it depends on vendor_available or product_available
// but not double_loadable transtively.
@@ -2231,22 +2404,24 @@
return true
}
- if to.isVndkSp() || to.isLlndk(ctx.Config()) || Bool(to.VendorProperties.Double_loadable) {
+ // The happy path. Keep tracking dependencies until we hit a non double-loadable
+ // one.
+ if Bool(to.VendorProperties.Double_loadable) {
+ return true
+ }
+
+ if to.isVndkSp() || to.IsLlndk() {
return false
}
- var stringPath []string
- for _, m := range ctx.GetWalkPath() {
- stringPath = append(stringPath, m.Name())
- }
ctx.ModuleErrorf("links a library %q which is not LL-NDK, "+
"VNDK-SP, or explicitly marked as 'double_loadable:true'. "+
- "(dependency: %s)", ctx.OtherModuleName(to), strings.Join(stringPath, " -> "))
+ "Dependency list: %s", ctx.OtherModuleName(to), ctx.GetPathString(false))
return false
}
if module, ok := ctx.Module().(*Module); ok {
if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
- if module.isLlndk(ctx.Config()) || Bool(module.VendorProperties.Double_loadable) {
+ if lib.hasLLNDKStubs() {
ctx.WalkDeps(check)
}
}
@@ -2257,8 +2432,8 @@
// For example, with maxSdkVersion is 10 and versionList is [9,11]
// it returns 9 as string. The list of stubs must be in order from
// oldest to newest.
-func (c *Module) chooseSdkVersion(ctx android.PathContext, stubsInfo []SharedLibraryStubsInfo,
- maxSdkVersion android.ApiLevel) (SharedLibraryStubsInfo, error) {
+func (c *Module) chooseSdkVersion(ctx android.PathContext, stubsInfo []SharedStubLibrary,
+ maxSdkVersion android.ApiLevel) (SharedStubLibrary, error) {
for i := range stubsInfo {
stubInfo := stubsInfo[len(stubsInfo)-i-1]
@@ -2269,7 +2444,7 @@
var err error
ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version)
if err != nil {
- return SharedLibraryStubsInfo{}, err
+ return SharedStubLibrary{}, err
}
}
if ver.LessThanOrEqualTo(maxSdkVersion) {
@@ -2280,7 +2455,7 @@
for _, stubInfo := range stubsInfo {
versionList = append(versionList, stubInfo.Version)
}
- return SharedLibraryStubsInfo{}, fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion.String(), versionList)
+ return SharedStubLibrary{}, fmt.Errorf("not found a version(<=%s) in versionList: %v", maxSdkVersion.String(), versionList)
}
// Convert dependencies to paths. Returns a PathDeps containing paths
@@ -2366,9 +2541,6 @@
if depTag == android.ProtoPluginDepTag {
return
}
- if depTag == llndkImplDep {
- return
- }
if dep.Target().Os != ctx.Os() {
ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
@@ -2403,6 +2575,10 @@
return
}
+ if !apexInfo.IsForPlatform() && libDepTag.excludeInApex {
+ return
+ }
+
depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
var ptr *android.Paths
@@ -2412,7 +2588,14 @@
switch {
case libDepTag.header():
- // nothing
+ if !ctx.OtherModuleHasProvider(dep, HeaderLibraryInfoProvider) {
+ if !ctx.Config().AllowMissingDependencies() {
+ ctx.ModuleErrorf("module %q is not a header library", depName)
+ } else {
+ ctx.AddMissingDependencies([]string{depName})
+ }
+ return
+ }
case libDepTag.shared():
if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
if !ctx.Config().AllowMissingDependencies() {
@@ -2422,10 +2605,11 @@
}
return
}
- sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
- sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryImplementationStubsInfoProvider).(SharedLibraryImplementationStubsInfo)
- if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedLibraryStubsInfos) > 0 {
+ sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+ sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
+
+ if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
useStubs := false
if lib := moduleLibraryInterface(dep); lib.buildStubs() && c.UseVndk() { // LLNDK
@@ -2440,7 +2624,7 @@
// an APEX (and not from platform)
// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
// always link to non-stub variant
- useStubs = dep.(android.ApexModule).AnyVariantDirectlyInAnyApex() && !c.bootstrap()
+ useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
// Another exception: if this module is bundled with an APEX, then
// it is linked with the non-stub variant of a module in the APEX
// as if this is part of the APEX.
@@ -2460,7 +2644,7 @@
// when to use (unspecified) stubs, check min_sdk_version and choose the right one
if useStubs {
sharedLibraryStubsInfo, err :=
- c.chooseSdkVersion(ctx, sharedLibraryStubsInfo.SharedLibraryStubsInfos, c.apexSdkVersion)
+ c.chooseSdkVersion(ctx, sharedLibraryStubsInfo.SharedStubLibraries, c.apexSdkVersion)
if err != nil {
ctx.OtherModuleErrorf(dep, err.Error())
return
@@ -2470,6 +2654,12 @@
}
}
+ // Stubs lib doesn't link to the shared lib dependencies. Don't set
+ // linkFile, depFile, and ptr.
+ if c.IsStubs() {
+ break
+ }
+
linkFile = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
depFile = sharedLibraryInfo.TableOfContents
@@ -2497,6 +2687,13 @@
}
return
}
+
+ // Stubs lib doesn't link to the static lib dependencies. Don't set
+ // linkFile, depFile, and ptr.
+ if c.IsStubs() {
+ break
+ }
+
staticLibraryInfo := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
linkFile = android.OptionalPathForPath(staticLibraryInfo.StaticLibrary)
if libDepTag.wholeStatic {
@@ -2624,7 +2821,9 @@
c.Properties.AndroidMkStaticLibs, makeLibName)
}
}
- } else {
+ } else if !c.IsStubs() {
+ // Stubs lib doesn't link to the runtime lib, object, crt, etc. dependencies.
+
switch depTag {
case runtimeDepTag:
c.Properties.AndroidMkRuntimeLibs = append(
@@ -2702,16 +2901,18 @@
func baseLibName(depName string) string {
libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
- libName = strings.TrimPrefix(libName, "prebuilt_")
+ libName = android.RemoveOptionalPrebuiltPrefix(libName)
return libName
}
func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
vendorSuffixModules := vendorSuffixModules(ctx.Config())
+ recoverySuffixModules := recoverySuffixModules(ctx.Config())
vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
libName := baseLibName(depName)
- isLLndk := isLlndkLibrary(libName, ctx.Config())
+ ccDepModule, _ := ccDep.(*Module)
+ isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
isVendorPublicLib := inList(libName, *vendorPublicLibraries)
bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
@@ -2724,8 +2925,10 @@
return baseName + ".vendor"
}
- if vendorSuffixModules[baseName] {
+ if c.InVendor() && vendorSuffixModules[baseName] {
return baseName + ".vendor"
+ } else if c.InRecovery() && recoverySuffixModules[baseName] {
+ return baseName + ".recovery"
} else {
return baseName
}
@@ -2749,7 +2952,7 @@
return libName + vendorRamdiskSuffix
} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
return libName + recoverySuffix
- } else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
+ } else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
return libName + nativeBridgeSuffix
} else {
return libName
@@ -2834,7 +3037,17 @@
return false
}
-func (c *Module) header() bool {
+func (c *Module) testBinary() bool {
+ if test, ok := c.linker.(interface {
+ testBinary() bool
+ }); ok {
+ return test.testBinary()
+ }
+ return false
+}
+
+// Header returns true if the module is a header-only variant. (See cc/library.go header()).
+func (c *Module) Header() bool {
if h, ok := c.linker.(interface {
header() bool
}); ok {
@@ -2861,22 +3074,21 @@
return false
}
-func (c *Module) getMakeLinkType(actx android.ModuleContext) string {
+func GetMakeLinkType(actx android.ModuleContext, c LinkableInterface) string {
if c.UseVndk() {
- if lib, ok := c.linker.(*llndkStubDecorator); ok {
- if Bool(lib.Properties.Vendor_available) {
- return "native:vndk"
+ if c.IsLlndk() {
+ if !c.IsLlndkPublic() {
+ return "native:vndk_private"
}
- return "native:vndk_private"
+ return "native:vndk"
}
- if c.IsVndk() && !c.isVndkExt() {
- // Product_available, if defined, must have the same value with Vendor_available.
- if Bool(c.VendorProperties.Vendor_available) {
- return "native:vndk"
+ if c.IsVndk() && !c.IsVndkExt() {
+ if c.IsVndkPrivate() {
+ return "native:vndk_private"
}
- return "native:vndk_private"
+ return "native:vndk"
}
- if c.inProduct() {
+ if c.InProduct() {
return "native:product"
}
return "native:vendor"
@@ -2886,7 +3098,7 @@
return "native:vendor_ramdisk"
} else if c.InRecovery() {
return "native:recovery"
- } else if c.Target().Os == android.Android && String(c.Properties.Sdk_version) != "" {
+ } else if c.Target().Os == android.Android && c.SdkVersion() != "" {
return "native:ndk:none:none"
// TODO(b/114741097): use the correct ndk stl once build errors have been fixed
//family, link := getNdkStlFamilyAndLinkType(c)
@@ -2922,13 +3134,7 @@
}
func (c *Module) TestFor() []string {
- if test, ok := c.linker.(interface {
- testFor() []string
- }); ok {
- return test.testFor()
- } else {
- return c.ApexModuleBase.TestFor()
- }
+ return c.Properties.Test_for
}
func (c *Module) UniqueApexVariations() bool {
@@ -2981,6 +3187,9 @@
}
}
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
depTag := ctx.OtherModuleDependencyTag(dep)
libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
@@ -3002,14 +3211,25 @@
// linked; the dependency is used only during the compilation phase.
return false
}
+
+ if isLibDepTag && libDepTag.excludeInApex {
+ return false
+ }
}
- if depTag == stubImplDepTag || depTag == llndkImplDep {
+ if depTag == stubImplDepTag || depTag == llndkStubDepTag {
// We don't track beyond LLNDK or from an implementation library to its stubs.
return false
}
+ if depTag == staticVariantTag {
+ // This dependency is for optimization (reuse *.o from the static lib). It doesn't
+ // actually mean that the static lib (and its dependencies) are copied into the
+ // APEX.
+ return false
+ }
return true
}
+// Implements android.ApexModule
func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index af9b943..d0d8759 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -68,6 +68,7 @@
t.Helper()
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
return testCcWithConfig(t, config)
@@ -81,6 +82,15 @@
return testCcWithConfig(t, config)
}
+func testCcNoProductVndk(t *testing.T, bp string) *android.TestContext {
+ t.Helper()
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+
+ return testCcWithConfig(t, config)
+}
+
func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
t.Helper()
@@ -224,9 +234,6 @@
t.Helper()
mod := ctx.ModuleForTests(name, variant).Module().(*Module)
- if !mod.HasVendorVariant() {
- t.Errorf("%q must have variant %q", name, variant)
- }
// Check library properties.
lib, ok := mod.compiler.(*libraryDecorator)
@@ -250,8 +257,8 @@
// Check VNDK extension properties.
isVndkExt := extends != ""
- if mod.isVndkExt() != isVndkExt {
- t.Errorf("%q isVndkExt() must equal to %t", name, isVndkExt)
+ if mod.IsVndkExt() != isVndkExt {
+ t.Errorf("%q IsVndkExt() must equal to %t", name, isVndkExt)
}
if actualExtends := mod.getVndkExtendsModuleName(); actualExtends != extends {
@@ -309,16 +316,8 @@
func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
t.Helper()
- vndkLibraries := ctx.ModuleForTests(module, "")
-
- var output string
- if module != "vndkcorevariant.libraries.txt" {
- output = insertVndkVersion(module, "VER")
- } else {
- output = module
- }
-
- checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
+ got := ctx.ModuleForTests(module, "").Module().(*vndkLibrariesTxt).fileNames
+ assertArrayString(t, got, expected)
}
func TestVndk(t *testing.T) {
@@ -326,7 +325,6 @@
cc_library {
name: "libvndk",
vendor_available: true,
- product_available: true,
vndk: {
enabled: true,
},
@@ -335,21 +333,38 @@
cc_library {
name: "libvndk_private",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
stem: "libvndk-private",
}
cc_library {
- name: "libvndk_sp",
+ name: "libvndk_product",
vendor_available: true,
product_available: true,
vndk: {
enabled: true,
+ },
+ nocrt: true,
+ target: {
+ vendor: {
+ cflags: ["-DTEST"],
+ },
+ product: {
+ cflags: ["-DTEST"],
+ },
+ },
+ }
+
+ cc_library {
+ name: "libvndk_sp",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
support_system_process: true,
},
nocrt: true,
@@ -358,11 +373,11 @@
cc_library {
name: "libvndk_sp_private",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
vndk: {
enabled: true,
support_system_process: true,
+ private: true,
},
nocrt: true,
target: {
@@ -371,20 +386,45 @@
},
},
}
- vndk_libraries_txt {
+
+ cc_library {
+ name: "libvndk_sp_product_private",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ private: true,
+ },
+ nocrt: true,
+ target: {
+ vendor: {
+ suffix: "-x",
+ },
+ product: {
+ suffix: "-x",
+ },
+ },
+ }
+
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
- vndk_libraries_txt {
+ vndksp_libraries_txt {
name: "vndksp.libraries.txt",
}
- vndk_libraries_txt {
+ vndkprivate_libraries_txt {
name: "vndkprivate.libraries.txt",
}
- vndk_libraries_txt {
+ vndkproduct_libraries_txt {
+ name: "vndkproduct.libraries.txt",
+ }
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
@@ -399,16 +439,15 @@
// They are installed as part of VNDK APEX instead.
checkVndkModule(t, ctx, "libvndk", "", false, "", vendorVariant)
checkVndkModule(t, ctx, "libvndk_private", "", false, "", vendorVariant)
+ checkVndkModule(t, ctx, "libvndk_product", "", false, "", vendorVariant)
checkVndkModule(t, ctx, "libvndk_sp", "", true, "", vendorVariant)
checkVndkModule(t, ctx, "libvndk_sp_private", "", true, "", vendorVariant)
+ checkVndkModule(t, ctx, "libvndk_sp_product_private", "", true, "", vendorVariant)
- checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
- checkVndkModule(t, ctx, "libvndk_private", "", false, "", productVariant)
- checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
- checkVndkModule(t, ctx, "libvndk_sp_private", "", true, "", productVariant)
+ checkVndkModule(t, ctx, "libvndk_product", "", false, "", productVariant)
+ checkVndkModule(t, ctx, "libvndk_sp_product_private", "", true, "", productVariant)
// Check VNDK snapshot output.
-
snapshotDir := "vndk-snapshot"
snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
@@ -429,6 +468,8 @@
checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
@@ -437,6 +478,7 @@
checkSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
checkSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
checkSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+ checkSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
@@ -446,16 +488,23 @@
"VNDK-SP: libc++.so",
"VNDK-SP: libvndk_sp-x.so",
"VNDK-SP: libvndk_sp_private-x.so",
+ "VNDK-SP: libvndk_sp_product_private-x.so",
"VNDK-core: libvndk-private.so",
"VNDK-core: libvndk.so",
+ "VNDK-core: libvndk_product.so",
"VNDK-private: libft2.so",
"VNDK-private: libvndk-private.so",
"VNDK-private: libvndk_sp_private-x.so",
+ "VNDK-private: libvndk_sp_product_private-x.so",
+ "VNDK-product: libc++.so",
+ "VNDK-product: libvndk_product.so",
+ "VNDK-product: libvndk_sp_product_private-x.so",
})
checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
- checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so"})
- checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so"})
- checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so", "libvndk_product.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkproduct.libraries.txt", []string{"libc++.so", "libvndk_product.so", "libvndk_sp_product_private-x.so"})
checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
}
@@ -487,7 +536,7 @@
}
}
- vndk_libraries_txt {
+ vndkcore_libraries_txt {
name: "vndkcore.libraries.txt",
}
`)
@@ -497,8 +546,9 @@
func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
bp := `
- vndk_libraries_txt {
+ llndk_libraries_txt {
name: "llndk.libraries.txt",
+ insert_vndk_version: true,
}`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -535,16 +585,18 @@
cc_library {
name: "libvndk2",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
}
- vndk_libraries_txt {
+ vndkcorevariant_libraries_txt {
name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
}
`
@@ -668,32 +720,90 @@
},
nocrt: true,
}
+ cc_library {
+ name: "libvndk-private",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ private: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "libllndk",
+ llndk_stubs: "libllndk.llndk",
+ }
+
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ export_llndk_headers: ["libllndk_headers"],
+ }
+
+ llndk_headers {
+ name: "libllndk_headers",
+ export_include_dirs: ["include"],
+ }
`)
checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
"LLNDK: libc.so",
"LLNDK: libdl.so",
"LLNDK: libft2.so",
+ "LLNDK: libllndk.so",
"LLNDK: libm.so",
"VNDK-SP: libc++.so",
+ "VNDK-core: libvndk-private.so",
"VNDK-core: libvndk.so",
"VNDK-private: libft2.so",
+ "VNDK-private: libvndk-private.so",
+ "VNDK-product: libc++.so",
+ "VNDK-product: libvndk-private.so",
+ "VNDK-product: libvndk.so",
})
}
func TestVndkModuleError(t *testing.T) {
// Check the error message for vendor_available and product_available properties.
- testCcError(t, "product_available: may not have different value than `vendor_available`", `
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
cc_library {
name: "libvndk",
- vendor_available: true,
- product_available: false,
vndk: {
enabled: true,
},
nocrt: true,
}
`)
+
+ testCcErrorProductVndk(t, "vndk: vendor_available must be set to true when `vndk: {enabled: true}`", `
+ cc_library {
+ name: "libvndk",
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+ `)
+
+ testCcErrorProductVndk(t, "product properties must have the same values with the vendor properties for VNDK modules", `
+ cc_library {
+ name: "libvndkprop",
+ vendor_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ target: {
+ vendor: {
+ cflags: ["-DTEST",],
+ },
+ },
+ }
+ `)
}
func TestVndkDepError(t *testing.T) {
@@ -826,10 +936,11 @@
testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
cc_library {
name: "libvndkprivate",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
shared_libs: ["libnonvndk"],
nocrt: true,
@@ -867,11 +978,12 @@
testCcError(t, "module \".*\" variant \".*\": \\(.*\\) should not link to \".*\"", `
cc_library {
name: "libvndkspprivate",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
+ private: true,
},
shared_libs: ["libnonvndk"],
nocrt: true,
@@ -962,10 +1074,11 @@
cc_library {
name: "libnondoubleloadable",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
double_loadable: true,
}
@@ -1049,6 +1162,16 @@
name: "obj",
vendor_available: true,
}
+
+ cc_library {
+ name: "libllndk",
+ llndk_stubs: "libllndk.llndk",
+ }
+
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ }
`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -1080,6 +1203,9 @@
filepath.Join(sharedDir, "libvendor.so.json"),
filepath.Join(sharedDir, "libvendor_available.so.json"))
+ // LLNDK modules are not captured
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
// Also cfi variants are captured, except for prebuilts like toolchain_library
staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
@@ -1129,6 +1255,104 @@
t.Errorf("%q expected but not found", jsonFile)
}
}
+
+ // fake snapshot should have all outputs in the normal snapshot.
+ fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+ for _, output := range snapshotSingleton.AllOutputs() {
+ fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+ if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+ t.Errorf("%q expected but not found", fakeOutput)
+ }
+ }
+}
+
+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) {
@@ -1400,10 +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, ctx *android.TestContext, name string, expected bool) {
+ t.Helper()
+ m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
+ if m.ExcludeFromRecoverySnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
}
}
@@ -1427,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 := `
@@ -1460,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.
@@ -1497,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.
@@ -1553,49 +1794,8 @@
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
- })
-}
-
-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 "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
})
}
@@ -1654,7 +1854,7 @@
}
`
config := TestConfig(buildDir, android.Android, nil, bp, nil)
- config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -1725,6 +1925,119 @@
}
}
+func TestRecoverySnapshotExclude(t *testing.T) {
+ // This test verifies that the exclude_from_recovery_snapshot property
+ // makes its way from the Android.bp source file into the module data
+ // structure. It also verifies that modules are correctly included or
+ // excluded in the recovery snapshot based on their path (framework or
+ // vendor) and the exclude_from_recovery_snapshot property.
+
+ frameworkBp := `
+ cc_library_shared {
+ name: "libinclude",
+ srcs: ["src/include.cpp"],
+ recovery_available: true,
+ }
+ cc_library_shared {
+ name: "libexclude",
+ srcs: ["src/exclude.cpp"],
+ 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: "librecovery",
+ srcs: ["recovery.cpp"],
+ recovery: true,
+ }
+ `
+
+ depsBp := GatherRequiredDepsForTest(android.Android)
+
+ mockFS := map[string][]byte{
+ "deps/Android.bp": []byte(depsBp),
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/include.cpp": nil,
+ "framework/exclude.cpp": nil,
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/recovery.cpp": nil,
+ }
+
+ config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+ config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ ctx := CreateTestContext(config)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ // Test an include and exclude framework module.
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+
+ // A recovery module is excluded, but by its path, not the
+ // exclude_from_recovery_snapshot property.
+ assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+
+ // Verify the content of the recovery snapshot.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+ // Included modules
+ checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+ // Excluded modules
+ checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.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.
+ 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 TestDoubleLoadableDepError(t *testing.T) {
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
@@ -1769,63 +2082,6 @@
}
`)
- // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable vendor_available lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: true,
- }
- `)
-
- // Check whether an error is emitted when a double_loadable lib depends on a non-double_loadable VNDK lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- }
- `)
-
- // Check whether an error is emitted when a double_loadable VNDK depends on a non-double_loadable VNDK private lib.
- testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
- cc_library {
- name: "libdoubleloadable",
- vendor_available: true,
- product_available: true,
- vndk: {
- enabled: true,
- },
- double_loadable: true,
- shared_libs: ["libnondoubleloadable"],
- }
-
- cc_library {
- name: "libnondoubleloadable",
- vendor_available: false,
- product_available: false,
- vndk: {
- enabled: true,
- },
- }
- `)
-
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable indirectly.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
cc_library {
@@ -1850,6 +2106,29 @@
vendor_available: true,
}
`)
+
+ // The error is not from 'client' but from 'libllndk'
+ testCcError(t, "module \"libllndk\".* links a library \"libnondoubleloadable\".*double_loadable", `
+ cc_library {
+ name: "client",
+ vendor_available: true,
+ double_loadable: true,
+ shared_libs: ["libllndk"],
+ }
+ cc_library {
+ name: "libllndk",
+ shared_libs: ["libnondoubleloadable"],
+ llndk_stubs: "libllndk.llndk",
+ }
+ llndk_library {
+ name: "libllndk.llndk",
+ symbol_file: "",
+ }
+ cc_library {
+ name: "libnondoubleloadable",
+ vendor_available: true,
+ }
+ `)
}
func TestCheckVndkMembershipBeforeDoubleLoadable(t *testing.T) {
@@ -2002,7 +2281,7 @@
func TestVndkExtWithoutProductVndkVersion(t *testing.T) {
// This test checks the VNDK-Ext properties when PRODUCT_PRODUCT_VNDK_VERSION is not set.
- ctx := testCc(t, `
+ ctx := testCcNoProductVndk(t, `
cc_library {
name: "libvndk",
vendor_available: true,
@@ -2172,14 +2451,15 @@
func TestVndkExtVendorAvailableFalseError(t *testing.T) {
// This test ensures an error is emitted when a VNDK-Ext library extends a VNDK library
- // with `vendor_available: false`.
- testCcError(t, "`extends` refers module \".*\" which does not have `vendor_available: true`", `
+ // with `private: true`.
+ testCcError(t, "`extends` refers module \".*\" which has `private: true`", `
cc_library {
name: "libvndk",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
}
@@ -2195,13 +2475,14 @@
}
`)
- testCcErrorProductVndk(t, "`extends` refers module \".*\" which does not have `vendor_available: true`", `
+ testCcErrorProductVndk(t, "`extends` refers module \".*\" which has `private: true`", `
cc_library {
name: "libvndk",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
}
@@ -2549,7 +2830,6 @@
cc_library {
name: "libvndk2",
vendor_available: true,
- product_available: true,
vndk: {
enabled: true,
},
@@ -2622,7 +2902,6 @@
cc_library {
name: "libvndk_sp2",
vendor_available: true,
- product_available: true,
vndk: {
enabled: true,
},
@@ -2676,6 +2955,20 @@
nocrt: true,
}
cc_library {
+ name: "libboth_available",
+ vendor_available: true,
+ product_available: true,
+ nocrt: true,
+ target: {
+ vendor: {
+ suffix: "-vendor",
+ },
+ product: {
+ suffix: "-product",
+ },
+ }
+ }
+ cc_library {
name: "libproduct_va",
product_specific: true,
vendor_available: true,
@@ -2689,6 +2982,7 @@
"libvndk",
"libvndk_sp",
"libpa",
+ "libboth_available",
"libproduct_va",
],
nocrt: true,
@@ -2701,6 +2995,7 @@
"libvndk",
"libvndk_sp",
"libva",
+ "libboth_available",
"libproduct_va",
],
nocrt: true,
@@ -2716,6 +3011,12 @@
checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
+
+ mod_vendor := ctx.ModuleForTests("libboth_available", vendorVariant).Module().(*Module)
+ assertString(t, mod_vendor.outputFile.Path().Base(), "libboth_available-vendor.so")
+
+ mod_product := ctx.ModuleForTests("libboth_available", productVariant).Module().(*Module)
+ assertString(t, mod_product.outputFile.Path().Base(), "libboth_available-product.so")
}
func TestEnforceProductVndkVersionErrors(t *testing.T) {
@@ -2748,7 +3049,22 @@
nocrt: true,
}
`)
- testCcErrorProductVndk(t, "Vendor module that is not VNDK should not link to \".*\" which is marked as `vendor_available: false`", `
+ testCcErrorProductVndk(t, "dependency \".*\" of \".*\" missing variant:\n.*image:product.VER", `
+ cc_library {
+ name: "libprod",
+ product_specific: true,
+ shared_libs: [
+ "libva",
+ ],
+ nocrt: true,
+ }
+ cc_library {
+ name: "libva",
+ vendor_available: true,
+ nocrt: true,
+ }
+ `)
+ testCcErrorProductVndk(t, "non-VNDK module should not link to \".*\" which has `private: true`", `
cc_library {
name: "libprod",
product_specific: true,
@@ -2759,10 +3075,11 @@
}
cc_library {
name: "libvndk_private",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
nocrt: true,
}
@@ -2820,10 +3137,11 @@
}
cc_library {
name: "libvndkprivate",
- vendor_available: false,
- product_available: false,
+ vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
+ private: true,
},
}
cc_library {
@@ -2868,9 +3186,27 @@
}
llndk_library {
name: "libllndkprivate.llndk",
- vendor_available: false,
+ private: true,
symbol_file: "",
- }`
+ }
+
+ llndk_libraries_txt {
+ name: "llndk.libraries.txt",
+ }
+ vndkcore_libraries_txt {
+ name: "vndkcore.libraries.txt",
+ }
+ vndksp_libraries_txt {
+ name: "vndksp.libraries.txt",
+ }
+ vndkprivate_libraries_txt {
+ name: "vndkprivate.libraries.txt",
+ }
+ vndkcorevariant_libraries_txt {
+ name: "vndkcorevariant.libraries.txt",
+ insert_vndk_version: false,
+ }
+ `
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -2878,14 +3214,14 @@
// native:vndk
ctx := testCcWithConfig(t, config)
- assertMapKeys(t, vndkCoreLibraries(config),
- []string{"libvndk", "libvndkprivate"})
- assertMapKeys(t, vndkSpLibraries(config),
- []string{"libc++", "libvndksp"})
- assertMapKeys(t, llndkLibraries(config),
- []string{"libc", "libdl", "libft2", "libllndk", "libllndkprivate", "libm"})
- assertMapKeys(t, vndkPrivateLibraries(config),
- []string{"libft2", "libllndkprivate", "libvndkprivate"})
+ checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt",
+ []string{"libvndk.so", "libvndkprivate.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt",
+ []string{"libc++.so", "libvndksp.so"})
+ checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt",
+ []string{"libc.so", "libdl.so", "libft2.so", "libllndk.so", "libllndkprivate.so", "libm.so"})
+ checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt",
+ []string{"libft2.so", "libllndkprivate.so", "libvndkprivate.so"})
vendorVariant27 := "android_vendor.27_arm64_armv8-a_shared"
@@ -2899,7 +3235,7 @@
{vendorVariant, "libvndkprivate", "native:vndk_private"},
{vendorVariant, "libvendor", "native:vendor"},
{vendorVariant, "libvndkext", "native:vendor"},
- {vendorVariant, "libllndk.llndk", "native:vndk"},
+ {vendorVariant, "libllndk", "native:vndk"},
{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vndk"},
{coreVariant, "libvndk", "native:platform"},
{coreVariant, "libvndkprivate", "native:platform"},
@@ -3178,8 +3514,39 @@
llndk_library {
name: "libllndk.llndk",
}
+
+ cc_prebuilt_library_shared {
+ name: "libllndkprebuilt",
+ stubs: { versions: ["1", "2"] },
+ llndk_stubs: "libllndkprebuilt.llndk",
+ }
+ llndk_library {
+ name: "libllndkprebuilt.llndk",
+ }
+
+ cc_library {
+ name: "libllndk_with_external_headers",
+ stubs: { versions: ["1", "2"] },
+ llndk_stubs: "libllndk_with_external_headers.llndk",
+ header_libs: ["libexternal_headers"],
+ export_header_lib_headers: ["libexternal_headers"],
+ }
+ llndk_library {
+ name: "libllndk_with_external_headers.llndk",
+ }
+ cc_library_headers {
+ name: "libexternal_headers",
+ export_include_dirs: ["include"],
+ vendor_available: true,
+ }
`)
- actual := ctx.ModuleVariantsForTests("libllndk.llndk")
+ actual := ctx.ModuleVariantsForTests("libllndk")
+ for i := 0; i < len(actual); i++ {
+ if !strings.HasPrefix(actual[i], "android_vendor.VER_") {
+ actual = append(actual[:i], actual[i+1:]...)
+ i--
+ }
+ }
expected := []string{
"android_vendor.VER_arm64_armv8-a_shared_1",
"android_vendor.VER_arm64_armv8-a_shared_2",
@@ -3190,10 +3557,10 @@
}
checkEquals(t, "variants for llndk stubs", expected, actual)
- params := ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
+ params := ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
checkEquals(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
- params = ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
+ params = ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
checkEquals(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
}
@@ -3244,8 +3611,17 @@
const runtimeLibAndroidBp = `
cc_library {
+ name: "liball_available",
+ vendor_available: true,
+ product_available: true,
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
name: "libvendor_available1",
vendor_available: true,
+ runtime_libs: ["liball_available"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3253,18 +3629,10 @@
cc_library {
name: "libvendor_available2",
vendor_available: true,
- runtime_libs: ["libvendor_available1"],
- no_libcrt : true,
- nocrt : true,
- system_shared_libs : [],
- }
- cc_library {
- name: "libvendor_available3",
- vendor_available: true,
- runtime_libs: ["libvendor_available1"],
+ runtime_libs: ["liball_available"],
target: {
vendor: {
- exclude_runtime_libs: ["libvendor_available1"],
+ exclude_runtime_libs: ["liball_available"],
}
},
no_libcrt : true,
@@ -3273,7 +3641,7 @@
}
cc_library {
name: "libcore",
- runtime_libs: ["libvendor_available1"],
+ runtime_libs: ["liball_available"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3288,7 +3656,30 @@
cc_library {
name: "libvendor2",
vendor: true,
- runtime_libs: ["libvendor_available1", "libvendor1"],
+ runtime_libs: ["liball_available", "libvendor1"],
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct_available1",
+ product_available: true,
+ runtime_libs: ["liball_available"],
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct1",
+ product_specific: true,
+ no_libcrt : true,
+ nocrt : true,
+ system_shared_libs : [],
+ }
+ cc_library {
+ name: "libproduct2",
+ product_specific: true,
+ runtime_libs: ["liball_available", "libproduct1"],
no_libcrt : true,
nocrt : true,
system_shared_libs : [],
@@ -3301,32 +3692,45 @@
// runtime_libs for core variants use the module names without suffixes.
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
+
+ module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
module = ctx.ModuleForTests("libcore", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
// runtime_libs for vendor variants have '.vendor' suffixes if the modules have both core
// and vendor variants.
variant = "android_vendor.VER_arm64_armv8-a_shared"
- module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1.vendor"}, module)
+ module = ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.vendor"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1.vendor", "libvendor1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available.vendor", "libvendor1"}, module)
+
+ // runtime_libs for product variants have '.product' suffixes if the modules have both core
+ // and product variants.
+ variant = "android_product.VER_arm64_armv8-a_shared"
+
+ module = ctx.ModuleForTests("libproduct_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.product"}, module)
+
+ module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available.product", "libproduct1"}, module)
}
func TestExcludeRuntimeLibs(t *testing.T) {
ctx := testCc(t, runtimeLibAndroidBp)
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
variant = "android_vendor.VER_arm64_armv8-a_shared"
- module = ctx.ModuleForTests("libvendor_available3", variant).Module().(*Module)
+ module = ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
checkRuntimeLibs(t, nil, module)
}
@@ -3337,11 +3741,14 @@
variant := "android_arm64_armv8-a_shared"
- module := ctx.ModuleForTests("libvendor_available2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1"}, module)
+ module := ctx.ModuleForTests("libvendor_available1", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available"}, module)
module = ctx.ModuleForTests("libvendor2", variant).Module().(*Module)
- checkRuntimeLibs(t, []string{"libvendor_available1", "libvendor1"}, module)
+ checkRuntimeLibs(t, []string{"liball_available", "libvendor1"}, module)
+
+ module = ctx.ModuleForTests("libproduct2", variant).Module().(*Module)
+ checkRuntimeLibs(t, []string{"liball_available", "libproduct1"}, module)
}
func checkStaticLibs(t *testing.T, expected []string, module *Module) {
@@ -4056,3 +4463,360 @@
}
}
+
+func TestStubsLibReexportsHeaders(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library_shared {
+ name: "libclient",
+ srcs: ["foo.c"],
+ shared_libs: ["libfoo#1"],
+ }
+
+ cc_library_shared {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ shared_libs: ["libbar"],
+ export_shared_lib_headers: ["libbar"],
+ stubs: {
+ symbol_file: "foo.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ }
+
+ cc_library_shared {
+ name: "libbar",
+ export_include_dirs: ["include/libbar"],
+ srcs: ["foo.c"],
+ }`)
+
+ cFlags := ctx.ModuleForTests("libclient", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+
+ if !strings.Contains(cFlags, "-Iinclude/libbar") {
+ t.Errorf("expected %q in cflags, got %q", "-Iinclude/libbar", cFlags)
+ }
+}
+
+func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
+ ctx := testCc(t, `
+ cc_library {
+ name: "libfoo",
+ srcs: ["a/Foo.aidl"],
+ aidl: { flags: ["-Werror"], },
+ }
+ `)
+
+ libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+ manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl.sbox.textproto"))
+ aidlCommand := manifest.Commands[0].GetCommand()
+ expectedAidlFlag := "-Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
+type MemtagNoteType int
+
+const (
+ None MemtagNoteType = iota + 1
+ Sync
+ Async
+)
+
+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 checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+ note_async := "note_memtag_heap_async"
+ note_sync := "note_memtag_heap_sync"
+
+ 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
+ }
+ }
+
+ if found != expected {
+ t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+ }
+}
+
+func makeMemtagTestConfig(t *testing.T) android.Config {
+ templateBp := `
+ cc_test {
+ name: "%[1]s_test",
+ gtest: false,
+ }
+
+ cc_test {
+ name: "%[1]s_test_false",
+ gtest: false,
+ sanitize: { memtag_heap: false },
+ }
+
+ cc_test {
+ 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 } },
+ }
+
+ cc_binary {
+ 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 } },
+ }
+ `
+ 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_default/Android.bp": []byte(subdirDefaultBp),
+ "subdir_exclude/Android.bp": []byte(subdirExcludeBp),
+ "subdir_sync/Android.bp": []byte(subdirSyncBp),
+ "subdir_async/Android.bp": []byte(subdirAsyncBp),
+ }
+
+ 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_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"
+
+ 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()
+
+ _, 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), 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/check.go b/cc/check.go
index 0058b8c..a357a97 100644
--- a/cc/check.go
+++ b/cc/check.go
@@ -104,7 +104,7 @@
// Check for bad host_ldlibs
func CheckBadHostLdlibs(ctx ModuleContext, prop string, flags []string) {
- allowed_ldlibs := ctx.toolchain().AvailableLibraries()
+ allowedLdlibs := ctx.toolchain().AvailableLibraries()
if !ctx.Host() {
panic("Invalid call to CheckBadHostLdlibs")
@@ -116,7 +116,7 @@
// TODO: Probably should just redo this property to prefix -l in Soong
if !strings.HasPrefix(flag, "-l") && !strings.HasPrefix(flag, "-framework") {
ctx.PropertyErrorf(prop, "Invalid flag: `%s`, must start with `-l` or `-framework`", flag)
- } else if !inList(flag, allowed_ldlibs) {
+ } else if !inList(flag, allowedLdlibs) {
ctx.PropertyErrorf(prop, "Host library `%s` not available", flag)
}
}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index f7d9081..d441c57 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -125,15 +125,15 @@
}
// Only write CMakeLists.txt for the first variant of each architecture of each module
- clionproject_location := getCMakeListsForModule(ccModule, ctx)
- if seenProjects[clionproject_location] {
+ clionprojectLocation := getCMakeListsForModule(ccModule, ctx)
+ if seenProjects[clionprojectLocation] {
return
}
- seenProjects[clionproject_location] = true
+ seenProjects[clionprojectLocation] = true
// Ensure the directory hosting the cmakelists.txt exists
- projectDir := path.Dir(clionproject_location)
+ projectDir := path.Dir(clionprojectLocation)
os.MkdirAll(projectDir, os.ModePerm)
// Create cmakelists.txt
diff --git a/cc/compiler.go b/cc/compiler.go
index b78bb6c..5f30d3d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -123,6 +123,9 @@
// whether to generate traces (for systrace) for this interface
Generate_traces *bool
+
+ // list of flags that will be passed to the AIDL compiler
+ Flags []string
}
Renderscript struct {
@@ -401,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 {
@@ -479,8 +487,7 @@
}
if ctx.inProduct() {
- // TODO(b/150902910): must use 'compiler.Properties.Target.Product.Cflags'
- flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
+ flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Product.Cflags)...)
}
if ctx.inRecovery() {
@@ -522,6 +529,7 @@
}
if compiler.hasSrcExt(".aidl") {
+ flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(localAidlIncludeDirs))
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 441bff2..35dd10f 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -94,6 +94,7 @@
// `modernize-*`.
var ClangTidyDisableChecks = []string{
"misc-no-recursion",
+ "readability-function-cognitive-complexity", // http://b/175055536
}
func init() {
@@ -140,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{
@@ -190,6 +198,8 @@
"-Wno-pessimizing-move", // http://b/154270751
// New warnings to be fixed after clang-r399163
"-Wno-non-c-typedef-for-linkage", // http://b/161304145
+ // New warnings to be fixed after clang-r407598
+ "-Wno-string-concatenation", // http://b/175068488
}, " "))
// Extra cflags for external third-party projects to disable warnings that
@@ -249,6 +259,10 @@
return result
}
+func ClangLibToolingFilterUnknownCflags(libToolingFlags []string) []string {
+ return android.RemoveListFromList(libToolingFlags, ClangLibToolingUnknownCflags)
+}
+
func inListSorted(s string, list []string) bool {
for _, l := range list {
if s == l {
diff --git a/cc/config/global.go b/cc/config/global.go
index e5cb7ee..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
@@ -135,8 +144,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r399163b"
- ClangDefaultShortVersion = "11.0.5"
+ ClangDefaultVersion = "clang-r407598"
+ ClangDefaultShortVersion = "12.0.1"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
@@ -215,10 +224,6 @@
"frameworks/native/opengl/include",
"frameworks/av/include",
})
- // This is used by non-NDK modules to get jni.h. export_include_dirs doesn't help
- // with this, since there is no associated library.
- pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I",
- []string{"libnativehelper/include_jni"})
pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index 4ac9e58..d5d01b4 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -20,25 +20,54 @@
)
func init() {
- // Most Android source files are not clang-tidy clean yet.
- // Global tidy checks include only google*, performance*,
- // and misc-macro-parentheses, but not google-readability*
- // or google-runtime-references.
+ // Many clang-tidy checks like altera-*, llvm-*, modernize-*
+ // are not designed for Android source code or creating too
+ // many (false-positive) warnings. The global default tidy checks
+ // should include only tested groups and exclude known noisy checks.
+ // See https://clang.llvm.org/extra/clang-tidy/checks/list.html
pctx.VariableFunc("TidyDefaultGlobalChecks", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("DEFAULT_GLOBAL_TIDY_CHECKS"); override != "" {
return override
}
- return strings.Join([]string{
+ checks := strings.Join([]string{
"-*",
- "bugprone*",
+ "abseil-*",
+ "android-*",
+ "bugprone-*",
+ "cert-*",
"clang-diagnostic-unused-command-line-argument",
- "google*",
- "misc-macro-parentheses",
- "performance*",
+ "google-*",
+ "misc-*",
+ "performance-*",
+ "portability-*",
"-bugprone-narrowing-conversions",
"-google-readability*",
"-google-runtime-references",
+ "-misc-no-recursion",
+ "-misc-non-private-member-variables-in-classes",
+ "-misc-unused-parameters",
+ // the following groups are excluded by -*
+ // -altera-*
+ // -cppcoreguidelines-*
+ // -darwin-*
+ // -fuchsia-*
+ // -hicpp-*
+ // -llvm-*
+ // -llvmlibc-*
+ // -modernize-*
+ // -mpi-*
+ // -objc-*
+ // -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 563ce76..107ae7d 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -18,15 +18,24 @@
// For these libraries, the vendor variants must be installed even if the device
// has VndkUseCoreVariant set.
var VndkMustUseVendorVariantList = []string{
+ "android.hardware.authsecret-unstable-ndk_platform",
"android.hardware.automotive.occupant_awareness-ndk_platform",
+ "android.hardware.health.storage-ndk_platform",
+ "android.hardware.health.storage-unstable-ndk_platform",
"android.hardware.light-ndk_platform",
"android.hardware.identity-ndk_platform",
- "android.hardware.keymint-ndk_platform",
- "android.hardware.keymint-unstable-ndk_platform",
"android.hardware.nfc@1.2",
+ "android.hardware.memtrack-unstable-ndk_platform",
+ "android.hardware.oemlock-unstable-ndk_platform",
"android.hardware.power-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
+ "android.hardware.security.keymint-unstable-ndk_platform",
+ "android.hardware.security.secureclock-ndk_platform",
+ "android.hardware.security.secureclock-unstable-ndk_platform",
+ "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 aa1fdf6..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"
}
@@ -149,6 +151,7 @@
coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
}
}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 6b17c48..d7da5ab 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -41,6 +41,12 @@
// Specify who should be acknowledged for CVEs in the Android Security
// Bulletin.
Acknowledgement []string `json:"acknowledgement,omitempty"`
+ // Additional options to be passed to libfuzzer when run in Haiku.
+ Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on-device in Haiku.
+ Hwasan_options []string `json:"hwasan_options,omitempty"`
+ // Additional options to be passed to HWASAN when running on host in Haiku.
+ Asan_options []string `json:"asan_options,omitempty"`
}
func (f *FuzzConfig) String() string {
@@ -315,7 +321,7 @@
module, binary := NewBinary(hod)
binary.baseInstaller = NewFuzzInstaller()
- module.sanitize.SetSanitizer(fuzzer, true)
+ module.sanitize.SetSanitizer(Fuzzer, true)
fuzz := &fuzzBinary{
binaryDecorator: binary,
diff --git a/cc/genrule.go b/cc/genrule.go
index 3668e2b..1ce2169 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -75,7 +75,15 @@
}
func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
- return Bool(g.Recovery_available)
+ // If the build is using a snapshot, the recovery variant under AOSP directories
+ // is not needed.
+ recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
+ if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
+ !isRecoveryProprietaryModule(ctx) {
+ return false
+ } else {
+ return Bool(g.Recovery_available)
+ }
}
func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
@@ -101,8 +109,7 @@
return variants
}
- // TODO(b/150902910): vendor_available will not create product variant. Remove Bool(g.Vendor_available)
- if Bool(g.Vendor_available) || Bool(g.Product_available) || ctx.ProductSpecific() {
+ if Bool(g.Product_available) || ctx.ProductSpecific() {
variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
if vndkVersion := ctx.DeviceConfig().ProductVndkVersion(); vndkVersion != "current" {
variants = append(variants, ProductVariationPrefix+vndkVersion)
diff --git a/cc/image.go b/cc/image.go
index 3d6769e..f89194f 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -17,6 +17,8 @@
// functions to determine where a module is installed, etc.
import (
+ "fmt"
+ "reflect"
"strings"
"android/soong/android"
@@ -24,36 +26,18 @@
var _ android.ImageInterface = (*Module)(nil)
-type imageVariantType string
+type ImageVariantType string
const (
- coreImageVariant imageVariantType = "core"
- vendorImageVariant imageVariantType = "vendor"
- productImageVariant imageVariantType = "product"
- ramdiskImageVariant imageVariantType = "ramdisk"
- vendorRamdiskImageVariant imageVariantType = "vendor_ramdisk"
- recoveryImageVariant imageVariantType = "recovery"
- hostImageVariant imageVariantType = "host"
+ coreImageVariant ImageVariantType = "core"
+ vendorImageVariant ImageVariantType = "vendor"
+ productImageVariant ImageVariantType = "product"
+ ramdiskImageVariant ImageVariantType = "ramdisk"
+ vendorRamdiskImageVariant ImageVariantType = "vendor_ramdisk"
+ recoveryImageVariant ImageVariantType = "recovery"
+ hostImageVariant ImageVariantType = "host"
)
-func (c *Module) getImageVariantType() imageVariantType {
- if c.Host() {
- return hostImageVariant
- } else if c.inVendor() {
- return vendorImageVariant
- } else if c.inProduct() {
- return productImageVariant
- } else if c.InRamdisk() {
- return ramdiskImageVariant
- } else if c.InVendorRamdisk() {
- return vendorRamdiskImageVariant
- } else if c.InRecovery() {
- return recoveryImageVariant
- } else {
- return coreImageVariant
- }
-}
-
const (
// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
// against the VNDK.
@@ -65,22 +49,23 @@
)
func (ctx *moduleContext) ProductSpecific() bool {
- //TODO(b/150902910): Replace HasNonSystemVariants() with HasProductVariant()
- return ctx.ModuleContext.ProductSpecific() ||
- (ctx.mod.HasNonSystemVariants() && ctx.mod.inProduct())
+ // Additionally check if this module is inProduct() that means it is a "product" variant of a
+ // module. As well as product specific modules, product variants must be installed to /product.
+ return ctx.ModuleContext.ProductSpecific() || ctx.mod.InProduct()
}
func (ctx *moduleContext) SocSpecific() bool {
- return ctx.ModuleContext.SocSpecific() ||
- (ctx.mod.HasVendorVariant() && ctx.mod.inVendor())
+ // Additionally check if this module is inVendor() that means it is a "vendor" variant of a
+ // module. As well as SoC specific modules, vendor variants must be installed to /vendor.
+ return ctx.ModuleContext.SocSpecific() || ctx.mod.InVendor()
}
func (ctx *moduleContextImpl) inProduct() bool {
- return ctx.mod.inProduct()
+ return ctx.mod.InProduct()
}
func (ctx *moduleContextImpl) inVendor() bool {
- return ctx.mod.inVendor()
+ return ctx.mod.InVendor()
}
func (ctx *moduleContextImpl) inRamdisk() bool {
@@ -97,26 +82,26 @@
// Returns true when this module is configured to have core and vendor variants.
func (c *Module) HasVendorVariant() bool {
- return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
+ return Bool(c.VendorProperties.Vendor_available)
}
// Returns true when this module is configured to have core and product variants.
func (c *Module) HasProductVariant() bool {
- return c.IsVndk() || Bool(c.VendorProperties.Product_available)
+ return Bool(c.VendorProperties.Product_available)
}
// Returns true when this module is configured to have core and either product or vendor variants.
func (c *Module) HasNonSystemVariants() bool {
- return c.IsVndk() || Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Product_available)
+ return c.HasVendorVariant() || c.HasProductVariant()
}
// Returns true if the module is "product" variant. Usually these modules are installed in /product
-func (c *Module) inProduct() bool {
+func (c *Module) InProduct() bool {
return c.Properties.ImageVariationPrefix == ProductVariationPrefix
}
// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
+func (c *Module) InVendor() bool {
return c.Properties.ImageVariationPrefix == VendorVariationPrefix
}
@@ -144,27 +129,65 @@
return c.ModuleBase.InstallInRecovery()
}
+func visitPropsAndCompareVendorAndProductProps(v reflect.Value) bool {
+ if v.Kind() != reflect.Struct {
+ return true
+ }
+ for i := 0; i < v.NumField(); i++ {
+ prop := v.Field(i)
+ if prop.Kind() == reflect.Struct && v.Type().Field(i).Name == "Target" {
+ vendor_prop := prop.FieldByName("Vendor")
+ product_prop := prop.FieldByName("Product")
+ if vendor_prop.Kind() != reflect.Struct && product_prop.Kind() != reflect.Struct {
+ // Neither Target.Vendor nor Target.Product is defined
+ continue
+ }
+ if vendor_prop.Kind() != reflect.Struct || product_prop.Kind() != reflect.Struct ||
+ !reflect.DeepEqual(vendor_prop.Interface(), product_prop.Interface()) {
+ // If only one of either Target.Vendor or Target.Product is
+ // defined or they have different values, it fails the build
+ // since VNDK must have the same properties for both vendor
+ // and product variants.
+ return false
+ }
+ } else if !visitPropsAndCompareVendorAndProductProps(prop) {
+ // Visit the substructures to find Target.Vendor and Target.Product
+ return false
+ }
+ }
+ return true
+}
+
+// In the case of VNDK, vendor and product variants must have the same properties.
+// VNDK installs only one file and shares it for both vendor and product modules on
+// runtime. We may not define different versions of a VNDK lib for each partition.
+// This function is used only for the VNDK modules that is available to both vendor
+// and product partitions.
+func (c *Module) compareVendorAndProductProps() bool {
+ if !c.IsVndk() && !Bool(c.VendorProperties.Product_available) {
+ panic(fmt.Errorf("This is only for product available VNDK libs. %q is not a VNDK library or not product available", c.Name()))
+ }
+ for _, properties := range c.GetProperties() {
+ if !visitPropsAndCompareVendorAndProductProps(reflect.ValueOf(properties).Elem()) {
+ return false
+ }
+ }
+ return true
+}
+
func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
// Validation check
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
productSpecific := mctx.ProductSpecific()
- if m.VendorProperties.Vendor_available != nil {
+ if Bool(m.VendorProperties.Vendor_available) {
if vendorSpecific {
mctx.PropertyErrorf("vendor_available",
"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
}
- // If defined, make sure vendor_available and product_available has the
- // same value since `false` for these properties means the module is
- // for system only but provides the variant.
- if m.VendorProperties.Product_available != nil {
- if Bool(m.VendorProperties.Vendor_available) != Bool(m.VendorProperties.Product_available) {
- mctx.PropertyErrorf("product_available", "may not have different value than `vendor_available`")
- }
- }
}
- if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Product_available) {
if productSpecific {
mctx.PropertyErrorf("product_available",
"doesn't make sense at the same time as `product_specific: true`")
@@ -181,10 +204,10 @@
if !vndkdep.isVndkExt() {
mctx.PropertyErrorf("vndk",
"must set `extends: \"...\"` to vndk extension")
- } else if m.VendorProperties.Vendor_available != nil {
+ } else if Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vendor_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
- } else if m.VendorProperties.Product_available != nil {
+ } else if Bool(m.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
}
@@ -194,9 +217,17 @@
"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
m.getVndkExtendsModuleName())
}
- if m.VendorProperties.Vendor_available == nil {
+ if !Bool(m.VendorProperties.Vendor_available) {
mctx.PropertyErrorf("vndk",
- "vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+ "vendor_available must be set to true when `vndk: {enabled: true}`")
+ }
+ if Bool(m.VendorProperties.Product_available) {
+ // If a VNDK module creates both product and vendor variants, they
+ // must have the same properties since they share a single VNDK
+ // library on runtime.
+ if !m.compareVendorAndProductProps() {
+ mctx.ModuleErrorf("product properties must have the same values with the vendor properties for VNDK modules")
+ }
}
}
} else {
@@ -223,6 +254,9 @@
platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
boardVndkVersion := mctx.DeviceConfig().VndkVersion()
productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+ recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion()
+ usingRecoverySnapshot := recoverySnapshotVersion != "current" &&
+ recoverySnapshotVersion != ""
if boardVndkVersion == "current" {
boardVndkVersion = platformVndkVersion
}
@@ -230,42 +264,49 @@
productVndkVersion = platformVndkVersion
}
- if boardVndkVersion == "" {
+ _, isLLNDKLibrary := m.linker.(*llndkStubDecorator)
+ _, isLLNDKHeaders := m.linker.(*llndkHeadersDecorator)
+ lib := moduleLibraryInterface(m)
+ hasLLNDKStubs := lib != nil && lib.hasLLNDKStubs()
+
+ if isLLNDKLibrary || isLLNDKHeaders || hasLLNDKStubs {
+ // This is an LLNDK library. The implementation of the library will be on /system,
+ // and vendor and product variants will be created with LLNDK stubs.
+ // The LLNDK libraries need vendor variants even if there is no VNDK.
+ // The obsolete llndk_library and llndk_headers modules also need the vendor variants
+ // so the cc_library LLNDK stubs can depend on them.
+ if hasLLNDKStubs {
+ coreVariantNeeded = true
+ }
+ if platformVndkVersion != "" {
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ productVariants = append(productVariants, platformVndkVersion)
+ }
+ if boardVndkVersion != "" {
+ vendorVariants = append(vendorVariants, boardVndkVersion)
+ }
+ if productVndkVersion != "" {
+ productVariants = append(productVariants, productVndkVersion)
+ }
+ } else if boardVndkVersion == "" {
// If the device isn't compiling against the VNDK, we always
// use the core mode.
coreVariantNeeded = true
- } else if _, ok := m.linker.(*llndkStubDecorator); ok {
- // LL-NDK stubs only exist in the vendor and product variants,
- // since the real libraries will be used in the core variant.
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
- } else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
- // ... and LL-NDK headers as well
- vendorVariants = append(vendorVariants,
- platformVndkVersion,
- boardVndkVersion,
- )
- productVariants = append(productVariants,
- platformVndkVersion,
- productVndkVersion,
- )
} else if m.isSnapshotPrebuilt() {
// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
// PRODUCT_EXTRA_VNDK_VERSIONS.
if snapshot, ok := m.linker.(interface {
version() string
}); ok {
- vendorVariants = append(vendorVariants, snapshot.version())
+ if m.InstallInRecovery() {
+ recoveryVariantNeeded = true
+ } else {
+ vendorVariants = append(vendorVariants, snapshot.version())
+ }
} else {
mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
}
- } else if m.HasNonSystemVariants() && !m.isVndkExt() {
+ } else if m.HasNonSystemVariants() && !m.IsVndkExt() {
// This will be available to /system unless it is product_specific
// which will be handled later.
coreVariantNeeded = true
@@ -281,13 +322,13 @@
}
}
- // vendor_available modules are also available to /product.
- // TODO(b/150902910): product variant will be created only if
- // m.HasProductVariant() is true.
- productVariants = append(productVariants, platformVndkVersion)
- // VNDK is always PLATFORM_VNDK_VERSION
- if !m.IsVndk() {
- productVariants = append(productVariants, productVndkVersion)
+ // product_available modules are available to /product.
+ if m.HasProductVariant() {
+ productVariants = append(productVariants, platformVndkVersion)
+ // VNDK is always PLATFORM_VNDK_VERSION
+ if !m.IsVndk() {
+ productVariants = append(productVariants, productVndkVersion)
+ }
}
} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
// This will be available in /vendor (or /odm) only
@@ -355,6 +396,15 @@
coreVariantNeeded = false
}
+ // If using a snapshot, the recovery variant under AOSP directories is not needed,
+ // except for kernel headers, which needs all variants.
+ if _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
+ !m.isSnapshotPrebuilt() &&
+ usingRecoverySnapshot &&
+ !isRecoveryProprietaryModule(mctx) {
+ recoveryVariantNeeded = false
+ }
+
for _, variant := range android.FirstUniqueStrings(vendorVariants) {
m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
}
@@ -367,6 +417,14 @@
m.Properties.VendorRamdiskVariantNeeded = vendorRamdiskVariantNeeded
m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
m.Properties.CoreVariantNeeded = coreVariantNeeded
+
+ // Disable the module if no variants are needed.
+ if !ramdiskVariantNeeded &&
+ !recoveryVariantNeeded &&
+ !coreVariantNeeded &&
+ len(m.Properties.ExtraVariants) == 0 {
+ m.Disable()
+ }
}
func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -454,12 +512,11 @@
vndkVersion := ctx.DeviceConfig().VndkVersion()
if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
m.Properties.HideFromMake = true
- m.SkipInstall()
+ m.HideFromMake()
}
} else if strings.HasPrefix(variant, ProductVariationPrefix) {
m.Properties.ImageVariationPrefix = ProductVariationPrefix
m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
- // TODO (b/150902910): This will be replaced with squashProductSrcs(m).
- squashVendorSrcs(m)
+ squashProductSrcs(m)
}
}
diff --git a/cc/library.go b/cc/library.go
index c626b03..af9aaca 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -22,12 +22,14 @@
"strings"
"sync"
+ "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"android/soong/android"
"android/soong/cc/config"
)
+// LibraryProperties is a collection of properties shared by cc library rules.
type LibraryProperties struct {
// local file name to pass to the linker as -unexported_symbols_list
Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -113,16 +115,29 @@
// If this is an LLNDK library, the name of the equivalent llndk_library module.
Llndk_stubs *string
+
+ // If this is an LLNDK library, properties to describe the LLNDK stubs. Will be copied from
+ // the module pointed to by llndk_stubs if it is set.
+ Llndk llndkLibraryProperties
}
+// StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
+// library module.
type StaticProperties struct {
Static StaticOrSharedProperties `android:"arch_variant"`
}
+// SharedProperties is a properties stanza to affect only attributes of the "shared" variants of a
+// library module.
type SharedProperties struct {
Shared StaticOrSharedProperties `android:"arch_variant"`
}
+// StaticOrSharedProperties is an embedded struct representing properties to affect attributes of
+// either only the "static" variants or only the "shared" variants of a library module. These override
+// the base properties of the same name.
+// Use `StaticProperties` or `SharedProperties`, depending on which variant is needed.
+// `StaticOrSharedProperties` exists only to avoid duplication.
type StaticOrSharedProperties struct {
Srcs []string `android:"path,arch_variant"`
@@ -242,44 +257,62 @@
return module.Init()
}
+// flagExporter is a separated portion of libraryDecorator pertaining to exported
+// include paths and flags. Keeping this dependency-related information separate
+// from the rest of library information is helpful in keeping data more structured
+// and explicit.
type flagExporter struct {
Properties FlagExporterProperties
- dirs android.Paths
- systemDirs android.Paths
- flags []string
+ dirs android.Paths // Include directories to be included with -I
+ systemDirs android.Paths // System include directories to be included with -isystem
+ flags []string // Exported raw flags.
deps android.Paths
headers android.Paths
}
+// exportedIncludes returns the effective include paths for this module and
+// any module that links against this module. This is obtained from
+// the export_include_dirs property in the appropriate target stanza.
func (f *flagExporter) exportedIncludes(ctx ModuleContext) android.Paths {
- // TODO(b/150902910): product variant must use Target.Product
- if ctx.useVndk() && f.Properties.Target.Vendor.Override_export_include_dirs != nil {
+ if ctx.inVendor() && f.Properties.Target.Vendor.Override_export_include_dirs != nil {
return android.PathsForModuleSrc(ctx, f.Properties.Target.Vendor.Override_export_include_dirs)
- } else {
- return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
}
+ if ctx.inProduct() && f.Properties.Target.Product.Override_export_include_dirs != nil {
+ return android.PathsForModuleSrc(ctx, f.Properties.Target.Product.Override_export_include_dirs)
+ }
+ return android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
}
+// exportIncludes registers the include directories and system include directories to be exported
+// transitively to modules depending on this module.
func (f *flagExporter) exportIncludes(ctx ModuleContext) {
f.dirs = append(f.dirs, f.exportedIncludes(ctx)...)
f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
}
+// exportIncludesAsSystem registers the include directories and system include directories to be
+// exported transitively both as system include directories to modules depending on this module.
func (f *flagExporter) exportIncludesAsSystem(ctx ModuleContext) {
// all dirs are force exported as system
f.systemDirs = append(f.systemDirs, f.exportedIncludes(ctx)...)
f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
}
+// reexportDirs registers the given directories as include directories to be exported transitively
+// to modules depending on this module.
func (f *flagExporter) reexportDirs(dirs ...android.Path) {
f.dirs = append(f.dirs, dirs...)
}
+// reexportSystemDirs registers the given directories as system include directories
+// to be exported transitively to modules depending on this module.
func (f *flagExporter) reexportSystemDirs(dirs ...android.Path) {
f.systemDirs = append(f.systemDirs, dirs...)
}
+// reexportFlags registers the flags to be exported transitively to modules depending on this
+// module.
func (f *flagExporter) reexportFlags(flags ...string) {
if android.PrefixInList(flags, "-I") || android.PrefixInList(flags, "-isystem") {
panic(fmt.Errorf("Exporting invalid flag %q: "+
@@ -355,7 +388,7 @@
versionScriptPath android.OptionalPath
- post_install_cmds []string
+ postInstallCmds []string
// If useCoreVariant is true, the vendor variant of a VNDK library is
// not installed.
@@ -457,6 +490,8 @@
return l.collectedSnapshotHeaders
}
+// linkerProps returns the list of properties structs relevant for this library. (For example, if
+// the library is cc_shared_library, then static-library properties are omitted.)
func (library *libraryDecorator) linkerProps() []interface{} {
var props []interface{}
props = append(props, library.baseLinker.linkerProps()...)
@@ -476,6 +511,9 @@
return props
}
+// linkerFlags takes a Flags struct and augments it to contain linker flags that are defined by this
+// library, or that are implied by attributes of this library (such as whether this library is a
+// shared library).
func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
flags = library.baseLinker.linkerFlags(ctx, flags)
@@ -526,6 +564,9 @@
return flags
}
+// compilerFlags takes a Flags and augments it to contain compile flags from global values,
+// per-target values, module type values, per-module Blueprints properties, extra flags from
+// `flags`, and generated sources from `deps`.
func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
if len(exportIncludeDirs) > 0 {
@@ -535,6 +576,12 @@
}
flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
+ if ctx.IsLlndk() {
+ // LLNDK libraries ignore most of the properties on the cc_library and use the
+ // LLNDK-specific properties instead.
+ // Wipe all the module-local properties, leaving only the global properties.
+ flags.Local = LocalOrGlobalFlags{}
+ }
if library.buildStubs() {
// Remove -include <file> when compiling stubs. Otherwise, the force included
// headers might cause conflicting types error with the symbols in the
@@ -559,62 +606,31 @@
return flags
}
-// Returns a string that represents the class of the ABI dump.
-// Returns an empty string if ABI check is disabled for this library.
-func (library *libraryDecorator) classifySourceAbiDump(ctx ModuleContext) string {
- enabled := library.Properties.Header_abi_checker.Enabled
- if enabled != nil && !Bool(enabled) {
- return ""
- }
- // Return NDK if the library is both NDK and LLNDK.
- if ctx.isNdk(ctx.Config()) {
- return "NDK"
- }
- if ctx.isLlndkPublic(ctx.Config()) {
- return "LLNDK"
- }
- if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(ctx.Config()) {
- if ctx.isVndkSp() {
- if ctx.isVndkExt() {
- return "VNDK-SP-ext"
- } else {
- return "VNDK-SP"
- }
- } else {
- if ctx.isVndkExt() {
- return "VNDK-ext"
- } else {
- return "VNDK-core"
- }
- }
- }
- if Bool(enabled) || library.hasStubsVariants() {
- return "PLATFORM"
- }
- return ""
+func (library *libraryDecorator) headerAbiCheckerEnabled() bool {
+ return Bool(library.Properties.Header_abi_checker.Enabled)
}
-func (library *libraryDecorator) shouldCreateSourceAbiDump(ctx ModuleContext) bool {
- if !ctx.shouldCreateSourceAbiDump() {
- return false
- }
- if !ctx.isForPlatform() {
- if !library.hasStubsVariants() {
- // Skip ABI checks if this library is for APEX but isn't exported.
- return false
- }
- if !Bool(library.Properties.Header_abi_checker.Enabled) {
- // Skip ABI checks if this library is for APEX and did not explicitly enable
- // ABI checks.
- // TODO(b/145608479): ABI checks should be enabled by default. Remove this
- // after evaluating the extra build time.
- return false
- }
- }
- return library.classifySourceAbiDump(ctx) != ""
+func (library *libraryDecorator) headerAbiCheckerExplicitlyDisabled() bool {
+ return !BoolDefault(library.Properties.Header_abi_checker.Enabled, true)
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+ if ctx.IsLlndk() {
+ // This is the vendor variant of an LLNDK library, build the LLNDK stubs.
+ vndkVer := ctx.Module().(*Module).VndkVersion()
+ if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
+ // For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
+ vndkVer = "current"
+ }
+ if library.stubsVersion() != "" {
+ vndkVer = library.stubsVersion()
+ }
+ objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
+ if !Bool(library.Properties.Llndk.Unversioned) {
+ library.versionScriptPath = android.OptionalPathForPath(versionScript)
+ }
+ return objs
+ }
if library.buildStubs() {
objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
library.versionScriptPath = android.OptionalPathForPath(versionScript)
@@ -633,7 +649,7 @@
}
return Objects{}
}
- if library.shouldCreateSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
+ if library.sabi.shouldCreateSourceAbiDump() {
exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
var SourceAbiFlags []string
for _, dir := range exportIncludeDirs.Strings() {
@@ -643,9 +659,9 @@
SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
}
flags.SAbiFlags = SourceAbiFlags
- total_length := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
+ totalLength := len(library.baseCompiler.Properties.Srcs) + len(deps.GeneratedSources) +
len(library.SharedProperties.Shared.Srcs) + len(library.StaticProperties.Static.Srcs)
- if total_length > 0 {
+ if totalLength > 0 {
flags.SAbiDump = true
}
}
@@ -683,6 +699,10 @@
setStatic()
setShared()
+ // Check whether header_abi_checker is enabled or explicitly disabled.
+ headerAbiCheckerEnabled() bool
+ headerAbiCheckerExplicitlyDisabled() bool
+
// Write LOCAL_ADDITIONAL_DEPENDENCIES for ABI diff
androidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer)
@@ -701,12 +721,13 @@
allStubsVersions() []string
implementationModuleName(name string) string
+ hasLLNDKStubs() bool
}
var _ libraryInterface = (*libraryDecorator)(nil)
var _ versionedInterface = (*libraryDecorator)(nil)
-func (library *libraryDecorator) getLibNameHelper(baseModuleName string, useVndk bool) string {
+func (library *libraryDecorator) getLibNameHelper(baseModuleName string, inVendor bool, inProduct bool) string {
name := library.libName
if name == "" {
name = String(library.Properties.Stem)
@@ -716,9 +737,10 @@
}
suffix := ""
- if useVndk {
- // TODO(b/150902910): product variant must use Target.Product
+ if inVendor {
suffix = String(library.Properties.Target.Vendor.Suffix)
+ } else if inProduct {
+ suffix = String(library.Properties.Target.Product.Suffix)
}
if suffix == "" {
suffix = String(library.Properties.Suffix)
@@ -727,10 +749,12 @@
return name + suffix
}
+// getLibName returns the actual canonical name of the library (the name which
+// should be passed to the linker via linker flags).
func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
- name := library.getLibNameHelper(ctx.baseModuleName(), ctx.useVndk())
+ name := library.getLibNameHelper(ctx.baseModuleName(), ctx.inVendor(), ctx.inProduct())
- if ctx.isVndkExt() {
+ if ctx.IsVndkExt() {
// vndk-ext lib should have the same name with original lib
ctx.VisitDirectDepsWithTag(vndkExtDepTag, func(module android.Module) {
originalName := module.(*Module).outputFile.Path()
@@ -774,12 +798,27 @@
}
func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ if ctx.IsLlndk() {
+ // LLNDK libraries ignore most of the properties on the cc_library and use the
+ // LLNDK-specific properties instead.
+ return deps
+ }
+
deps = library.baseCompiler.compilerDeps(ctx, deps)
return deps
}
func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ if ctx.IsLlndk() {
+ // LLNDK libraries ignore most of the properties on the cc_library and use the
+ // LLNDK-specific properties instead.
+ deps.HeaderLibs = append(deps.HeaderLibs, library.Properties.Llndk.Export_llndk_headers...)
+ deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+ library.Properties.Llndk.Export_llndk_headers...)
+ return deps
+ }
+
if library.static() {
// Compare with nil because an empty list needs to be propagated.
if library.StaticProperties.Static.System_shared_libs != nil {
@@ -814,14 +853,20 @@
deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
}
- // TODO(b/150902910): product variant must use Target.Product
- if ctx.useVndk() {
+ if ctx.inVendor() {
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
}
+ if ctx.inProduct() {
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Product.Exclude_static_libs)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Product.Exclude_shared_libs)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Product.Exclude_static_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, library.baseLinker.Properties.Target.Product.Exclude_shared_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, library.baseLinker.Properties.Target.Product.Exclude_static_libs)
+ }
if ctx.inRecovery() {
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
@@ -903,16 +948,22 @@
ctx.CheckbuildFile(outputFile)
- ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: outputFile,
- ReuseObjects: library.reuseObjects,
- Objects: library.objects,
+ if library.static() {
+ ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+ StaticLibrary: outputFile,
+ ReuseObjects: library.reuseObjects,
+ Objects: library.objects,
- TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
- Direct(outputFile).
- Transitive(deps.TranstiveStaticLibrariesForOrdering).
- Build(),
- })
+ TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
+ Direct(outputFile).
+ Transitive(deps.TranstiveStaticLibrariesForOrdering).
+ Build(),
+ })
+ }
+
+ if library.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+ }
return outputFile
}
@@ -977,7 +1028,12 @@
transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
stripFlags := flagsToStripFlags(flags)
- if library.stripper.NeedsStrip(ctx) {
+ needsStrip := library.stripper.NeedsStrip(ctx)
+ if library.buildStubs() {
+ // No need to strip stubs libraries
+ needsStrip = false
+ }
+ if needsStrip {
if ctx.Darwin() {
stripFlags.StripUseGnuStrip = true
}
@@ -1017,7 +1073,7 @@
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
- if Bool(library.Properties.Sort_bss_symbols_by_size) {
+ if Bool(library.Properties.Sort_bss_symbols_by_size) && !library.buildStubs() {
unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
@@ -1058,20 +1114,20 @@
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
if len(stubs) > 0 {
- var stubsInfo []SharedLibraryStubsInfo
+ var stubsInfo []SharedStubLibrary
for _, stub := range stubs {
stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
- stubsInfo = append(stubsInfo, SharedLibraryStubsInfo{
+ stubsInfo = append(stubsInfo, SharedStubLibrary{
Version: moduleLibraryInterface(stub).stubsVersion(),
SharedLibraryInfo: stubInfo,
FlagExporterInfo: flagInfo,
})
}
- ctx.SetProvider(SharedLibraryImplementationStubsInfoProvider, SharedLibraryImplementationStubsInfo{
- SharedLibraryStubsInfos: stubsInfo,
+ ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
+ SharedStubLibraries: stubsInfo,
- IsLLNDK: ctx.isLlndk(ctx.Config()),
+ IsLLNDK: ctx.IsLlndk(),
})
}
@@ -1100,7 +1156,7 @@
func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
// The logic must be consistent with classifySourceAbiDump.
isNdk := ctx.isNdk(ctx.Config())
- isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || (ctx.useVndk() && ctx.isVndk())
+ isLlndkOrVndk := ctx.IsLlndkPublic() || (ctx.useVndk() && ctx.isVndk())
refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
@@ -1121,7 +1177,7 @@
}
func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
- if library.shouldCreateSourceAbiDump(ctx) {
+ if library.sabi.shouldCreateSourceAbiDump() {
var vndkVersion string
if ctx.useVndk() {
@@ -1146,21 +1202,74 @@
library.Properties.Header_abi_checker.Exclude_symbol_versions,
library.Properties.Header_abi_checker.Exclude_symbol_tags)
- addLsdumpPath(library.classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
+ addLsdumpPath(classifySourceAbiDump(ctx) + ":" + library.sAbiOutputFile.String())
refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
if refAbiDumpFile != nil {
library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
refAbiDumpFile, fileName, exportedHeaderFlags,
Bool(library.Properties.Header_abi_checker.Check_all_apis),
- ctx.isLlndk(ctx.Config()), ctx.isNdk(ctx.Config()), ctx.isVndkExt())
+ ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
}
}
}
+func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
+ srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
+ srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
+
+ var installPaths []android.WritablePath
+ for _, header := range srcFiles {
+ headerDir := filepath.Dir(header.String())
+ relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
+ if err != nil {
+ ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
+ srcDir.String(), headerDir, err)
+ continue
+ }
+
+ installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
+ }
+
+ return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
+}
+
+// link registers actions to link this library, and sets various fields
+// on this library to reflect information that should be exported up the build
+// tree (for example, exported flags and include paths).
func (library *libraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
+ if ctx.IsLlndk() {
+ if len(library.Properties.Llndk.Export_preprocessed_headers) > 0 {
+ // This is the vendor variant of an LLNDK library with preprocessed headers.
+ genHeaderOutDir := android.PathForModuleGen(ctx, "include")
+
+ var timestampFiles android.Paths
+ for _, dir := range library.Properties.Llndk.Export_preprocessed_headers {
+ timestampFiles = append(timestampFiles, processLLNDKHeaders(ctx, dir, genHeaderOutDir))
+ }
+
+ if Bool(library.Properties.Llndk.Export_headers_as_system) {
+ library.reexportSystemDirs(genHeaderOutDir)
+ } else {
+ library.reexportDirs(genHeaderOutDir)
+ }
+
+ library.reexportDeps(timestampFiles...)
+ }
+
+ if Bool(library.Properties.Llndk.Export_headers_as_system) {
+ library.flagExporter.Properties.Export_system_include_dirs = append(
+ library.flagExporter.Properties.Export_system_include_dirs,
+ library.flagExporter.Properties.Export_include_dirs...)
+ library.flagExporter.Properties.Export_include_dirs = nil
+ }
+ }
+
+ // Linking this library consists of linking `deps.Objs` (.o files in dependencies
+ // of this library), together with `objs` (.o files created by compiling this
+ // library).
objs = deps.Objs.Copy().Append(objs)
var out android.Path
if library.static() || library.header() {
@@ -1169,6 +1278,7 @@
out = library.linkShared(ctx, flags, deps, objs)
}
+ // Export include paths and flags to be propagated up the tree.
library.exportIncludes(ctx)
library.reexportDirs(deps.ReexportedDirs...)
library.reexportSystemDirs(deps.ReexportedSystemDirs...)
@@ -1176,6 +1286,7 @@
library.reexportDeps(deps.ReexportedDeps...)
library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
+ // Optionally export aidl headers.
if Bool(library.Properties.Aidl.Export_aidl_headers) {
if library.baseCompiler.hasSrcExt(".aidl") {
dir := android.PathForModuleGen(ctx, "aidl")
@@ -1187,6 +1298,7 @@
}
}
+ // Optionally export proto headers.
if Bool(library.Properties.Proto.Export_proto_headers) {
if library.baseCompiler.hasSrcExt(".proto") {
var includes android.Paths
@@ -1206,40 +1318,47 @@
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")
}
}
+ // Add sysprop-related directories to the exported directories of this library.
library.reexportDirs(dir)
library.reexportDeps(library.baseCompiler.pathDeps...)
library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
}
- if library.buildStubs() && !library.skipAPIDefine {
- library.reexportFlags("-D" + versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx)) + "=" + library.stubsVersion())
- }
+ // Add stub-related flags if this library is a stub library.
+ library.exportVersioningMacroIfNeeded(ctx)
+ // Propagate a Provider containing information about exported flags, deps, and include paths.
library.flagExporter.setProvider(ctx)
return out
}
+func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
+ if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
+ name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
+ ver := library.stubsVersion()
+ library.reexportFlags("-D" + name + "=" + ver)
+ }
+}
+
+// buildStatic returns true if this library should be built as a static library.
func (library *libraryDecorator) buildStatic() bool {
return library.MutatedProperties.BuildStatic &&
BoolDefault(library.StaticProperties.Static.Enabled, true)
}
+// buildShared returns true if this library should be built as a shared library.
func (library *libraryDecorator) buildShared() bool {
return library.MutatedProperties.BuildShared &&
BoolDefault(library.SharedProperties.Shared.Enabled, true)
@@ -1262,14 +1381,14 @@
dirOnDevice := android.InstallPathToOnDevicePath(ctx, dir)
target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), "bionic", file.Base())
ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
- library.post_install_cmds = append(library.post_install_cmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
+ library.postInstallCmds = append(library.postInstallCmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
}
func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
if library.shared() {
if ctx.Device() && ctx.useVndk() {
// set subDir for VNDK extensions
- if ctx.isVndkExt() {
+ if ctx.IsVndkExt() {
if ctx.isVndkSp() {
library.baseInstaller.subDir = "vndk-sp"
} else {
@@ -1277,8 +1396,9 @@
}
}
- // In some cases we want to use core variant for VNDK-Core libs
- if ctx.isVndk() && !ctx.isVndkSp() && !ctx.isVndkExt() {
+ // In some cases we want to use core variant for VNDK-Core libs.
+ // Skip product variant since VNDKs use only the vendor variant.
+ if ctx.isVndk() && !ctx.isVndkSp() && !ctx.IsVndkExt() && !ctx.inProduct() {
mayUseCoreVariant := true
if ctx.mustUseVendorVariant() {
@@ -1299,7 +1419,7 @@
// do not install vndk libs
// vndk libs are packaged into VNDK APEX
- if ctx.isVndk() && !ctx.isVndkExt() {
+ if ctx.isVndk() && !ctx.IsVndkExt() {
return
}
} else if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.directlyInAnyApex() {
@@ -1314,9 +1434,9 @@
}
library.baseInstaller.subDir = "bootstrap"
}
- } else if ctx.directlyInAnyApex() && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
+ } else if ctx.directlyInAnyApex() && ctx.IsLlndk() && !isBionic(ctx.baseModuleName()) {
// Skip installing LLNDK (non-bionic) libraries moved to APEX.
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
}
library.baseInstaller.install(ctx, file)
@@ -1325,7 +1445,7 @@
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
library.baseLinker.sanitize.isUnsanitizedVariant() &&
- !library.buildStubs() && ctx.sdkVersion() == "" {
+ ctx.isForPlatform() && !ctx.isPreventInstall() {
installPath := getNdkSysrootBase(ctx).Join(
ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
@@ -1346,41 +1466,56 @@
return library.shared() || library.static()
}
+// static returns true if this library is for a "static' variant.
func (library *libraryDecorator) static() bool {
return library.MutatedProperties.VariantIsStatic
}
+// shared returns true if this library is for a "shared' variant.
func (library *libraryDecorator) shared() bool {
return library.MutatedProperties.VariantIsShared
}
+// header returns true if this library is for a header-only variant.
func (library *libraryDecorator) header() bool {
+ // Neither "static" nor "shared" implies this library is header-only.
return !library.static() && !library.shared()
}
+// setStatic marks the library variant as "static".
func (library *libraryDecorator) setStatic() {
library.MutatedProperties.VariantIsStatic = true
library.MutatedProperties.VariantIsShared = false
}
+// setShared marks the library variant as "shared".
func (library *libraryDecorator) setShared() {
library.MutatedProperties.VariantIsStatic = false
library.MutatedProperties.VariantIsShared = true
}
+// BuildOnlyStatic disables building this library as a shared library.
func (library *libraryDecorator) BuildOnlyStatic() {
library.MutatedProperties.BuildShared = false
}
+// BuildOnlyShared disables building this library as a static library.
func (library *libraryDecorator) BuildOnlyShared() {
library.MutatedProperties.BuildStatic = false
}
+// HeaderOnly disables building this library as a shared or static library;
+// the library only exists to propagate header file dependencies up the build graph.
func (library *libraryDecorator) HeaderOnly() {
library.MutatedProperties.BuildShared = false
library.MutatedProperties.BuildStatic = false
}
+// hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
+func (library *libraryDecorator) hasLLNDKStubs() bool {
+ return String(library.Properties.Llndk_stubs) != ""
+}
+
func (library *libraryDecorator) implementationModuleName(name string) string {
return name
}
@@ -1393,6 +1528,9 @@
if library.Properties.Header_abi_checker.Symbol_file != nil {
return library.Properties.Header_abi_checker.Symbol_file
}
+ if ctx.Module().(*Module).IsLlndk() {
+ return library.Properties.Llndk.Symbol_file
+ }
if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
return library.Properties.Stubs.Symbol_file
}
@@ -1458,6 +1596,17 @@
var versioningMacroNamesListKey = android.NewOnceKey("versioningMacroNamesList")
+// versioningMacroNamesList returns a singleton map, where keys are "version macro names",
+// and values are the module name responsible for registering the version macro name.
+//
+// Version macros are used when building against stubs, to provide version information about
+// the stub. Only stub libraries should have an entry in this list.
+//
+// For example, when building against libFoo#ver, __LIBFOO_API__ macro is set to ver so
+// that headers from libFoo can be conditionally compiled (this may hide APIs
+// that are not available for the version).
+//
+// This map is used to ensure that there aren't conflicts between these version macro names.
func versioningMacroNamesList(config android.Config) *map[string]string {
return config.Once(versioningMacroNamesListKey, func() interface{} {
m := make(map[string]string)
@@ -1469,12 +1618,17 @@
// other characters are all converted to _
var charsNotForMacro = regexp.MustCompile("[^a-zA-Z0-9_]+")
+// versioningMacroName returns the canonical version macro name for the given module.
func versioningMacroName(moduleName string) string {
macroName := charsNotForMacro.ReplaceAllString(moduleName, "_")
macroName = strings.ToUpper(macroName)
return "__" + macroName + "_API__"
}
+// NewLibrary builds and returns a new Module corresponding to a C++ library.
+// Individual module implementations which comprise a C++ library (or something like
+// a C++ library) should call this function, set some fields on the result, and
+// then call the Init function.
func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module := newModule(hod, android.MultilibBoth)
@@ -1530,16 +1684,20 @@
}
}
+// LinkageMutator adds "static" or "shared" variants for modules depending
+// on whether the module can be built as a static library or a shared library.
func LinkageMutator(mctx android.BottomUpMutatorContext) {
- cc_prebuilt := false
+ ccPrebuilt := false
if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
- _, cc_prebuilt = m.linker.(prebuiltLibraryInterface)
+ _, ccPrebuilt = m.linker.(prebuiltLibraryInterface)
}
- if cc_prebuilt {
+ if ccPrebuilt {
library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface)
// Differentiate between header only and building an actual static/shared library
- if library.buildStatic() || library.buildShared() {
+ buildStatic := library.buildStatic()
+ buildShared := library.buildShared()
+ if buildStatic || buildShared {
// Always create both the static and shared variants for prebuilt libraries, and then disable the one
// that is not being used. This allows them to share the name of a cc_library module, which requires that
// all the variants of the cc_library also exist on the prebuilt.
@@ -1550,16 +1708,16 @@
static.linker.(prebuiltLibraryInterface).setStatic()
shared.linker.(prebuiltLibraryInterface).setShared()
- if library.buildShared() {
+ if buildShared {
mctx.AliasVariation("shared")
- } else if library.buildStatic() {
+ } else if buildStatic {
mctx.AliasVariation("static")
}
- if !library.buildStatic() {
+ if !buildStatic {
static.linker.(prebuiltLibraryInterface).disablePrebuilt()
}
- if !library.buildShared() {
+ if !buildShared {
shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
}
} else {
@@ -1574,7 +1732,15 @@
variations = append(variations, "")
}
- if library.BuildStaticVariant() && library.BuildSharedVariant() {
+ isLLNDK := false
+ if m, ok := mctx.Module().(*Module); ok {
+ // Don't count the vestigial llndk_library module as isLLNDK, it needs a static
+ // variant so that a cc_library_prebuilt can depend on it.
+ isLLNDK = m.IsLlndk() && !isVestigialLLNDKModule(m)
+ }
+ buildStatic := library.BuildStaticVariant() && !isLLNDK
+ buildShared := library.BuildSharedVariant()
+ if buildStatic && buildShared {
variations := append([]string{"static", "shared"}, variations...)
modules := mctx.CreateLocalVariations(variations...)
@@ -1588,13 +1754,13 @@
reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
}
mctx.AliasVariation("shared")
- } else if library.BuildStaticVariant() {
+ } else if buildStatic {
variations := append([]string{"static"}, variations...)
modules := mctx.CreateLocalVariations(variations...)
modules[0].(LinkableInterface).SetStatic()
mctx.AliasVariation("static")
- } else if library.BuildSharedVariant() {
+ } else if buildShared {
variations := append([]string{"shared"}, variations...)
modules := mctx.CreateLocalVariations(variations...)
@@ -1607,6 +1773,9 @@
}
}
+// normalizeVersions modifies `versions` in place, so that each raw version
+// string becomes its normalized canonical form.
+// Validates that the versions in `versions` are specified in least to greatest order.
func normalizeVersions(ctx android.BaseModuleContext, versions []string) {
var previous android.ApiLevel
for i, v := range versions {
@@ -1624,22 +1793,32 @@
}
func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
- // "" is for the non-stubs (implementation) variant.
+ // "" is for the non-stubs (implementation) variant for system modules, or the LLNDK variant
+ // for LLNDK modules.
variants := append(android.CopyOf(versions), "")
+ m := mctx.Module().(*Module)
+ isLLNDK := m.IsLlndk()
+
modules := mctx.CreateLocalVariations(variants...)
for i, m := range modules {
- if variants[i] != "" {
+
+ if variants[i] != "" || isLLNDK {
+ // A stubs or LLNDK stubs variant.
c := m.(*Module)
- c.Properties.HideFromMake = true
c.sanitize = nil
c.stl = nil
c.Properties.PreventInstall = true
lib := moduleLibraryInterface(m)
lib.setBuildStubs()
- lib.setStubsVersion(variants[i])
- // The implementation depends on the stubs
- mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+
+ if variants[i] != "" {
+ // A non-LLNDK stubs module is hidden from make and has a dependency from the
+ // implementation module to the stubs module.
+ c.Properties.HideFromMake = true
+ lib.setStubsVersion(variants[i])
+ mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+ }
}
}
mctx.AliasVariation("")
@@ -1669,9 +1848,8 @@
Host() bool
InRamdisk() bool
InVendorRamdisk() bool
- InRecovery() bool
}) bool {
- return !module.Host() && !module.InRamdisk() && !module.InVendorRamdisk() && !module.InRecovery()
+ return !module.Host() && !module.InRamdisk() && !module.InVendorRamdisk()
}
func CanBeVersionVariant(module interface {
@@ -1686,7 +1864,7 @@
module.CcLibraryInterface() && module.Shared()
}
-func moduleLibraryInterface(module android.Module) libraryInterface {
+func moduleLibraryInterface(module blueprint.Module) libraryInterface {
if m, ok := module.(*Module); ok {
return m.library
}
@@ -1707,7 +1885,15 @@
// Set the versions on the pre-mutated module so they can be read by any llndk modules that
// depend on the implementation library and haven't been mutated yet.
library.setAllStubsVersions(versions)
- return
+ }
+
+ if mctx.Module().(*Module).UseVndk() && library.hasLLNDKStubs() {
+ // Propagate the version to the llndk stubs module.
+ mctx.VisitDirectDepsWithTag(llndkStubDepTag, func(stubs android.Module) {
+ if stubsLib := moduleLibraryInterface(stubs); stubsLib != nil {
+ stubsLib.setAllStubsVersions(library.allStubsVersions())
+ }
+ })
}
}
}
diff --git a/cc/linkable.go b/cc/linkable.go
index 0609b28..ab5a552 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -6,7 +6,63 @@
"github.com/google/blueprint"
)
+// PlatformSanitizeable is an interface for sanitizing platform modules.
+type PlatformSanitizeable interface {
+ LinkableInterface
+
+ // SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
+ SanitizePropDefined() bool
+
+ // IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
+ // of another module. For example, cc_binary and rust_binary represent dependency roots as other
+ // modules cannot have linkage dependencies against these types.
+ IsDependencyRoot() bool
+
+ // IsSanitizerEnabled returns whether a sanitizer is enabled.
+ IsSanitizerEnabled(t SanitizerType) bool
+
+ // IsSanitizerExplicitlyDisabled returns whether a sanitizer has been explicitly disabled (set to false) rather
+ // than left undefined.
+ IsSanitizerExplicitlyDisabled(t SanitizerType) bool
+
+ // SanitizeDep returns the value of the SanitizeDep flag, which is set if a module is a dependency of a
+ // sanitized module.
+ SanitizeDep() bool
+
+ // SetSanitizer enables or disables the specified sanitizer type if it's supported, otherwise this should panic.
+ SetSanitizer(t SanitizerType, b bool)
+
+ // SetSanitizerDep returns true if the module is statically linked.
+ SetSanitizeDep(b bool)
+
+ // StaticallyLinked returns true if the module is statically linked.
+ StaticallyLinked() bool
+
+ // SetInSanitizerDir sets the module installation to the sanitizer directory.
+ SetInSanitizerDir()
+
+ // SanitizeNever returns true if this module should never be sanitized.
+ SanitizeNever() bool
+
+ // SanitizerSupported returns true if a sanitizer type is supported by this modules compiler.
+ SanitizerSupported(t SanitizerType) bool
+
+ // SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type.
+ SanitizableDepTagChecker() SantizableDependencyTagChecker
+}
+
+// SantizableDependencyTagChecker functions check whether or not a dependency
+// tag can be sanitized. These functions should return true if the tag can be
+// sanitized, otherwise they should return false. These functions should also
+// handle all possible dependency tags in the dependency tree. For example,
+// Rust modules can depend on both Rust and CC libraries, so the Rust module
+// implementation should handle tags from both.
+type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool
+
+// LinkableInterface is an interface for a type of module that is linkable in a C++ library.
type LinkableInterface interface {
+ android.Module
+
Module() android.Module
CcLibrary() bool
CcLibraryInterface() bool
@@ -24,6 +80,8 @@
SetShared()
Static() bool
Shared() bool
+ Header() bool
+ IsPrebuilt() bool
Toc() android.OptionalPath
Host() bool
@@ -37,37 +95,76 @@
InRecovery() bool
OnlyInRecovery() bool
+ InVendor() bool
+
UseSdk() bool
UseVndk() bool
MustUseVendorVariant() bool
+ IsLlndk() bool
+ IsLlndkPublic() bool
IsVndk() bool
+ IsVndkExt() bool
+ IsVndkPrivate() bool
HasVendorVariant() bool
+ InProduct() bool
SdkVersion() string
AlwaysSdk() bool
IsSdkVariant() bool
SplitPerApiLevel() bool
+
+ // SetPreventInstall sets the PreventInstall property to 'true' for this module.
+ SetPreventInstall()
+ // SetHideFromMake sets the HideFromMake property to 'true' for this module.
+ SetHideFromMake()
}
var (
+ // Dependency tag for crtbegin, an object file responsible for initialization.
CrtBeginDepTag = dependencyTag{name: "crtbegin"}
- CrtEndDepTag = dependencyTag{name: "crtend"}
+ // Dependency tag for crtend, an object file responsible for program termination.
+ CrtEndDepTag = dependencyTag{name: "crtend"}
+ // Dependency tag for coverage library.
CoverageDepTag = dependencyTag{name: "coverage"}
)
+// GetImageVariantType returns the ImageVariantType string value for the given module
+// (these are defined in cc/image.go).
+func GetImageVariantType(c LinkableInterface) ImageVariantType {
+ if c.Host() {
+ return hostImageVariant
+ } else if c.InVendor() {
+ return vendorImageVariant
+ } else if c.InProduct() {
+ return productImageVariant
+ } else if c.InRamdisk() {
+ return ramdiskImageVariant
+ } else if c.InVendorRamdisk() {
+ return vendorRamdiskImageVariant
+ } else if c.InRecovery() {
+ return recoveryImageVariant
+ } else {
+ return coreImageVariant
+ }
+}
+
+// SharedDepTag returns the dependency tag for any C++ shared libraries.
func SharedDepTag() blueprint.DependencyTag {
return libraryDependencyTag{Kind: sharedLibraryDependency}
}
+// StaticDepTag returns the dependency tag for any C++ static libraries.
func StaticDepTag() blueprint.DependencyTag {
return libraryDependencyTag{Kind: staticLibraryDependency}
}
+// HeaderDepTag returns the dependency tag for any C++ "header-only" libraries.
func HeaderDepTag() blueprint.DependencyTag {
return libraryDependencyTag{Kind: headerLibraryDependency}
}
+// SharedLibraryInfo is a provider to propagate information about a shared C++ library.
type SharedLibraryInfo struct {
SharedLibrary android.Path
UnstrippedSharedLibrary android.Path
@@ -80,22 +177,30 @@
var SharedLibraryInfoProvider = blueprint.NewProvider(SharedLibraryInfo{})
-type SharedLibraryImplementationStubsInfo struct {
- SharedLibraryStubsInfos []SharedLibraryStubsInfo
-
- IsLLNDK bool
-}
-
-var SharedLibraryImplementationStubsInfoProvider = blueprint.NewProvider(SharedLibraryImplementationStubsInfo{})
-
-type SharedLibraryStubsInfo struct {
+// SharedStubLibrary is a struct containing information about a stub shared library.
+// Stub libraries are used for cross-APEX dependencies; when a library is to depend on a shared
+// library in another APEX, it must depend on the stub version of that library.
+type SharedStubLibrary struct {
+ // The version of the stub (corresponding to the stable version of the shared library being
+ // stubbed).
Version string
SharedLibraryInfo SharedLibraryInfo
FlagExporterInfo FlagExporterInfo
}
-var SharedLibraryStubsInfoProvider = blueprint.NewProvider(SharedLibraryStubsInfo{})
+// SharedLibraryStubsInfo is a provider to propagate information about all shared library stubs
+// which are dependencies of a library.
+// Stub libraries are used for cross-APEX dependencies; when a library is to depend on a shared
+// library in another APEX, it must depend on the stub version of that library.
+type SharedLibraryStubsInfo struct {
+ SharedStubLibraries []SharedStubLibrary
+ IsLLNDK bool
+}
+
+var SharedLibraryStubsProvider = blueprint.NewProvider(SharedLibraryStubsInfo{})
+
+// StaticLibraryInfo is a provider to propagate information about a static C++ library.
type StaticLibraryInfo struct {
StaticLibrary android.Path
Objects Objects
@@ -109,10 +214,19 @@
var StaticLibraryInfoProvider = blueprint.NewProvider(StaticLibraryInfo{})
+// HeaderLibraryInfo is a marker provider that identifies a module as a header library.
+type HeaderLibraryInfo struct {
+}
+
+// HeaderLibraryInfoProvider is a marker provider that identifies a module as a header library.
+var HeaderLibraryInfoProvider = blueprint.NewProvider(HeaderLibraryInfo{})
+
+// FlagExporterInfo is a provider to propagate transitive library information
+// pertaining to exported include paths and flags.
type FlagExporterInfo struct {
- IncludeDirs android.Paths
- SystemIncludeDirs android.Paths
- Flags []string
+ IncludeDirs android.Paths // Include directories to be included with -I
+ SystemIncludeDirs android.Paths // System include directories to be included with -isystem
+ Flags []string // Exported raw flags.
Deps android.Paths
GeneratedHeaders android.Paths
}
diff --git a/cc/linker.go b/cc/linker.go
index 9d4a643..ff07224 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -174,6 +174,15 @@
// variants.
Shared_libs []string
}
+ Apex struct {
+ // list of shared libs that should not be used to build the apex variant of
+ // the C/C++ module.
+ Exclude_shared_libs []string
+
+ // list of static libs that should not be used to build the apex ramdisk
+ // variant of the C/C++ module.
+ Exclude_static_libs []string
+ }
}
// make android::build:GetBuildNumber() available containing the build ID.
@@ -240,12 +249,21 @@
deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Exclude_static_libs)
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+ // Record the libraries that need to be excluded when building for APEX. Unlike other
+ // target.*.exclude_* properties, SharedLibs and StaticLibs are not modified here because
+ // this module hasn't yet passed the apexMutator. Therefore, we can't tell whether this is
+ // an apex variant of not. Record the exclude list in the deps struct for now. The info is
+ // used to mark the dependency tag when adding dependencies to the deps. Then inside
+ // GenerateAndroidBuildActions, the marked dependencies are ignored (i.e. not used) for APEX
+ // variants.
+ deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_shared_libs...)
+ deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_static_libs...)
+
if Bool(linker.Properties.Use_version_lib) {
deps.WholeStaticLibs = append(deps.WholeStaticLibs, "libbuildversion")
}
- // TODO(b/150902910): product variant must use Target.Product
- if ctx.useVndk() {
+ if ctx.inVendor() {
deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Vendor.Shared_libs...)
deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Vendor.Exclude_shared_libs)
deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Vendor.Exclude_shared_libs)
@@ -257,6 +275,18 @@
deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
}
+ if ctx.inProduct() {
+ deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Product.Shared_libs...)
+ deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Product.Exclude_shared_libs)
+ deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Product.Exclude_shared_libs)
+ deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Product.Static_libs...)
+ deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
+ deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Product.Exclude_header_libs)
+ deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs)
+ deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs)
+ deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs)
+ }
+
if ctx.inRecovery() {
deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Recovery.Shared_libs...)
deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
@@ -450,14 +480,14 @@
flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
if ctx.Host() && !ctx.Windows() {
- rpath_prefix := `\$$ORIGIN/`
+ rpathPrefix := `\$$ORIGIN/`
if ctx.Darwin() {
- rpath_prefix = "@loader_path/"
+ rpathPrefix = "@loader_path/"
}
if !ctx.static() {
for _, rpath := range linker.dynamicProperties.RunPaths {
- flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
+ flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
}
}
}
@@ -481,11 +511,14 @@
versionScript := ctx.ExpandOptionalSource(
linker.Properties.Version_script, "version_script")
- // TODO(b/150902910): product variant must use Target.Product
- if ctx.useVndk() && linker.Properties.Target.Vendor.Version_script != nil {
+ if ctx.inVendor() && linker.Properties.Target.Vendor.Version_script != nil {
versionScript = ctx.ExpandOptionalSource(
linker.Properties.Target.Vendor.Version_script,
"target.vendor.version_script")
+ } else if ctx.inProduct() && linker.Properties.Target.Product.Version_script != nil {
+ versionScript = ctx.ExpandOptionalSource(
+ linker.Properties.Target.Product.Version_script,
+ "target.product.version_script")
}
if versionScript.Valid() {
@@ -561,8 +594,8 @@
// Rule to generate .bss symbol ordering file.
var (
- _ = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
- gen_sorted_bss_symbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
+ _ = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
+ genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
blueprint.RuleParams{
Command: "CROSS_COMPILE=$crossCompile $genSortedBssSymbolsPath ${in} ${out}",
CommandDeps: []string{"$genSortedBssSymbolsPath", "${crossCompile}nm"},
@@ -573,7 +606,7 @@
func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
crossCompile := gccCmd(flags.toolchain, "")
ctx.Build(pctx, android.BuildParams{
- Rule: gen_sorted_bss_symbols,
+ Rule: genSortedBssSymbols,
Description: "generate bss symbol order " + symbolOrderingFile.Base(),
Output: symbolOrderingFile,
Input: in,
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 212720b..a46b31c 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -15,21 +15,21 @@
package cc
import (
- "fmt"
- "path/filepath"
"strings"
"android/soong/android"
)
-var llndkImplDep = dependencyTag{name: "llndk impl"}
-
var (
llndkLibrarySuffix = ".llndk"
llndkHeadersSuffix = ".llndk"
)
-// Creates a stub shared library based on the provided version file.
+// Holds properties to describe a stub shared library based on the provided version file.
+// The stub library will actually be built by the cc_library module that points to this
+// module with the llndk_stubs property.
+// TODO(ccross): move the properties from llndk_library modules directly into the cc_library
+// modules and remove the llndk_library modules.
//
// Example:
//
@@ -55,52 +55,34 @@
// Whether the system library uses symbol versions.
Unversioned *bool
- // whether this module can be directly depended upon by libs that are installed
- // to /vendor and /product.
- // When set to false, this module can only be depended on by VNDK libraries, not
- // vendor nor product libraries. This effectively hides this module from
- // non-system modules. Default value is true.
- Vendor_available *bool
-
// list of llndk headers to re-export include directories from.
Export_llndk_headers []string `android:"arch_variant"`
+
+ // whether this module can be directly depended upon by libs that are installed
+ // to /vendor and /product.
+ // When set to true, this module can only be depended on by VNDK libraries, not
+ // vendor nor product libraries. This effectively hides this module from
+ // non-system modules. Default value is false.
+ Private *bool
}
type llndkStubDecorator struct {
*libraryDecorator
Properties llndkLibraryProperties
-
- movedToApex bool
}
var _ versionedInterface = (*llndkStubDecorator)(nil)
func (stub *llndkStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
- flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
- return addStubLibraryCompilerFlags(flags)
+ return flags
}
func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
- vndkVer := ctx.Module().(*Module).VndkVersion()
- if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
- // For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
- vndkVer = "current"
- }
- if stub.stubsVersion() != "" {
- vndkVer = stub.stubsVersion()
- }
- objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndkVer, "--llndk")
- if !Bool(stub.Properties.Unversioned) {
- stub.versionScriptPath = android.OptionalPathForPath(versionScript)
- }
- return objs
+ return Objects{}
}
func (stub *llndkStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
- headers := addSuffix(stub.Properties.Export_llndk_headers, llndkHeadersSuffix)
- deps.HeaderLibs = append(deps.HeaderLibs, headers...)
- deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, headers...)
return deps
}
@@ -111,62 +93,19 @@
return name + llndkLibrarySuffix
}
+func (stub *llndkStubDecorator) linkerProps() []interface{} {
+ props := stub.libraryDecorator.linkerProps()
+ return append(props, &stub.Properties)
+}
+
func (stub *llndkStubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
stub.libraryDecorator.libName = stub.implementationModuleName(ctx.ModuleName())
return stub.libraryDecorator.linkerFlags(ctx, flags)
}
-func (stub *llndkStubDecorator) processHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
- srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
- srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
-
- var installPaths []android.WritablePath
- for _, header := range srcFiles {
- headerDir := filepath.Dir(header.String())
- relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
- if err != nil {
- ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
- srcDir.String(), headerDir, err)
- continue
- }
-
- installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
- }
-
- return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
-}
-
func (stub *llndkStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
objs Objects) android.Path {
-
- impl := ctx.GetDirectDepWithTag(stub.implementationModuleName(ctx.ModuleName()), llndkImplDep)
- if implApexModule, ok := impl.(android.ApexModule); ok {
- stub.movedToApex = implApexModule.DirectlyInAnyApex()
- }
-
- if len(stub.Properties.Export_preprocessed_headers) > 0 {
- genHeaderOutDir := android.PathForModuleGen(ctx, "include")
-
- var timestampFiles android.Paths
- for _, dir := range stub.Properties.Export_preprocessed_headers {
- timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
- }
-
- if Bool(stub.Properties.Export_headers_as_system) {
- stub.reexportSystemDirs(genHeaderOutDir)
- } else {
- stub.reexportDirs(genHeaderOutDir)
- }
-
- stub.reexportDeps(timestampFiles...)
- }
-
- if Bool(stub.Properties.Export_headers_as_system) {
- stub.exportIncludesAsSystem(ctx)
- stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
- }
-
- return stub.libraryDecorator.link(ctx, flags, deps, objs)
+ return nil
}
func (stub *llndkStubDecorator) nativeCoverage() bool {
@@ -181,20 +120,8 @@
return true
}
-func (stub *llndkStubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
- // Get the versions from the implementation module.
- impls := ctx.GetDirectDepsWithTag(llndkImplDep)
- if len(impls) > 1 {
- panic(fmt.Errorf("Expected single implmenetation library, got %d", len(impls)))
- } else if len(impls) == 1 {
- return moduleLibraryInterface(impls[0]).allStubsVersions()
- }
- return nil
-}
-
func NewLLndkStubLibrary() *Module {
module, library := NewLibrary(android.DeviceSupported)
- library.BuildOnlyShared()
module.stl = nil
module.sanitize = nil
library.disableStripping()
@@ -202,18 +129,11 @@
stub := &llndkStubDecorator{
libraryDecorator: library,
}
- stub.Properties.Vendor_available = BoolPtr(true)
module.compiler = stub
module.linker = stub
module.installer = nil
module.library = stub
- module.AddProperties(
- &module.Properties,
- &stub.Properties,
- &library.MutatedProperties,
- &library.flagExporter.Properties)
-
return module
}
@@ -227,16 +147,25 @@
// }
func LlndkLibraryFactory() android.Module {
module := NewLLndkStubLibrary()
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
- return module
+ return module.Init()
+}
+
+// isVestigialLLNDKModule returns true if m is a vestigial llndk_library module used to provide
+// properties to the LLNDK variant of a cc_library.
+func isVestigialLLNDKModule(m *Module) bool {
+ _, ok := m.linker.(*llndkStubDecorator)
+ return ok
}
type llndkHeadersDecorator struct {
*libraryDecorator
}
-func (headers *llndkHeadersDecorator) Name(name string) string {
- return name + llndkHeadersSuffix
+func (llndk *llndkHeadersDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+ deps.HeaderLibs = append(deps.HeaderLibs, llndk.Properties.Llndk.Export_llndk_headers...)
+ deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+ llndk.Properties.Llndk.Export_llndk_headers...)
+ return deps
}
// llndk_headers contains a set of c/c++ llndk headers files which are imported
@@ -256,11 +185,6 @@
module.installer = nil
module.library = decorator
- module.AddProperties(
- &module.Properties,
- &library.MutatedProperties,
- &library.flagExporter.Properties)
-
module.Init()
return module
diff --git a/cc/makevars.go b/cc/makevars.go
index bd8aab5..48d5636 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -99,6 +99,7 @@
ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
+ ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
// Filter vendor_public_library that are exported to make
exportedVendorPublicLibraries := []string{}
@@ -153,16 +154,6 @@
ctx.Strict("SOONG_STRIP_PATH", "${stripPath}")
ctx.Strict("XZ", "${xzCmd}")
- nativeHelperIncludeFlags, err := ctx.Eval("${config.CommonNativehelperInclude}")
- if err != nil {
- panic(err)
- }
- nativeHelperIncludes, nativeHelperSystemIncludes := splitSystemIncludes(ctx, nativeHelperIncludeFlags)
- if len(nativeHelperSystemIncludes) > 0 {
- panic("native helper may not have any system includes")
- }
- ctx.Strict("JNI_H_INCLUDE", strings.Join(nativeHelperIncludes, " "))
-
includeFlags, err := ctx.Eval("${config.CommonGlobalIncludes}")
if err != nil {
panic(err)
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/pgo.go b/cc/pgo.go
index 3cf550a..ada694b 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -103,7 +103,7 @@
}
func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath {
- profile_file := *props.Pgo.Profile_file
+ profileFile := *props.Pgo.Profile_file
// Test if the profile_file is present in any of the PGO profile projects
for _, profileProject := range getPgoProfileProjects(ctx.DeviceConfig()) {
@@ -112,24 +112,24 @@
// <profile_file>.<arbitrary-version> when available. This
// works around an issue where ccache serves stale cache
// entries when the profile file has changed.
- globPattern := filepath.Join(profileProject, profile_file+".*")
- versioned_profiles, err := ctx.GlobWithDeps(globPattern, nil)
+ globPattern := filepath.Join(profileProject, profileFile+".*")
+ versionedProfiles, err := ctx.GlobWithDeps(globPattern, nil)
if err != nil {
ctx.ModuleErrorf("glob: %s", err.Error())
}
- path := android.ExistentPathForSource(ctx, profileProject, profile_file)
+ path := android.ExistentPathForSource(ctx, profileProject, profileFile)
if path.Valid() {
- if len(versioned_profiles) != 0 {
- ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+filepath.Join(profileProject, profile_file)+", "+strings.Join(versioned_profiles, ", "))
+ if len(versionedProfiles) != 0 {
+ ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+filepath.Join(profileProject, profileFile)+", "+strings.Join(versionedProfiles, ", "))
}
return path
}
- if len(versioned_profiles) > 1 {
- ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+strings.Join(versioned_profiles, ", "))
- } else if len(versioned_profiles) == 1 {
- return android.OptionalPathForPath(android.PathForSource(ctx, versioned_profiles[0]))
+ if len(versionedProfiles) > 1 {
+ ctx.PropertyErrorf("pgo.profile_file", "Profile_file has multiple versions: "+strings.Join(versionedProfiles, ", "))
+ } else if len(versionedProfiles) == 1 {
+ return android.OptionalPathForPath(android.PathForSource(ctx, versionedProfiles[0]))
}
}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 37df4ba..2cd18cb 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"path/filepath"
+ "strings"
)
func init() {
@@ -117,6 +118,8 @@
return nil
}
+ p.libraryDecorator.exportVersioningMacroIfNeeded(ctx)
+
in := android.PathForModuleSrc(ctx, srcs[0])
if p.static() {
@@ -190,6 +193,12 @@
}
}
+ if p.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+
+ return nil
+ }
+
return nil
}
@@ -220,6 +229,11 @@
p.properties.Srcs = nil
}
+// Implements versionedInterface
+func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
+ return strings.TrimPrefix(name, "prebuilt_")
+}
+
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
diff --git a/cc/sabi.go b/cc/sabi.go
index ef6bead..4a1ba3c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -15,7 +15,6 @@
package cc
import (
- "strings"
"sync"
"android/soong/android"
@@ -23,12 +22,18 @@
)
var (
- lsdumpPaths []string
- sabiLock sync.Mutex
+ lsdumpPaths []string
+ lsdumpPathsLock sync.Mutex
)
type SAbiProperties struct {
- CreateSAbiDumps bool `blueprint:"mutated"`
+ // Whether ABI dump should be created for this module.
+ // Set by `sabiDepsMutator` if this module is a shared library that needs ABI check, or a static
+ // library that is depended on by an ABI checked library.
+ ShouldCreateSourceAbiDump bool `blueprint:"mutated"`
+
+ // Include directories that may contain ABI information exported by a library.
+ // These directories are passed to the header-abi-dumper.
ReexportedIncludes []string `blueprint:"mutated"`
}
@@ -36,66 +41,172 @@
Properties SAbiProperties
}
-func (sabimod *sabi) props() []interface{} {
- return []interface{}{&sabimod.Properties}
+func (sabi *sabi) props() []interface{} {
+ return []interface{}{&sabi.Properties}
}
-func (sabimod *sabi) begin(ctx BaseModuleContext) {}
+func (sabi *sabi) begin(ctx BaseModuleContext) {}
-func (sabimod *sabi) deps(ctx BaseModuleContext, deps Deps) Deps {
+func (sabi *sabi) deps(ctx BaseModuleContext, deps Deps) Deps {
return deps
}
-func inListWithPrefixSearch(flag string, filter []string) bool {
- // Assuming the filter is small enough.
- // If the suffix of a filter element is *, try matching prefixes as well.
- for _, f := range filter {
- if (f == flag) || (strings.HasSuffix(f, "*") && strings.HasPrefix(flag, strings.TrimSuffix(f, "*"))) {
- return true
- }
- }
- return false
-}
-
-func filterOutWithPrefix(list []string, filter []string) (remainder []string) {
- // Go through the filter, matching and optionally doing a prefix search for list elements.
- for _, l := range list {
- if !inListWithPrefixSearch(l, filter) {
- remainder = append(remainder, l)
- }
- }
- return
-}
-
-func (sabimod *sabi) flags(ctx ModuleContext, flags Flags) Flags {
- // Assuming that the cflags which clang LibTooling tools cannot
- // understand have not been converted to ninja variables yet.
- flags.Local.ToolingCFlags = filterOutWithPrefix(flags.Local.CFlags, config.ClangLibToolingUnknownCflags)
- flags.Global.ToolingCFlags = filterOutWithPrefix(flags.Global.CFlags, config.ClangLibToolingUnknownCflags)
- flags.Local.ToolingCppFlags = filterOutWithPrefix(flags.Local.CppFlags, config.ClangLibToolingUnknownCflags)
- flags.Global.ToolingCppFlags = filterOutWithPrefix(flags.Global.CppFlags, config.ClangLibToolingUnknownCflags)
-
+func (sabi *sabi) flags(ctx ModuleContext, flags Flags) Flags {
+ // Filter out flags which libTooling don't understand.
+ // This is here for legacy reasons and future-proof, in case the version of libTooling and clang
+ // diverge.
+ flags.Local.ToolingCFlags = config.ClangLibToolingFilterUnknownCflags(flags.Local.CFlags)
+ flags.Global.ToolingCFlags = config.ClangLibToolingFilterUnknownCflags(flags.Global.CFlags)
+ flags.Local.ToolingCppFlags = config.ClangLibToolingFilterUnknownCflags(flags.Local.CppFlags)
+ flags.Global.ToolingCppFlags = config.ClangLibToolingFilterUnknownCflags(flags.Global.CppFlags)
return flags
}
-func sabiDepsMutator(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok &&
- ((c.IsVndk() && c.UseVndk()) || c.isLlndk(mctx.Config()) ||
- (c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
- mctx.VisitDirectDeps(func(m android.Module) {
- if tag, ok := mctx.OtherModuleDependencyTag(m).(libraryDependencyTag); ok && tag.static() {
- cc, _ := m.(*Module)
- if cc == nil {
- return
- }
- cc.sabi.Properties.CreateSAbiDumps = true
+// Returns true if ABI dump should be created for this library, either because library is ABI
+// checked or is depended on by an ABI checked library.
+// Could be called as a nil receiver.
+func (sabi *sabi) shouldCreateSourceAbiDump() bool {
+ return sabi != nil && sabi.Properties.ShouldCreateSourceAbiDump
+}
+
+// Returns a string that represents the class of the ABI dump.
+// Returns an empty string if ABI check is disabled for this library.
+func classifySourceAbiDump(ctx android.BaseModuleContext) string {
+ m := ctx.Module().(*Module)
+ if m.library.headerAbiCheckerExplicitlyDisabled() {
+ return ""
+ }
+ // Return NDK if the library is both NDK and LLNDK.
+ if m.IsNdk(ctx.Config()) {
+ return "NDK"
+ }
+ if m.isImplementationForLLNDKPublic() {
+ return "LLNDK"
+ }
+ if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() {
+ if m.isVndkSp() {
+ if m.IsVndkExt() {
+ return "VNDK-SP-ext"
+ } else {
+ return "VNDK-SP"
}
- })
+ } else {
+ if m.IsVndkExt() {
+ return "VNDK-ext"
+ } else {
+ return "VNDK-core"
+ }
+ }
+ }
+ if m.library.headerAbiCheckerEnabled() || m.library.hasStubsVariants() {
+ return "PLATFORM"
+ }
+ return ""
+}
+
+// Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
+// ctx should be wrapping a native library type module.
+func shouldCreateSourceAbiDumpForLibrary(ctx android.BaseModuleContext) bool {
+ if ctx.Fuchsia() {
+ return false
+ }
+
+ // Only generate ABI dump for device modules.
+ if !ctx.Device() {
+ return false
+ }
+
+ m := ctx.Module().(*Module)
+
+ // Only create ABI dump for native library module types.
+ if m.library == nil {
+ return false
+ }
+
+ // Create ABI dump for static libraries only if they are dependencies of ABI checked libraries.
+ if m.library.static() {
+ return m.sabi.shouldCreateSourceAbiDump()
+ }
+
+ // Module is shared library type.
+
+ // Don't check uninstallable modules.
+ if m.IsHideFromMake() {
+ return false
+ }
+
+ // Don't check ramdisk or recovery variants. Only check core, vendor or product variants.
+ if m.InRamdisk() || m.InVendorRamdisk() || m.InRecovery() {
+ return false
+ }
+
+ // Don't create ABI dump for prebuilts.
+ if m.Prebuilt() != nil || m.isSnapshotPrebuilt() {
+ return false
+ }
+
+ // Coverage builds have extra symbols.
+ if m.isCoverageVariant() {
+ return false
+ }
+
+ // Some sanitizer variants may have different ABI.
+ if m.sanitize != nil && !m.sanitize.isVariantOnProductionDevice() {
+ return false
+ }
+
+ // Don't create ABI dump for stubs.
+ if m.isNDKStubLibrary() || m.IsLlndk() || m.IsStubs() {
+ return false
+ }
+
+ isPlatformVariant := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
+ if isPlatformVariant {
+ // Bionic libraries that are installed to the bootstrap directory are not ABI checked.
+ // Only the runtime APEX variants, which are the implementation libraries of bionic NDK stubs,
+ // are checked.
+ if InstallToBootstrap(m.BaseModuleName(), ctx.Config()) {
+ return false
+ }
+ } else {
+ // Don't create ABI dump if this library is for APEX but isn't exported.
+ if !m.HasStubsVariants() {
+ return false
+ }
+ }
+ return classifySourceAbiDump(ctx) != ""
+}
+
+// Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
+// of their dependencies would be generated.
+func sabiDepsMutator(mctx android.TopDownMutatorContext) {
+ // Escape hatch to not check any ABI dump.
+ if mctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
+ return
+ }
+ // Only create ABI dump for native shared libraries and their static library dependencies.
+ if m, ok := mctx.Module().(*Module); ok && m.sabi != nil {
+ if shouldCreateSourceAbiDumpForLibrary(mctx) {
+ // Mark this module so that .sdump / .lsdump for this library can be generated.
+ m.sabi.Properties.ShouldCreateSourceAbiDump = true
+ // Mark all of its static library dependencies.
+ mctx.VisitDirectDeps(func(child android.Module) {
+ depTag := mctx.OtherModuleDependencyTag(child)
+ if libDepTag, ok := depTag.(libraryDependencyTag); ok && libDepTag.static() {
+ if c, ok := child.(*Module); ok && c.sabi != nil {
+ // Mark this module so that .sdump for this static library can be generated.
+ c.sabi.Properties.ShouldCreateSourceAbiDump = true
+ }
+ }
+ })
+ }
}
}
+// Add an entry to the global list of lsdump. The list is exported to a Make variable by
+// `cc.makeVarsProvider`.
func addLsdumpPath(lsdumpPath string) {
- sabiLock.Lock()
+ lsdumpPathsLock.Lock()
+ defer lsdumpPathsLock.Unlock()
lsdumpPaths = append(lsdumpPaths, lsdumpPath)
- sabiLock.Unlock()
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 22ee25f..8eeb355 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -71,7 +71,7 @@
"export_memory_stats=0", "max_malloc_fill_size=0"}
)
-type sanitizerType int
+type SanitizerType int
func boolPtr(v bool) *bool {
if v {
@@ -82,19 +82,20 @@
}
const (
- asan sanitizerType = iota + 1
+ Asan SanitizerType = iota + 1
hwasan
tsan
intOverflow
cfi
scs
- fuzzer
+ Fuzzer
+ memtag_heap
)
// Name of the sanitizer variation for this sanitizer type
-func (t sanitizerType) variationName() string {
+func (t SanitizerType) variationName() string {
switch t {
- case asan:
+ case Asan:
return "asan"
case hwasan:
return "hwasan"
@@ -106,20 +107,24 @@
return "cfi"
case scs:
return "scs"
- case fuzzer:
+ case memtag_heap:
+ return "memtag_heap"
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
// This is the sanitizer names in SANITIZE_[TARGET|HOST]
-func (t sanitizerType) name() string {
+func (t SanitizerType) name() string {
switch t {
- case asan:
+ case Asan:
return "address"
case hwasan:
return "hwaddress"
+ case memtag_heap:
+ return "memtag_heap"
case tsan:
return "thread"
case intOverflow:
@@ -128,15 +133,37 @@
return "cfi"
case scs:
return "shadow-call-stack"
- case fuzzer:
+ case Fuzzer:
return "fuzzer"
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
-func (t sanitizerType) incompatibleWithCfi() bool {
- return t == asan || t == fuzzer || t == hwasan
+func (*Module) SanitizerSupported(t SanitizerType) bool {
+ switch t {
+ case Asan:
+ return true
+ case hwasan:
+ return true
+ case tsan:
+ return true
+ case intOverflow:
+ return true
+ case cfi:
+ return true
+ case scs:
+ return true
+ case Fuzzer:
+ return true
+ default:
+ return false
+ }
+}
+
+// incompatibleWithCfi returns true if a sanitizer is incompatible with CFI.
+func (t SanitizerType) incompatibleWithCfi() bool {
+ return t == Asan || t == Fuzzer || t == hwasan
}
type SanitizeUserProps struct {
@@ -157,6 +184,7 @@
Integer_overflow *bool `android:"arch_variant"`
Scudo *bool `android:"arch_variant"`
Scs *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
// A modifier for ASAN and HWASAN for write only instrumentation
Writeonly *bool `android:"arch_variant"`
@@ -168,6 +196,7 @@
Undefined *bool `android:"arch_variant"`
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
+ Memtag_heap *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
No_recover []string `android:"arch_variant"`
} `android:"arch_variant"`
@@ -204,6 +233,13 @@
Properties SanitizeProperties
}
+// Mark this tag with a check to see if apex dependency check should be skipped
+func (t libraryDependencyTag) SkipApexAllowedDependenciesCheck() bool {
+ return t.skipApexAllowedDependenciesCheck
+}
+
+var _ android.SkipApexAllowedDependenciesCheck = (*libraryDependencyTag)(nil)
+
func init() {
android.RegisterMakeVarsProvider(pctx, cfiMakeVarsProvider)
android.RegisterMakeVarsProvider(pctx, hwasanMakeVarsProvider)
@@ -231,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
@@ -301,6 +343,11 @@
}
s.Writeonly = boolPtr(true)
}
+ if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
+ if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
+ s.Memtag_heap = boolPtr(true)
+ }
+ }
if len(globalSanitizers) > 0 {
ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
@@ -317,11 +364,32 @@
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])
}
}
+ // Enable Memtag for all components in the include paths (for Aarch64 only)
+ if ctx.Arch().ArchType == android.Arm64 {
+ if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
+ 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()) {
+ if s.Memtag_heap == nil {
+ s.Memtag_heap = boolPtr(true)
+ }
+ }
+ }
+
// Enable CFI for all components in the include paths (for Aarch64 only)
if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
s.Cfi = boolPtr(true)
@@ -352,6 +420,11 @@
s.Scs = nil
}
+ // memtag_heap is only implemented on AArch64.
+ if ctx.Arch().ArchType != android.Arm64 {
+ s.Memtag_heap = nil
+ }
+
// Also disable CFI if ASAN is enabled.
if Bool(s.Address) || Bool(s.Hwaddress) {
s.Cfi = boolPtr(false)
@@ -406,7 +479,7 @@
if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
- Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs)) {
+ Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap)) {
sanitize.Properties.SanitizerEnabled = true
}
@@ -451,6 +524,22 @@
return false
}
+func toDisableUnsignedShiftBaseChange(flags []string) bool {
+ // Returns true if any flag is fsanitize*integer, and there is
+ // no explicit flag about sanitize=unsigned-shift-base.
+ for _, f := range flags {
+ if strings.Contains(f, "sanitize=unsigned-shift-base") {
+ return false
+ }
+ }
+ for _, f := range flags {
+ if strings.HasPrefix(f, "-fsanitize") && strings.Contains(f, "integer") {
+ return true
+ }
+ }
+ return false
+}
+
func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
@@ -607,6 +696,10 @@
if toDisableImplicitIntegerChange(flags.Local.CFlags) {
flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
}
+ // http://b/171275751, Android doesn't build with this sanitizer yet.
+ if toDisableUnsignedShiftBaseChange(flags.Local.CFlags) {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=unsigned-shift-base")
+ }
}
if len(sanitize.Properties.DiagSanitizers) > 0 {
@@ -653,9 +746,10 @@
return sanitize.Properties.InSanitizerDir
}
-func (sanitize *sanitize) getSanitizerBoolPtr(t sanitizerType) *bool {
+// getSanitizerBoolPtr returns the SanitizerTypes associated bool pointer from SanitizeProperties.
+func (sanitize *sanitize) getSanitizerBoolPtr(t SanitizerType) *bool {
switch t {
- case asan:
+ case Asan:
return sanitize.Properties.Sanitize.Address
case hwasan:
return sanitize.Properties.Sanitize.Hwaddress
@@ -667,32 +761,37 @@
return sanitize.Properties.Sanitize.Cfi
case scs:
return sanitize.Properties.Sanitize.Scs
- case fuzzer:
+ case memtag_heap:
+ return sanitize.Properties.Sanitize.Memtag_heap
+ case Fuzzer:
return sanitize.Properties.Sanitize.Fuzzer
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
}
+// isUnsanitizedVariant returns true if no sanitizers are enabled.
func (sanitize *sanitize) isUnsanitizedVariant() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
!sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
!sanitize.isSanitizerEnabled(cfi) &&
!sanitize.isSanitizerEnabled(scs) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(memtag_heap) &&
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
+// isVariantOnProductionDevice returns true if variant is for production devices (no non-production sanitizers enabled).
func (sanitize *sanitize) isVariantOnProductionDevice() bool {
- return !sanitize.isSanitizerEnabled(asan) &&
+ return !sanitize.isSanitizerEnabled(Asan) &&
!sanitize.isSanitizerEnabled(hwasan) &&
!sanitize.isSanitizerEnabled(tsan) &&
- !sanitize.isSanitizerEnabled(fuzzer)
+ !sanitize.isSanitizerEnabled(Fuzzer)
}
-func (sanitize *sanitize) SetSanitizer(t sanitizerType, b bool) {
+func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
switch t {
- case asan:
+ case Asan:
sanitize.Properties.Sanitize.Address = boolPtr(b)
case hwasan:
sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
@@ -704,10 +803,12 @@
sanitize.Properties.Sanitize.Cfi = boolPtr(b)
case scs:
sanitize.Properties.Sanitize.Scs = boolPtr(b)
- case fuzzer:
+ case memtag_heap:
+ sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+ case Fuzzer:
sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
default:
- panic(fmt.Errorf("unknown sanitizerType %d", t))
+ panic(fmt.Errorf("unknown SanitizerType %d", t))
}
if b {
sanitize.Properties.SanitizerEnabled = true
@@ -716,7 +817,7 @@
// Check if the sanitizer is explicitly disabled (as opposed to nil by
// virtue of not being set).
-func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -730,7 +831,7 @@
// indirectly (via a mutator) sets the bool ptr to true, and you can't
// distinguish between the cases. It isn't needed though - both cases can be
// treated identically.
-func (sanitize *sanitize) isSanitizerEnabled(t sanitizerType) bool {
+func (sanitize *sanitize) isSanitizerEnabled(t SanitizerType) bool {
if sanitize == nil {
return false
}
@@ -739,7 +840,8 @@
return sanitizerVal != nil && *sanitizerVal == true
}
-func isSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
+// IsSanitizableDependencyTag returns true if the dependency tag is sanitizable.
+func IsSanitizableDependencyTag(tag blueprint.DependencyTag) bool {
switch t := tag.(type) {
case dependencyTag:
return t == reuseObjTag || t == objDepTag
@@ -750,6 +852,10 @@
}
}
+func (m *Module) SanitizableDepTagChecker() SantizableDependencyTagChecker {
+ return IsSanitizableDependencyTag
+}
+
// Determines if the current module is a static library going to be captured
// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
// except for ones which explicitly disable cfi.
@@ -758,51 +864,58 @@
return false
}
- c := mctx.Module().(*Module)
+ c := mctx.Module().(PlatformSanitizeable)
- if !c.inVendor() {
+ if !c.InVendor() {
return false
}
- if !c.static() {
+ if !c.StaticallyLinked() {
return false
}
- if c.Prebuilt() != nil {
+ if c.IsPrebuilt() {
return false
}
- return c.sanitize != nil &&
- !Bool(c.sanitize.Properties.Sanitize.Never) &&
- !c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+ if !c.SanitizerSupported(cfi) {
+ return false
+ }
+
+ return c.SanitizePropDefined() &&
+ !c.SanitizeNever() &&
+ !c.IsSanitizerExplicitlyDisabled(cfi)
}
// Propagate sanitizer requirements down from binaries
-func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
+func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) {
return func(mctx android.TopDownMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok {
- enabled := c.sanitize.isSanitizerEnabled(t)
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok {
+ enabled := c.IsSanitizerEnabled(t)
if t == cfi && needsCfiForVendorSnapshot(mctx) {
// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
// determine defaultVariation in sanitizerMutator below.
// Instead, just mark SanitizeDep to forcefully create cfi variant.
enabled = true
- c.sanitize.Properties.SanitizeDep = true
+ c.SetSanitizeDep(true)
}
if enabled {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
}
- if d, ok := child.(*Module); ok && d.sanitize != nil &&
- !Bool(d.sanitize.Properties.Sanitize.Never) &&
- !d.sanitize.isSanitizerExplicitlyDisabled(t) {
+ if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() &&
+ !d.SanitizeNever() &&
+ !d.IsSanitizerExplicitlyDisabled(t) {
if t == cfi || t == hwasan || t == scs {
- if d.static() {
- d.sanitize.Properties.SanitizeDep = true
+ if d.StaticallyLinked() && d.SanitizerSupported(t) {
+ // Rust does not support some of these sanitizers, so we need to check if it's
+ // supported before setting this true.
+ d.SetSanitizeDep(true)
}
} else {
- d.sanitize.Properties.SanitizeDep = true
+ d.SetSanitizeDep(true)
}
}
return true
@@ -820,9 +933,19 @@
}
}
+func (c *Module) SanitizeNever() bool {
+ return Bool(c.sanitize.Properties.Sanitize.Never)
+}
+
+func (c *Module) IsSanitizerExplicitlyDisabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerExplicitlyDisabled(t)
+}
+
// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) {
+ // Change this to PlatformSanitizable when/if non-cc modules support ubsan sanitizers.
if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+ isSanitizableDependencyTag := c.SanitizableDepTagChecker()
mctx.WalkDeps(func(child, parent android.Module) bool {
if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
return false
@@ -958,6 +1081,20 @@
sanitizers = append(sanitizers, "shadow-call-stack")
}
+ if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() {
+ noteDep := "note_memtag_heap_async"
+ if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) {
+ noteDep = "note_memtag_heap_sync"
+ }
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: true}
+ variations := append(mctx.Target().Variations(),
+ blueprint.Variation{Mutator: "link", Variation: "static"})
+ if c.Device() {
+ variations = append(variations, c.ImageVariation())
+ }
+ mctx.AddFarVariationDependencies(variations, depTag, noteDep)
+ }
+
if Bool(c.sanitize.Properties.Sanitize.Fuzzer) {
sanitizers = append(sanitizers, "fuzzer-no-link")
}
@@ -1002,9 +1139,6 @@
if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
// UBSan is supported on non-bionic linux host builds as well
- if isLlndkLibrary(runtimeLibrary, mctx.Config()) && !c.static() && c.UseVndk() {
- runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
- }
// Adding dependency to the runtime library. We are using *FarVariation*
// because the runtime libraries themselves are not mutated by sanitizer
@@ -1033,7 +1167,7 @@
variations = append(variations, c.ImageVariation())
}
mctx.AddFarVariationDependencies(variations, depTag, deps...)
- } else if !c.static() && !c.header() {
+ } else if !c.static() && !c.Header() {
// If we're using snapshots and in vendor, redirect to snapshot whenever possible
if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() {
snapshots := vendorSnapshotSharedLibs(mctx.Config())
@@ -1041,9 +1175,18 @@
runtimeLibrary = lib
}
}
-
+ // Skip apex dependency check for sharedLibraryDependency
+ // when sanitizer diags are enabled. Skipping the check will allow
+ // building with diag libraries without having to list the
+ // dependency in Apex's allowed_deps file.
+ diagEnabled := len(diagSanitizers) > 0
// dynamic executable and shared libs get shared runtime libs
- depTag := libraryDependencyTag{Kind: sharedLibraryDependency, Order: earlyLibraryDependency}
+ depTag := libraryDependencyTag{
+ Kind: sharedLibraryDependency,
+ Order: earlyLibraryDependency,
+
+ skipApexAllowedDependenciesCheck: diagEnabled,
+ }
variations := append(mctx.Target().Variations(),
blueprint.Variation{Mutator: "link", Variation: "shared"})
if c.Device() {
@@ -1065,16 +1208,52 @@
AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
}
+func (c *Module) SanitizePropDefined() bool {
+ return c.sanitize != nil
+}
+
+func (c *Module) IsSanitizerEnabled(t SanitizerType) bool {
+ return c.sanitize.isSanitizerEnabled(t)
+}
+
+func (c *Module) SanitizeDep() bool {
+ return c.sanitize.Properties.SanitizeDep
+}
+
+func (c *Module) StaticallyLinked() bool {
+ return c.static()
+}
+
+func (c *Module) SetInSanitizerDir() {
+ if c.sanitize != nil {
+ c.sanitize.Properties.InSanitizerDir = true
+ }
+}
+
+func (c *Module) SetSanitizer(t SanitizerType, b bool) {
+ if c.sanitize != nil {
+ c.sanitize.SetSanitizer(t, b)
+ }
+}
+
+func (c *Module) SetSanitizeDep(b bool) {
+ if c.sanitize != nil {
+ c.sanitize.Properties.SanitizeDep = b
+ }
+}
+
+var _ PlatformSanitizeable = (*Module)(nil)
+
// Create sanitized variants for modules that need them
-func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
+func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
- if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
- if c.isDependencyRoot() && c.sanitize.isSanitizerEnabled(t) {
+ if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
+ if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- } else if c.sanitize.isSanitizerEnabled(t) || c.sanitize.Properties.SanitizeDep {
- isSanitizerEnabled := c.sanitize.isSanitizerEnabled(t)
- if c.static() || c.header() || t == asan || t == fuzzer {
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
+ isSanitizerEnabled := c.IsSanitizerEnabled(t)
+ if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer {
// Static and header libs are split into non-sanitized and sanitized variants.
// Shared libs are not split. However, for asan and fuzzer, we split even for shared
// libs because a library sanitized for asan/fuzzer can't be linked from a library
@@ -1088,17 +1267,20 @@
// module. By setting it to the name of the sanitized variation, the dangling dependency
// is redirected to the sanitized variant of the dependent module.
defaultVariation := t.variationName()
+ // Not all PlatformSanitizeable modules support the CFI sanitizer
+ cfiSupported := mctx.Module().(PlatformSanitizeable).SanitizerSupported(cfi)
mctx.SetDefaultDependencyVariation(&defaultVariation)
- modules := mctx.CreateVariations("", t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, false)
- modules[1].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
- modules[1].(*Module).sanitize.Properties.SanitizeDep = false
- if mctx.Device() && t.incompatibleWithCfi() {
+ modules := mctx.CreateVariations("", t.variationName())
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
+ modules[1].(PlatformSanitizeable).SetSanitizeDep(false)
+
+ if mctx.Device() && t.incompatibleWithCfi() && cfiSupported {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[1].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[1].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
// For cfi/scs/hwasan, we can export both sanitized and un-sanitized variants
@@ -1106,46 +1288,48 @@
// For other types of sanitizers, suppress the variation that is disabled.
if t != cfi && t != scs && t != hwasan {
if isSanitizerEnabled {
- modules[0].(*Module).Properties.PreventInstall = true
- modules[0].(*Module).Properties.HideFromMake = true
+ modules[0].(PlatformSanitizeable).SetPreventInstall()
+ modules[0].(PlatformSanitizeable).SetHideFromMake()
} else {
- modules[1].(*Module).Properties.PreventInstall = true
- modules[1].(*Module).Properties.HideFromMake = true
+ modules[1].(PlatformSanitizeable).SetPreventInstall()
+ modules[1].(PlatformSanitizeable).SetHideFromMake()
}
}
// Export the static lib name to make
- if c.static() && c.ExportedToMake() {
+ if c.StaticallyLinked() && c.ExportedToMake() {
if t == cfi {
- cfiStaticLibs(mctx.Config()).add(c, c.Name())
+ cfiStaticLibs(mctx.Config()).add(c, c.Module().Name())
} else if t == hwasan {
- hwasanStaticLibs(mctx.Config()).add(c, c.Name())
+ hwasanStaticLibs(mctx.Config()).add(c, c.Module().Name())
}
}
} else {
// Shared libs are not split. Only the sanitized variant is created.
modules := mctx.CreateVariations(t.variationName())
- modules[0].(*Module).sanitize.SetSanitizer(t, true)
- modules[0].(*Module).sanitize.Properties.SanitizeDep = false
+ modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
+ modules[0].(PlatformSanitizeable).SetSanitizeDep(false)
// locate the asan libraries under /data/asan
- if mctx.Device() && t == asan && isSanitizerEnabled {
- modules[0].(*Module).sanitize.Properties.InSanitizerDir = true
+ if mctx.Device() && t == Asan && isSanitizerEnabled {
+ modules[0].(PlatformSanitizeable).SetInSanitizerDir()
}
if mctx.Device() && t.incompatibleWithCfi() {
// TODO: Make sure that cfi mutator runs "after" any of the sanitizers that
// are incompatible with cfi
- modules[0].(*Module).sanitize.SetSanitizer(cfi, false)
+ modules[0].(PlatformSanitizeable).SetSanitizer(cfi, false)
}
}
}
- c.sanitize.Properties.SanitizeDep = false
+ c.SetSanitizeDep(false)
} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
// APEX modules fall here
sanitizeable.AddSanitizerDependencies(mctx, t.name())
mctx.CreateVariations(t.variationName())
} else if c, ok := mctx.Module().(*Module); ok {
+ //TODO: When Rust modules have vendor support, enable this path for PlatformSanitizeable
+
// Check if it's a snapshot module supporting sanitizer
if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
// Set default variation as above.
@@ -1170,23 +1354,23 @@
type sanitizerStaticLibsMap struct {
// libsMap contains one list of modules per each image and each arch.
// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
- libsMap map[imageVariantType]map[string][]string
+ libsMap map[ImageVariantType]map[string][]string
libsMapLock sync.Mutex
- sanitizerType sanitizerType
+ sanitizerType SanitizerType
}
-func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+func newSanitizerStaticLibsMap(t SanitizerType) *sanitizerStaticLibsMap {
return &sanitizerStaticLibsMap{
sanitizerType: t,
- libsMap: make(map[imageVariantType]map[string][]string),
+ libsMap: make(map[ImageVariantType]map[string][]string),
}
}
// Add the current module to sanitizer static libs maps
// Each module should pass its exported name as names of Make and Soong can differ.
-func (s *sanitizerStaticLibsMap) add(c *Module, name string) {
- image := c.getImageVariantType()
- arch := c.Arch().ArchType.String()
+func (s *sanitizerStaticLibsMap) add(c LinkableInterface, name string) {
+ image := GetImageVariantType(c)
+ arch := c.Module().Target().Arch.ArchType.String()
s.libsMapLock.Lock()
defer s.libsMapLock.Unlock()
@@ -1205,7 +1389,7 @@
// See build/make/core/binary.mk for more details.
func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
for _, image := range android.SortedStringKeys(s.libsMap) {
- archMap := s.libsMap[imageVariantType(image)]
+ archMap := s.libsMap[ImageVariantType(image)]
for _, arch := range android.SortedStringKeys(archMap) {
libs := archMap[arch]
sort.Strings(libs)
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
new file mode 100644
index 0000000..2003e03
--- /dev/null
+++ b/cc/snapshot_prebuilt.go
@@ -0,0 +1,1062 @@
+// Copyright 2020 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 cc
+
+// This file defines snapshot prebuilt modules, e.g. vendor snapshot and recovery snapshot. Such
+// snapshot modules will override original source modules with setting BOARD_VNDK_VERSION, with
+// snapshot mutators and snapshot information maps which are also defined in this file.
+
+import (
+ "strings"
+ "sync"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
+// vendor, recovery, ramdisk.
+type snapshotImage interface {
+ // Used to register callbacks with the build system.
+ init()
+
+ // Returns true if a snapshot should be generated for this image.
+ shouldGenerateSnapshot(ctx android.SingletonContext) bool
+
+ // Function that returns true if the module is included in this image.
+ // Using a function return instead of a value to prevent early
+ // evalution of a function that may be not be defined.
+ inImage(m *Module) func() bool
+
+ // Returns 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.
+ //
+ // For a given snapshot (e.g., vendor, recovery, etc.) if
+ // isProprietaryPath(dir) returns true, then the module in dir will be
+ // built from sources.
+ isProprietaryPath(dir string) bool
+
+ // Whether to include VNDK in the snapshot for this image.
+ includeVndk() bool
+
+ // Whether a given module has been explicitly excluded from the
+ // snapshot, e.g., using the exclude_from_vendor_snapshot or
+ // exclude_from_recovery_snapshot properties.
+ excludeFromSnapshot(m *Module) bool
+
+ // Returns the snapshotMap to be used for a given module and config, or nil if the
+ // module is not included in this image.
+ getSnapshotMap(m *Module, cfg android.Config) *snapshotMap
+
+ // Returns mutex used for mutual exclusion when updating the snapshot maps.
+ getMutex() *sync.Mutex
+
+ // For a given arch, a maps of which modules are included in this image.
+ suffixModules(config android.Config) map[string]bool
+
+ // Whether to add a given module to the suffix map.
+ shouldBeAddedToSuffixModules(m *Module) bool
+
+ // Returns true if the build is using a snapshot for this image.
+ isUsingSnapshot(cfg android.DeviceConfig) bool
+
+ // Whether to skip the module mutator for a module in a given context.
+ skipModuleMutator(ctx android.BottomUpMutatorContext) bool
+
+ // 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{}
+type recoverySnapshotImage struct{}
+
+func (vendorSnapshotImage) init() {
+ android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+ android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+ android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+ android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+ android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+
+ android.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+}
+
+func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+ // BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
+ return ctx.DeviceConfig().VndkVersion() == "current"
+}
+
+func (vendorSnapshotImage) inImage(m *Module) func() bool {
+ return m.InVendor
+}
+
+func (vendorSnapshotImage) private(m *Module) bool {
+ return m.IsVndkPrivate()
+}
+
+func (vendorSnapshotImage) isProprietaryPath(dir string) bool {
+ return isVendorProprietaryPath(dir)
+}
+
+// vendor snapshot includes static/header libraries with vndk: {enabled: true}.
+func (vendorSnapshotImage) includeVndk() bool {
+ return true
+}
+
+func (vendorSnapshotImage) excludeFromSnapshot(m *Module) bool {
+ return m.ExcludeFromVendorSnapshot()
+}
+
+func (vendorSnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
+ if lib, ok := m.linker.(libraryInterface); ok {
+ if lib.static() {
+ return vendorSnapshotStaticLibs(cfg)
+ } else if lib.shared() {
+ return vendorSnapshotSharedLibs(cfg)
+ } else {
+ // header
+ return vendorSnapshotHeaderLibs(cfg)
+ }
+ } else if m.binary() {
+ return vendorSnapshotBinaries(cfg)
+ } else if m.object() {
+ return vendorSnapshotObjects(cfg)
+ } else {
+ return nil
+ }
+}
+
+func (vendorSnapshotImage) getMutex() *sync.Mutex {
+ return &vendorSnapshotsLock
+}
+
+func (vendorSnapshotImage) suffixModules(config android.Config) map[string]bool {
+ return vendorSuffixModules(config)
+}
+
+func (vendorSnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
+ // vendor suffix should be added to snapshots if the source module isn't vendor: true.
+ if module.SocSpecific() {
+ return false
+ }
+
+ // But we can't just check SocSpecific() since we already passed the image mutator.
+ // Check ramdisk and recovery to see if we are real "vendor: true" module.
+ ramdiskAvailable := module.InRamdisk() && !module.OnlyInRamdisk()
+ vendorRamdiskAvailable := module.InVendorRamdisk() && !module.OnlyInVendorRamdisk()
+ recoveryAvailable := module.InRecovery() && !module.OnlyInRecovery()
+
+ return !ramdiskAvailable && !recoveryAvailable && !vendorRamdiskAvailable
+}
+
+func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+ vndkVersion := cfg.VndkVersion()
+ return vndkVersion != "current" && vndkVersion != ""
+}
+
+func (vendorSnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ module, ok := ctx.Module().(*Module)
+ return !ok || module.VndkVersion() != vndkVersion
+}
+
+func (vendorSnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ return true
+ }
+ if module.VndkVersion() != vndkVersion {
+ return true
+ }
+ // .. and also filter out llndk library
+ if module.IsLlndk() {
+ return true
+ }
+ 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)
+ android.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
+ android.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
+ android.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
+ android.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
+}
+
+func (recoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+ // RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
+ // snapshot.
+ return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
+}
+
+func (recoverySnapshotImage) inImage(m *Module) func() bool {
+ return m.InRecovery
+}
+
+// recovery snapshot does not have private libraries.
+func (recoverySnapshotImage) private(m *Module) bool {
+ return false
+}
+
+func (recoverySnapshotImage) isProprietaryPath(dir string) bool {
+ return isRecoveryProprietaryPath(dir)
+}
+
+// recovery snapshot does NOT treat vndk specially.
+func (recoverySnapshotImage) includeVndk() bool {
+ return false
+}
+
+func (recoverySnapshotImage) excludeFromSnapshot(m *Module) bool {
+ return m.ExcludeFromRecoverySnapshot()
+}
+
+func (recoverySnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
+ if lib, ok := m.linker.(libraryInterface); ok {
+ if lib.static() {
+ return recoverySnapshotStaticLibs(cfg)
+ } else if lib.shared() {
+ return recoverySnapshotSharedLibs(cfg)
+ } else {
+ // header
+ return recoverySnapshotHeaderLibs(cfg)
+ }
+ } else if m.binary() {
+ return recoverySnapshotBinaries(cfg)
+ } else if m.object() {
+ return recoverySnapshotObjects(cfg)
+ } else {
+ return nil
+ }
+}
+
+func (recoverySnapshotImage) getMutex() *sync.Mutex {
+ return &recoverySnapshotsLock
+}
+
+func (recoverySnapshotImage) suffixModules(config android.Config) map[string]bool {
+ return recoverySuffixModules(config)
+}
+
+func (recoverySnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
+ return proptools.BoolDefault(module.Properties.Recovery_available, false)
+}
+
+func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+ recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
+ return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
+}
+
+func (recoverySnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
+ module, ok := ctx.Module().(*Module)
+ return !ok || !module.InRecovery()
+}
+
+func (recoverySnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
+ module, ok := ctx.Module().(*Module)
+ 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
+
+func init() {
+ vendorSnapshotImageSingleton.init()
+ recoverySnapshotImageSingleton.init()
+}
+
+const (
+ vendorSnapshotHeaderSuffix = ".vendor_header."
+ vendorSnapshotSharedSuffix = ".vendor_shared."
+ vendorSnapshotStaticSuffix = ".vendor_static."
+ vendorSnapshotBinarySuffix = ".vendor_binary."
+ vendorSnapshotObjectSuffix = ".vendor_object."
+)
+
+const (
+ recoverySnapshotHeaderSuffix = ".recovery_header."
+ recoverySnapshotSharedSuffix = ".recovery_shared."
+ recoverySnapshotStaticSuffix = ".recovery_static."
+ recoverySnapshotBinarySuffix = ".recovery_binary."
+ recoverySnapshotObjectSuffix = ".recovery_object."
+)
+
+var (
+ vendorSnapshotsLock sync.Mutex
+ vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules")
+ vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
+ vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
+ vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
+ vendorSnapshotBinariesKey = android.NewOnceKey("vendorSnapshotBinaries")
+ vendorSnapshotObjectsKey = android.NewOnceKey("vendorSnapshotObjects")
+)
+
+var (
+ recoverySnapshotsLock sync.Mutex
+ recoverySuffixModulesKey = android.NewOnceKey("recoverySuffixModules")
+ recoverySnapshotHeaderLibsKey = android.NewOnceKey("recoverySnapshotHeaderLibs")
+ recoverySnapshotStaticLibsKey = android.NewOnceKey("recoverySnapshotStaticLibs")
+ recoverySnapshotSharedLibsKey = android.NewOnceKey("recoverySnapshotSharedLibs")
+ recoverySnapshotBinariesKey = android.NewOnceKey("recoverySnapshotBinaries")
+ recoverySnapshotObjectsKey = android.NewOnceKey("recoverySnapshotObjects")
+)
+
+// vendorSuffixModules holds names of modules whose vendor variants should have the vendor suffix.
+// This is determined by source modules, and then this will be used when exporting snapshot modules
+// to Makefile.
+//
+// For example, if libbase has "vendor_available: true", the name of core variant will be "libbase"
+// while the name of vendor variant will be "libbase.vendor". In such cases, the vendor snapshot of
+// "libbase" should be exported with the name "libbase.vendor".
+//
+// Refer to VendorSnapshotSourceMutator and makeLibName which use this.
+func vendorSuffixModules(config android.Config) map[string]bool {
+ return config.Once(vendorSuffixModulesKey, func() interface{} {
+ return make(map[string]bool)
+ }).(map[string]bool)
+}
+
+// these are vendor snapshot maps holding names of vendor snapshot modules
+func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotBinaries(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotBinariesKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func vendorSnapshotObjects(config android.Config) *snapshotMap {
+ return config.Once(vendorSnapshotObjectsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func recoverySuffixModules(config android.Config) map[string]bool {
+ return config.Once(recoverySuffixModulesKey, func() interface{} {
+ return make(map[string]bool)
+ }).(map[string]bool)
+}
+
+func recoverySnapshotHeaderLibs(config android.Config) *snapshotMap {
+ return config.Once(recoverySnapshotHeaderLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func recoverySnapshotSharedLibs(config android.Config) *snapshotMap {
+ return config.Once(recoverySnapshotSharedLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func recoverySnapshotStaticLibs(config android.Config) *snapshotMap {
+ return config.Once(recoverySnapshotStaticLibsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func recoverySnapshotBinaries(config android.Config) *snapshotMap {
+ return config.Once(recoverySnapshotBinariesKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+func recoverySnapshotObjects(config android.Config) *snapshotMap {
+ return config.Once(recoverySnapshotObjectsKey, func() interface{} {
+ return newSnapshotMap()
+ }).(*snapshotMap)
+}
+
+type baseSnapshotDecoratorProperties struct {
+ // snapshot version.
+ Version string
+
+ // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
+ Target_arch string
+
+ // Suffix to be added to the module name, e.g., vendor_shared,
+ // recovery_shared, etc.
+ Module_suffix string
+}
+
+// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
+// version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
+// collide with source modules. e.g. the following example module,
+//
+// vendor_snapshot_static {
+// name: "libbase",
+// arch: "arm64",
+// version: 30,
+// ...
+// }
+//
+// will be seen as "libbase.vendor_static.30.arm64" by Soong.
+type baseSnapshotDecorator struct {
+ baseProperties baseSnapshotDecoratorProperties
+}
+
+func (p *baseSnapshotDecorator) Name(name string) string {
+ return name + p.NameSuffix()
+}
+
+func (p *baseSnapshotDecorator) NameSuffix() string {
+ versionSuffix := p.version()
+ if p.arch() != "" {
+ versionSuffix += "." + p.arch()
+ }
+
+ return p.baseProperties.Module_suffix + versionSuffix
+}
+
+func (p *baseSnapshotDecorator) version() string {
+ return p.baseProperties.Version
+}
+
+func (p *baseSnapshotDecorator) arch() string {
+ return p.baseProperties.Target_arch
+}
+
+func (p *baseSnapshotDecorator) module_suffix() string {
+ return p.baseProperties.Module_suffix
+}
+
+func (p *baseSnapshotDecorator) isSnapshotPrebuilt() bool {
+ return true
+}
+
+// Call this with a module suffix after creating a snapshot module, such as
+// vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
+func (p *baseSnapshotDecorator) init(m *Module, suffix string) {
+ p.baseProperties.Module_suffix = suffix
+ m.AddProperties(&p.baseProperties)
+ android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+ vendorSnapshotLoadHook(ctx, p)
+ })
+}
+
+// vendorSnapshotLoadHook disables snapshots if it's not BOARD_VNDK_VERSION.
+// As vendor snapshot is only for vendor, such modules won't be used at all.
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *baseSnapshotDecorator) {
+ if p.version() != ctx.DeviceConfig().VndkVersion() {
+ ctx.Module().Disable()
+ return
+ }
+}
+
+//
+// Module definitions for snapshots of libraries (shared, static, header).
+//
+// Modules (vendor|recovery)_snapshot_(shared|static|header) are defined here. Shared libraries and
+// static libraries have their prebuilt library files (.so for shared, .a for static) as their src,
+// which can be installed or linked against. Also they export flags needed when linked, such as
+// include directories, c flags, sanitize dependency information, etc.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type snapshotLibraryProperties struct {
+ // Prebuilt file for each arch.
+ Src *string `android:"arch_variant"`
+
+ // list of directories that will be added to the include path (using -I).
+ Export_include_dirs []string `android:"arch_variant"`
+
+ // list of directories that will be added to the system path (using -isystem).
+ Export_system_include_dirs []string `android:"arch_variant"`
+
+ // list of flags that will be used for any module that links against this module.
+ Export_flags []string `android:"arch_variant"`
+
+ // Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
+ Sanitize_ubsan_dep *bool `android:"arch_variant"`
+
+ // Whether this prebuilt needs to depend on sanitize minimal runtime or not.
+ Sanitize_minimal_dep *bool `android:"arch_variant"`
+}
+
+type snapshotSanitizer interface {
+ isSanitizerEnabled(t SanitizerType) bool
+ setSanitizerVariation(t SanitizerType, enabled bool)
+}
+
+type snapshotLibraryDecorator struct {
+ baseSnapshotDecorator
+ *libraryDecorator
+ properties snapshotLibraryProperties
+ sanitizerProperties struct {
+ CfiEnabled bool `blueprint:"mutated"`
+
+ // Library flags for cfi variant.
+ Cfi snapshotLibraryProperties `android:"arch_variant"`
+ }
+ androidMkSuffix string
+}
+
+func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+ p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
+ return p.libraryDecorator.linkerFlags(ctx, flags)
+}
+
+func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+ arches := config.Arches()
+ if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+ return false
+ }
+ if !p.header() && p.properties.Src == nil {
+ return false
+ }
+ return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
+// done by normal library decorator, e.g. exporting flags.
+func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ m := ctx.Module().(*Module)
+
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = vendorSuffix
+ } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = recoverySuffix
+ }
+
+ if p.header() {
+ return p.libraryDecorator.link(ctx, flags, deps, objs)
+ }
+
+ if p.sanitizerProperties.CfiEnabled {
+ p.properties = p.sanitizerProperties.Cfi
+ }
+
+ if !p.matchesWithDevice(ctx.DeviceConfig()) {
+ return nil
+ }
+
+ p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
+ p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
+ p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
+
+ in := android.PathForModuleSrc(ctx, *p.properties.Src)
+ p.unstrippedOutputFile = in
+
+ if p.shared() {
+ libName := in.Base()
+ builderFlags := flagsToBuilderFlags(flags)
+
+ // Optimize out relinking against shared libraries whose interface hasn't changed by
+ // depending on a table of contents file instead of the library itself.
+ tocFile := android.PathForModuleOut(ctx, libName+".toc")
+ p.tocFile = android.OptionalPathForPath(tocFile)
+ transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+
+ ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+ SharedLibrary: in,
+ UnstrippedSharedLibrary: p.unstrippedOutputFile,
+
+ TableOfContents: p.tocFile,
+ })
+ }
+
+ if p.static() {
+ depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
+ ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
+ StaticLibrary: in,
+
+ TransitiveStaticLibrariesForOrdering: depSet,
+ })
+ }
+
+ p.libraryDecorator.flagExporter.setProvider(ctx)
+
+ return in
+}
+
+func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
+ if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+ p.baseInstaller.install(ctx, file)
+ }
+}
+
+func (p *snapshotLibraryDecorator) nativeCoverage() bool {
+ return false
+}
+
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t SanitizerType) bool {
+ switch t {
+ case cfi:
+ return p.sanitizerProperties.Cfi.Src != nil
+ default:
+ return false
+ }
+}
+
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t SanitizerType, enabled bool) {
+ if !enabled {
+ return
+ }
+ switch t {
+ case cfi:
+ p.sanitizerProperties.CfiEnabled = true
+ default:
+ return
+ }
+}
+
+func snapshotLibraryFactory(suffix string) (*Module, *snapshotLibraryDecorator) {
+ module, library := NewLibrary(android.DeviceSupported)
+
+ module.stl = nil
+ module.sanitize = nil
+ library.disableStripping()
+
+ prebuilt := &snapshotLibraryDecorator{
+ libraryDecorator: library,
+ }
+
+ prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if prebuilt.baseLinker.Properties.System_shared_libs == nil {
+ prebuilt.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ module.compiler = nil
+ module.linker = prebuilt
+ module.installer = prebuilt
+
+ prebuilt.init(module, suffix)
+ module.AddProperties(
+ &prebuilt.properties,
+ &prebuilt.sanitizerProperties,
+ )
+
+ return module, prebuilt
+}
+
+// vendor_snapshot_shared is a special prebuilt shared library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_shared
+// overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotSharedFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotSharedSuffix)
+ prebuilt.libraryDecorator.BuildOnlyShared()
+ return module.Init()
+}
+
+// recovery_snapshot_shared is a special prebuilt shared library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_shared
+// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotSharedFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotSharedSuffix)
+ prebuilt.libraryDecorator.BuildOnlyShared()
+ return module.Init()
+}
+
+// vendor_snapshot_static is a special prebuilt static library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_static
+// overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotStaticFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotStaticSuffix)
+ prebuilt.libraryDecorator.BuildOnlyStatic()
+ return module.Init()
+}
+
+// recovery_snapshot_static is a special prebuilt static library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_static
+// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotStaticFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotStaticSuffix)
+ prebuilt.libraryDecorator.BuildOnlyStatic()
+ return module.Init()
+}
+
+// vendor_snapshot_header is a special header library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_header
+// overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotHeaderFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(vendorSnapshotHeaderSuffix)
+ prebuilt.libraryDecorator.HeaderOnly()
+ return module.Init()
+}
+
+// recovery_snapshot_header is a special header library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_header
+// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotHeaderFactory() android.Module {
+ module, prebuilt := snapshotLibraryFactory(recoverySnapshotHeaderSuffix)
+ prebuilt.libraryDecorator.HeaderOnly()
+ return module.Init()
+}
+
+var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
+//
+// Module definitions for snapshots of executable binaries.
+//
+// Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
+// binaries (e.g. toybox, sh) as their src, which can be installed.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type snapshotBinaryProperties struct {
+ // Prebuilt file for each arch.
+ Src *string `android:"arch_variant"`
+}
+
+type snapshotBinaryDecorator struct {
+ baseSnapshotDecorator
+ *binaryDecorator
+ properties snapshotBinaryProperties
+ androidMkSuffix string
+}
+
+func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+ if config.DeviceArch() != p.arch() {
+ return false
+ }
+ if p.properties.Src == nil {
+ return false
+ }
+ return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary
+func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ if !p.matchesWithDevice(ctx.DeviceConfig()) {
+ return nil
+ }
+
+ in := android.PathForModuleSrc(ctx, *p.properties.Src)
+ p.unstrippedOutputFile = in
+ binName := in.Base()
+
+ m := ctx.Module().(*Module)
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = vendorSuffix
+ } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = recoverySuffix
+
+ }
+
+ // use cpExecutable to make it executable
+ outputFile := android.PathForModuleOut(ctx, binName)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.CpExecutable,
+ Description: "prebuilt",
+ Output: outputFile,
+ Input: in,
+ })
+
+ return outputFile
+}
+
+func (p *snapshotBinaryDecorator) nativeCoverage() bool {
+ return false
+}
+
+// vendor_snapshot_binary is a special prebuilt executable binary which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
+// overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
+func VendorSnapshotBinaryFactory() android.Module {
+ return snapshotBinaryFactory(vendorSnapshotBinarySuffix)
+}
+
+// recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
+// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
+func RecoverySnapshotBinaryFactory() android.Module {
+ return snapshotBinaryFactory(recoverySnapshotBinarySuffix)
+}
+
+func snapshotBinaryFactory(suffix string) android.Module {
+ module, binary := NewBinary(android.DeviceSupported)
+ binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
+ binary.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+ // Prevent default system libs (libc, libm, and libdl) from being linked
+ if binary.baseLinker.Properties.System_shared_libs == nil {
+ binary.baseLinker.Properties.System_shared_libs = []string{}
+ }
+
+ prebuilt := &snapshotBinaryDecorator{
+ binaryDecorator: binary,
+ }
+
+ module.compiler = nil
+ module.sanitize = nil
+ module.stl = nil
+ module.linker = prebuilt
+
+ prebuilt.init(module, suffix)
+ module.AddProperties(&prebuilt.properties)
+ return module.Init()
+}
+
+//
+// Module definitions for snapshots of object files (*.o).
+//
+// Modules (vendor|recovery)_snapshot_object are defined here. They have their prebuilt object
+// files (*.o) as their src.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type vendorSnapshotObjectProperties struct {
+ // Prebuilt file for each arch.
+ Src *string `android:"arch_variant"`
+}
+
+type snapshotObjectLinker struct {
+ baseSnapshotDecorator
+ objectLinker
+ properties vendorSnapshotObjectProperties
+ androidMkSuffix string
+}
+
+func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
+ if config.DeviceArch() != p.arch() {
+ return false
+ }
+ if p.properties.Src == nil {
+ return false
+ }
+ return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary
+func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+ if !p.matchesWithDevice(ctx.DeviceConfig()) {
+ return nil
+ }
+
+ m := ctx.Module().(*Module)
+
+ if m.InVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = vendorSuffix
+ } else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+ p.androidMkSuffix = recoverySuffix
+ }
+
+ return android.PathForModuleSrc(ctx, *p.properties.Src)
+}
+
+func (p *snapshotObjectLinker) nativeCoverage() bool {
+ return false
+}
+
+// vendor_snapshot_object is a special prebuilt compiled object file which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object
+// overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
+func VendorSnapshotObjectFactory() android.Module {
+ module := newObject()
+
+ prebuilt := &snapshotObjectLinker{
+ objectLinker: objectLinker{
+ baseLinker: NewBaseLinker(nil),
+ },
+ }
+ module.linker = prebuilt
+
+ prebuilt.init(module, vendorSnapshotObjectSuffix)
+ module.AddProperties(&prebuilt.properties)
+ return module.Init()
+}
+
+// recovery_snapshot_object is a special prebuilt compiled object file which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object
+// overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
+func RecoverySnapshotObjectFactory() android.Module {
+ module := newObject()
+
+ prebuilt := &snapshotObjectLinker{
+ objectLinker: objectLinker{
+ baseLinker: NewBaseLinker(nil),
+ },
+ }
+ module.linker = prebuilt
+
+ prebuilt.init(module, recoverySnapshotObjectSuffix)
+ module.AddProperties(&prebuilt.properties)
+ return module.Init()
+}
+
+type snapshotInterface interface {
+ matchesWithDevice(config android.DeviceConfig) bool
+}
+
+var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
+var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
+var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
+var _ snapshotInterface = (*snapshotObjectLinker)(nil)
+
+//
+// Mutators that helps vendor snapshot modules override source modules.
+//
+
+// VendorSnapshotMutator gathers all snapshots for vendor, and disable all snapshots which don't
+// match with device, e.g.
+// - snapshot version is different with BOARD_VNDK_VERSION
+// - snapshot arch is different with device's arch (e.g. arm vs x86)
+//
+// This also handles vndk_prebuilt_shared, except for they won't be disabled in any cases, given
+// that any versions of VNDK might be packed into vndk APEX.
+//
+// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
+func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
+ snapshotMutator(ctx, vendorSnapshotImageSingleton)
+}
+
+func RecoverySnapshotMutator(ctx android.BottomUpMutatorContext) {
+ snapshotMutator(ctx, recoverySnapshotImageSingleton)
+}
+
+func snapshotMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
+ if !image.isUsingSnapshot(ctx.DeviceConfig()) {
+ return
+ }
+ module, ok := ctx.Module().(*Module)
+ if !ok || !module.Enabled() {
+ return
+ }
+ if image.skipModuleMutator(ctx) {
+ return
+ }
+ if !module.isSnapshotPrebuilt() {
+ return
+ }
+
+ // isSnapshotPrebuilt ensures snapshotInterface
+ if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
+ // Disable unnecessary snapshot module, but do not disable
+ // vndk_prebuilt_shared because they might be packed into vndk APEX
+ if !module.IsVndk() {
+ module.Disable()
+ }
+ return
+ }
+
+ var snapshotMap *snapshotMap = image.getSnapshotMap(module, ctx.Config())
+ if snapshotMap == nil {
+ return
+ }
+
+ mutex := image.getMutex()
+ mutex.Lock()
+ defer mutex.Unlock()
+ snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
+}
+
+// VendorSnapshotSourceMutator disables source modules which have corresponding snapshots.
+func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+ snapshotSourceMutator(ctx, vendorSnapshotImageSingleton)
+}
+
+func RecoverySnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+ snapshotSourceMutator(ctx, recoverySnapshotImageSingleton)
+}
+
+func snapshotSourceMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
+ if !ctx.Device() {
+ return
+ }
+ if !image.isUsingSnapshot(ctx.DeviceConfig()) {
+ return
+ }
+
+ module, ok := ctx.Module().(*Module)
+ if !ok {
+ return
+ }
+
+ if image.shouldBeAddedToSuffixModules(module) {
+ mutex := image.getMutex()
+ mutex.Lock()
+ defer mutex.Unlock()
+
+ image.suffixModules(ctx.Config())[ctx.ModuleName()] = true
+ }
+
+ if module.isSnapshotPrebuilt() {
+ return
+ }
+ if image.skipSourceMutator(ctx) {
+ return
+ }
+
+ var snapshotMap *snapshotMap = image.getSnapshotMap(module, ctx.Config())
+ if snapshotMap == nil {
+ return
+ }
+
+ if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
+ // Corresponding snapshot doesn't exist
+ return
+ }
+
+ // Disables source modules if corresponding snapshot exists.
+ if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
+ // But do not disable because the shared variant depends on the static variant.
+ module.HideFromMake()
+ module.Properties.HideFromMake = true
+ } else {
+ module.Disable()
+ }
+}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index a3d52e6..c50ef45 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -13,6 +13,8 @@
// limitations under the License.
package cc
+// This file contains utility types and functions for VNDK / vendor snapshot.
+
import (
"android/soong/android"
)
@@ -21,15 +23,24 @@
headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
)
+// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
type snapshotLibraryInterface interface {
libraryInterface
+
+ // collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
+ // modules (See isSnapshotAware below).
+ // This function should gather all headers needed for snapshot.
collectHeadersForSnapshot(ctx android.ModuleContext)
+
+ // snapshotHeaders should return collected headers by collectHeadersForSnapshot.
+ // Calling snapshotHeaders before collectHeadersForSnapshot is an error.
snapshotHeaders() android.Paths
}
var _ snapshotLibraryInterface = (*prebuiltLibraryLinker)(nil)
var _ snapshotLibraryInterface = (*libraryDecorator)(nil)
+// snapshotMap is a helper wrapper to a map from base module name to snapshot module name.
type snapshotMap struct {
snapshots map[string]string
}
@@ -57,43 +68,21 @@
return snapshot, found
}
-func isSnapshotAware(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool {
- if _, _, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m, apexInfo); ok {
+// shouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
+// If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions.
+func shouldCollectHeadersForSnapshot(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool {
+ if ctx.DeviceConfig().VndkVersion() != "current" &&
+ ctx.DeviceConfig().RecoverySnapshotVersion() != "current" {
+ return false
+ }
+ if _, _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok {
return ctx.Config().VndkSnapshotBuildArtifacts()
- } else if isVendorSnapshotModule(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) ||
- isRecoverySnapshotModule(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) {
- return true
+ }
+
+ for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+ if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir()), apexInfo, image) {
+ return true
+ }
}
return false
}
-
-func copyFile(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: path,
- Output: outPath,
- Description: "Cp " + out,
- Args: map[string]string{
- "cpFlags": "-f -L",
- },
- })
- return outPath
-}
-
-func combineNotices(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cat,
- Inputs: paths,
- Output: outPath,
- Description: "combine notices for " + out,
- })
- return outPath
-}
-
-func writeStringToFile(ctx android.SingletonContext, content, out string) android.OutputPath {
- outPath := android.PathForOutput(ctx, out)
- android.WriteFileRule(ctx, outPath, content)
- return outPath
-}
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/strip.go b/cc/strip.go
index 1f10a74..b1f34bb 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -23,19 +23,23 @@
// StripProperties defines the type of stripping applied to the module.
type StripProperties struct {
Strip struct {
- // whether to disable all stripping.
+ // none forces all stripping to be disabled.
+ // Device modules default to stripping enabled leaving mini debuginfo.
+ // Host modules default to stripping disabled, but can be enabled by setting any other
+ // strip boolean property.
None *bool `android:"arch_variant"`
- // whether to strip everything, including the mini debug info.
+ // all forces stripping everything, including the mini debug info.
All *bool `android:"arch_variant"`
- // whether to keep the symbols.
+ // keep_symbols enables stripping but keeps all symbols.
Keep_symbols *bool `android:"arch_variant"`
- // keeps only the symbols defined here.
+ // keep_symbols_list specifies a list of symbols to keep if keep_symbols is enabled.
+ // If it is unset then all symbols are kept.
Keep_symbols_list []string `android:"arch_variant"`
- // whether to keep the symbols and the debug frames.
+ // keep_symbols_and_debug_frame enables stripping but keeps all symbols and debug frames.
Keep_symbols_and_debug_frame *bool `android:"arch_variant"`
} `android:"arch_variant"`
}
@@ -47,8 +51,12 @@
// NeedsStrip determines if stripping is required for a module.
func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool {
- // TODO(ccross): enable host stripping when Kati is enabled? Make never had support for stripping host binaries.
- return (!actx.Config().KatiEnabled() || actx.Device()) && !Bool(stripper.StripProperties.Strip.None)
+ forceDisable := Bool(stripper.StripProperties.Strip.None)
+ defaultEnable := (!actx.Config().KatiEnabled() || actx.Device())
+ forceEnable := Bool(stripper.StripProperties.Strip.All) ||
+ Bool(stripper.StripProperties.Strip.Keep_symbols) ||
+ Bool(stripper.StripProperties.Strip.Keep_symbols_and_debug_frame)
+ return !forceDisable && (forceEnable || defaultEnable)
}
func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
diff --git a/cc/test.go b/cc/test.go
index 3772691..f715a8d 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -29,11 +29,6 @@
// if set, use the isolated gtest runner. Defaults to false.
Isolated *bool
-
- // List of APEXes that this module tests. The module has access to
- // the private part of the listed APEXes even when it is not included in the
- // APEXes.
- Test_for []string
}
// Test option struct.
@@ -216,15 +211,15 @@
// name or even their number.
testNames = append(testNames, "")
tests := mctx.CreateLocalVariations(testNames...)
- all_tests := tests[numTests]
- all_tests.(*Module).linker.(testPerSrc).unsetSrc()
+ allTests := tests[numTests]
+ allTests.(*Module).linker.(testPerSrc).unsetSrc()
// Prevent the "all tests" variation from being installable nor
// exporting to Make, as it won't create any output file.
- all_tests.(*Module).Properties.PreventInstall = true
- all_tests.(*Module).Properties.HideFromMake = true
+ allTests.(*Module).Properties.PreventInstall = true
+ allTests.(*Module).Properties.HideFromMake = true
for i, src := range test.srcs() {
tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
- mctx.AddInterVariantDependency(testPerSrcDepTag, all_tests, tests[i])
+ mctx.AddInterVariantDependency(testPerSrcDepTag, allTests, tests[i])
}
mctx.AliasVariation("")
}
@@ -241,8 +236,8 @@
return BoolDefault(test.Properties.Gtest, true)
}
-func (test *testDecorator) testFor() []string {
- return test.Properties.Test_for
+func (test *testDecorator) testBinary() bool {
+ return true
}
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
@@ -378,9 +373,9 @@
}
})
- var api_level_prop string
+ var apiLevelProp string
var configs []tradefed.Config
- var min_level string
+ var minLevel string
for _, module := range test.Properties.Test_mainline_modules {
configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
}
@@ -407,16 +402,16 @@
if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
} else if test.Properties.Test_min_api_level != nil {
- api_level_prop = "ro.product.first_api_level"
- min_level = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
+ apiLevelProp = "ro.product.first_api_level"
+ minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
} else if test.Properties.Test_min_sdk_version != nil {
- api_level_prop = "ro.build.version.sdk"
- min_level = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
+ apiLevelProp = "ro.build.version.sdk"
+ minLevel = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
}
- if api_level_prop != "" {
+ if apiLevelProp != "" {
var options []tradefed.Option
- options = append(options, tradefed.Option{Name: "min-api-level", Value: min_level})
- options = append(options, tradefed.Option{Name: "api-level-prop", Value: api_level_prop})
+ options = append(options, tradefed.Option{Name: "min-api-level", Value: minLevel})
+ options = append(options, tradefed.Option{Name: "api-level-prop", Value: apiLevelProp})
configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
}
diff --git a/cc/testing.go b/cc/testing.go
index 95a93a0..dc9a59d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -29,6 +29,7 @@
ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
ctx.RegisterModuleType("llndk_library", LlndkLibraryFactory)
+ ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)
ctx.RegisterModuleType("cc_genrule", genRuleFactory)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
@@ -109,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,
@@ -298,7 +309,7 @@
llndk_library {
name: "libft2.llndk",
symbol_file: "",
- vendor_available: false,
+ private: true,
sdk_version: "current",
}
cc_library {
@@ -334,7 +345,7 @@
},
apex_available: [
"//apex_available:platform",
- "myapex"
+ "//apex_available:anyapex",
],
}
cc_library {
@@ -437,6 +448,21 @@
ndk_prebuilt_shared_stl {
name: "ndk_libc++_shared",
}
+
+ cc_library_static {
+ name: "libgoogle-benchmark",
+ sdk_version: "current",
+ stl: "none",
+ system_shared_libs: [],
+ }
+
+ cc_library_static {
+ name: "note_memtag_heap_async",
+ }
+
+ cc_library_static {
+ name: "note_memtag_heap_sync",
+ }
`
supportLinuxBionic := false
@@ -560,15 +586,16 @@
ctx.RegisterModuleType("vendor_public_library", vendorPublicLibraryFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
- ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+ RegisterVndkLibraryTxtTypes(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
android.RegisterPrebuiltMutators(ctx)
RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
diff --git a/cc/tidy.go b/cc/tidy.go
index 17471e6..972ad7b 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
package cc
import (
+ "regexp"
"strings"
"github.com/google/blueprint/proptools"
@@ -130,7 +131,31 @@
tidyChecks = tidyChecks + ",-bugprone-branch-clone"
flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
- if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
+ if ctx.Config().IsEnvTrue("WITH_TIDY") {
+ // WITH_TIDY=1 enables clang-tidy globally. There could be many unexpected
+ // warnings from new checks and many local tidy_checks_as_errors and
+ // -warnings-as-errors can break a global build.
+ // So allow all clang-tidy warnings.
+ inserted := false
+ for i, s := range flags.TidyFlags {
+ if strings.Contains(s, "-warnings-as-errors=") {
+ // clang-tidy accepts only one -warnings-as-errors
+ // replace the old one
+ re := regexp.MustCompile(`'?-?-warnings-as-errors=[^ ]* *`)
+ newFlag := re.ReplaceAllString(s, "")
+ if newFlag == "" {
+ flags.TidyFlags[i] = "-warnings-as-errors=-*"
+ } else {
+ flags.TidyFlags[i] = newFlag + " -warnings-as-errors=-*"
+ }
+ inserted = true
+ break
+ }
+ }
+ if !inserted {
+ flags.TidyFlags = append(flags.TidyFlags, "-warnings-as-errors=-*")
+ }
+ } else if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
tidyChecksAsErrors := "-warnings-as-errors=" + strings.Join(esc(tidy.Properties.Tidy_checks_as_errors), ",")
flags.TidyFlags = append(flags.TidyFlags, tidyChecksAsErrors)
}
diff --git a/cc/util.go b/cc/util.go
index 40374bf..1220d84 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -125,3 +125,52 @@
return "mkdir -p " + dir + " && " +
"ln -sf " + target + " " + filepath.Join(dir, linkName)
}
+
+func copyFileRule(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ outPath := android.PathForOutput(ctx, out)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: path,
+ Output: outPath,
+ Description: "copy " + path.String() + " -> " + out,
+ Args: map[string]string{
+ "cpFlags": "-f -L",
+ },
+ })
+ return outPath
+}
+
+func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
+ outPath := android.PathForOutput(ctx, out)
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cat,
+ Inputs: paths,
+ Output: outPath,
+ Description: "combine notices for " + out,
+ })
+ return outPath
+}
+
+func writeStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
+ outPath := android.PathForOutput(ctx, out)
+ android.WriteFileRule(ctx, outPath, content)
+ return outPath
+}
+
+// Dump a map to a list file as:
+//
+// {key1} {value1}
+// {key2} {value2}
+// ...
+func installMapListFileRule(ctx android.SingletonContext, m map[string]string, path string) android.OutputPath {
+ var txtBuilder strings.Builder
+ for idx, k := range android.SortedStringKeys(m) {
+ if idx > 0 {
+ txtBuilder.WriteString("\n")
+ }
+ txtBuilder.WriteString(k)
+ txtBuilder.WriteString(" ")
+ txtBuilder.WriteString(m[k])
+ }
+ return writeStringToFileRule(ctx, txtBuilder.String(), path)
+}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 3ef0b62..7346aac 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -13,625 +13,35 @@
// limitations under the License.
package cc
+// This file contains singletons to capture vendor and recovery snapshot. They consist of prebuilt
+// modules under AOSP so older vendor and recovery can be built with a newer system in a single
+// source tree.
+
import (
"encoding/json"
"path/filepath"
"sort"
"strings"
- "sync"
-
- "github.com/google/blueprint/proptools"
"android/soong/android"
)
-// Defines the specifics of different images to which the snapshot process is
-// applicable, e.g., vendor, recovery, ramdisk.
-type image interface {
- // Used to register callbacks with the build system.
- init()
-
- // Function that returns true if the module is included in this image.
- // Using a function return instead of a value to prevent early
- // evalution of a function that may be not be defined.
- inImage(m *Module) func() bool
-
- // Returns the value of the "available" property for a given module for
- // and snapshot, e.g., "vendor_available", "recovery_available", etc.
- // or nil if the property is not defined.
- available(m *Module) *bool
-
- // Returns true if a dir under source tree is an SoC-owned proprietary
- // directory, such as device/, vendor/, etc.
- //
- // For a given snapshot (e.g., vendor, recovery, etc.) if
- // isProprietaryPath(dir) returns true, then the module in dir will be
- // built from sources.
- isProprietaryPath(dir string) bool
-
- // Whether to include VNDK in the snapshot for this image.
- includeVndk() bool
-
- // Whether a given module has been explicitly excluded from the
- // snapshot, e.g., using the exclude_from_vendor_snapshot or
- // exclude_from_recovery_snapshot properties.
- excludeFromSnapshot(m *Module) bool
-}
-
-type vendorImage struct{}
-type recoveryImage struct{}
-
-func (vendorImage) init() {
- android.RegisterSingletonType(
- "vendor-snapshot", VendorSnapshotSingleton)
- android.RegisterModuleType(
- "vendor_snapshot_shared", VendorSnapshotSharedFactory)
- android.RegisterModuleType(
- "vendor_snapshot_static", VendorSnapshotStaticFactory)
- android.RegisterModuleType(
- "vendor_snapshot_header", VendorSnapshotHeaderFactory)
- android.RegisterModuleType(
- "vendor_snapshot_binary", VendorSnapshotBinaryFactory)
- android.RegisterModuleType(
- "vendor_snapshot_object", VendorSnapshotObjectFactory)
-}
-
-func (vendorImage) inImage(m *Module) func() bool {
- return m.inVendor
-}
-
-func (vendorImage) available(m *Module) *bool {
- return m.VendorProperties.Vendor_available
-}
-
-func (vendorImage) isProprietaryPath(dir string) bool {
- return isVendorProprietaryPath(dir)
-}
-
-func (vendorImage) includeVndk() bool {
- return true
-}
-
-func (vendorImage) excludeFromSnapshot(m *Module) bool {
- return m.ExcludeFromVendorSnapshot()
-}
-
-func (recoveryImage) init() {
- android.RegisterSingletonType(
- "recovery-snapshot", RecoverySnapshotSingleton)
- android.RegisterModuleType(
- "recovery_snapshot_shared", RecoverySnapshotSharedFactory)
- android.RegisterModuleType(
- "recovery_snapshot_static", RecoverySnapshotStaticFactory)
- android.RegisterModuleType(
- "recovery_snapshot_header", RecoverySnapshotHeaderFactory)
- android.RegisterModuleType(
- "recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
- android.RegisterModuleType(
- "recovery_snapshot_object", RecoverySnapshotObjectFactory)
-}
-
-func (recoveryImage) inImage(m *Module) func() bool {
- return m.InRecovery
-}
-
-func (recoveryImage) available(m *Module) *bool {
- return m.Properties.Recovery_available
-}
-
-func (recoveryImage) isProprietaryPath(dir string) bool {
- return isRecoveryProprietaryPath(dir)
-}
-
-func (recoveryImage) includeVndk() bool {
- return false
-}
-
-func (recoveryImage) excludeFromSnapshot(m *Module) bool {
- return m.ExcludeFromRecoverySnapshot()
-}
-
-var vendorImageSingleton vendorImage
-var recoveryImageSingleton recoveryImage
-
-const (
- vendorSnapshotHeaderSuffix = ".vendor_header."
- vendorSnapshotSharedSuffix = ".vendor_shared."
- vendorSnapshotStaticSuffix = ".vendor_static."
- vendorSnapshotBinarySuffix = ".vendor_binary."
- vendorSnapshotObjectSuffix = ".vendor_object."
-)
-
-const (
- recoverySnapshotHeaderSuffix = ".recovery_header."
- recoverySnapshotSharedSuffix = ".recovery_shared."
- recoverySnapshotStaticSuffix = ".recovery_static."
- recoverySnapshotBinarySuffix = ".recovery_binary."
- recoverySnapshotObjectSuffix = ".recovery_object."
-)
-
-var (
- vendorSnapshotsLock sync.Mutex
- vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules")
- vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
- vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
- vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
- vendorSnapshotBinariesKey = android.NewOnceKey("vendorSnapshotBinaries")
- vendorSnapshotObjectsKey = android.NewOnceKey("vendorSnapshotObjects")
-)
-
-// vendor snapshot maps hold names of vendor snapshot modules per arch
-func vendorSuffixModules(config android.Config) map[string]bool {
- return config.Once(vendorSuffixModulesKey, func() interface{} {
- return make(map[string]bool)
- }).(map[string]bool)
-}
-
-func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
-}
-
-func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
-}
-
-func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
-}
-
-func vendorSnapshotBinaries(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotBinariesKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
-}
-
-func vendorSnapshotObjects(config android.Config) *snapshotMap {
- return config.Once(vendorSnapshotObjectsKey, func() interface{} {
- return newSnapshotMap()
- }).(*snapshotMap)
-}
-
-type vendorSnapshotBaseProperties struct {
- // snapshot version.
- Version string
-
- // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
- Target_arch string
-}
-
-// vendorSnapshotModuleBase provides common basic functions for all snapshot modules.
-type vendorSnapshotModuleBase struct {
- baseProperties vendorSnapshotBaseProperties
- moduleSuffix string
-}
-
-func (p *vendorSnapshotModuleBase) Name(name string) string {
- return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotModuleBase) NameSuffix() string {
- versionSuffix := p.version()
- if p.arch() != "" {
- versionSuffix += "." + p.arch()
- }
-
- return p.moduleSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotModuleBase) version() string {
- return p.baseProperties.Version
-}
-
-func (p *vendorSnapshotModuleBase) arch() string {
- return p.baseProperties.Target_arch
-}
-
-func (p *vendorSnapshotModuleBase) isSnapshotPrebuilt() bool {
- return true
-}
-
-// Call this after creating a snapshot module with module suffix
-// such as vendorSnapshotSharedSuffix
-func (p *vendorSnapshotModuleBase) init(m *Module, suffix string) {
- p.moduleSuffix = suffix
- m.AddProperties(&p.baseProperties)
- android.AddLoadHook(m, func(ctx android.LoadHookContext) {
- vendorSnapshotLoadHook(ctx, p)
- })
-}
-
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *vendorSnapshotModuleBase) {
- if p.version() != ctx.DeviceConfig().VndkVersion() {
- ctx.Module().Disable()
- return
- }
-}
-
-type snapshotLibraryProperties struct {
- // Prebuilt file for each arch.
- Src *string `android:"arch_variant"`
-
- // list of directories that will be added to the include path (using -I).
- Export_include_dirs []string `android:"arch_variant"`
-
- // list of directories that will be added to the system path (using -isystem).
- Export_system_include_dirs []string `android:"arch_variant"`
-
- // list of flags that will be used for any module that links against this module.
- Export_flags []string `android:"arch_variant"`
-
- // Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
- Sanitize_ubsan_dep *bool `android:"arch_variant"`
-
- // Whether this prebuilt needs to depend on sanitize minimal runtime or not.
- Sanitize_minimal_dep *bool `android:"arch_variant"`
-}
-
-type snapshotSanitizer interface {
- isSanitizerEnabled(t sanitizerType) bool
- setSanitizerVariation(t sanitizerType, enabled bool)
-}
-
-type snapshotLibraryDecorator struct {
- vendorSnapshotModuleBase
- *libraryDecorator
- properties snapshotLibraryProperties
- sanitizerProperties struct {
- CfiEnabled bool `blueprint:"mutated"`
-
- // Library flags for cfi variant.
- Cfi snapshotLibraryProperties `android:"arch_variant"`
- }
- androidMkVendorSuffix bool
-}
-
-func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
- p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
- return p.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
- arches := config.Arches()
- if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
- return false
- }
- if !p.header() && p.properties.Src == nil {
- return false
- }
- return true
-}
-
-func (p *snapshotLibraryDecorator) link(ctx ModuleContext,
- flags Flags, deps PathDeps, objs Objects) android.Path {
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
- if p.header() {
- return p.libraryDecorator.link(ctx, flags, deps, objs)
- }
-
- if p.sanitizerProperties.CfiEnabled {
- p.properties = p.sanitizerProperties.Cfi
- }
-
- if !p.matchesWithDevice(ctx.DeviceConfig()) {
- return nil
- }
-
- p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
- p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
- p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
-
- in := android.PathForModuleSrc(ctx, *p.properties.Src)
- p.unstrippedOutputFile = in
-
- if p.shared() {
- libName := in.Base()
- builderFlags := flagsToBuilderFlags(flags)
-
- // Optimize out relinking against shared libraries whose interface hasn't changed by
- // depending on a table of contents file instead of the library itself.
- tocFile := android.PathForModuleOut(ctx, libName+".toc")
- p.tocFile = android.OptionalPathForPath(tocFile)
- transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
-
- ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- SharedLibrary: in,
- UnstrippedSharedLibrary: p.unstrippedOutputFile,
-
- TableOfContents: p.tocFile,
- })
- }
-
- if p.static() {
- depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(in).Build()
- ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
- StaticLibrary: in,
-
- TransitiveStaticLibrariesForOrdering: depSet,
- })
- }
-
- p.libraryDecorator.flagExporter.setProvider(ctx)
-
- return in
-}
-
-func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
- if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
- p.baseInstaller.install(ctx, file)
- }
-}
-
-func (p *snapshotLibraryDecorator) nativeCoverage() bool {
- return false
-}
-
-func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
- switch t {
- case cfi:
- return p.sanitizerProperties.Cfi.Src != nil
- default:
- return false
- }
-}
-
-func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
- if !enabled {
- return
- }
- switch t {
- case cfi:
- p.sanitizerProperties.CfiEnabled = true
- default:
- return
- }
-}
-
-func snapshotLibrary(suffix string) (*Module, *snapshotLibraryDecorator) {
- module, library := NewLibrary(android.DeviceSupported)
-
- module.stl = nil
- module.sanitize = nil
- library.disableStripping()
-
- prebuilt := &snapshotLibraryDecorator{
- libraryDecorator: library,
- }
-
- prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
- prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
-
- // Prevent default system libs (libc, libm, and libdl) from being linked
- if prebuilt.baseLinker.Properties.System_shared_libs == nil {
- prebuilt.baseLinker.Properties.System_shared_libs = []string{}
- }
-
- module.compiler = nil
- module.linker = prebuilt
- module.installer = prebuilt
-
- prebuilt.init(module, suffix)
- module.AddProperties(
- &prebuilt.properties,
- &prebuilt.sanitizerProperties,
- )
-
- return module, prebuilt
-}
-
-func VendorSnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibrary(vendorSnapshotSharedSuffix)
- prebuilt.libraryDecorator.BuildOnlyShared()
- return module.Init()
-}
-
-func RecoverySnapshotSharedFactory() android.Module {
- module, prebuilt := snapshotLibrary(recoverySnapshotSharedSuffix)
- prebuilt.libraryDecorator.BuildOnlyShared()
- return module.Init()
-}
-
-func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibrary(vendorSnapshotStaticSuffix)
- prebuilt.libraryDecorator.BuildOnlyStatic()
- return module.Init()
-}
-
-func RecoverySnapshotStaticFactory() android.Module {
- module, prebuilt := snapshotLibrary(recoverySnapshotStaticSuffix)
- prebuilt.libraryDecorator.BuildOnlyStatic()
- return module.Init()
-}
-
-func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibrary(vendorSnapshotHeaderSuffix)
- prebuilt.libraryDecorator.HeaderOnly()
- return module.Init()
-}
-
-func RecoverySnapshotHeaderFactory() android.Module {
- module, prebuilt := snapshotLibrary(recoverySnapshotHeaderSuffix)
- prebuilt.libraryDecorator.HeaderOnly()
- return module.Init()
-}
-
-var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
-
-type snapshotBinaryProperties struct {
- // Prebuilt file for each arch.
- Src *string `android:"arch_variant"`
-}
-
-type snapshotBinaryDecorator struct {
- vendorSnapshotModuleBase
- *binaryDecorator
- properties snapshotBinaryProperties
- androidMkVendorSuffix bool
-}
-
-func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
- if config.DeviceArch() != p.arch() {
- return false
- }
- if p.properties.Src == nil {
- return false
- }
- return true
-}
-
-func (p *snapshotBinaryDecorator) link(ctx ModuleContext,
- flags Flags, deps PathDeps, objs Objects) android.Path {
- if !p.matchesWithDevice(ctx.DeviceConfig()) {
- return nil
- }
-
- in := android.PathForModuleSrc(ctx, *p.properties.Src)
- stripFlags := flagsToStripFlags(flags)
- p.unstrippedOutputFile = in
- binName := in.Base()
- if p.stripper.NeedsStrip(ctx) {
- stripped := android.PathForModuleOut(ctx, "stripped", binName)
- p.stripper.StripExecutableOrSharedLib(ctx, in, stripped, stripFlags)
- in = stripped
- }
-
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
- // use cpExecutable to make it executable
- outputFile := android.PathForModuleOut(ctx, binName)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.CpExecutable,
- Description: "prebuilt",
- Output: outputFile,
- Input: in,
- })
-
- return outputFile
-}
-
-func (p *snapshotBinaryDecorator) nativeCoverage() bool {
- return false
-}
-
-func VendorSnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(vendorSnapshotBinarySuffix)
-}
-
-func RecoverySnapshotBinaryFactory() android.Module {
- return snapshotBinaryFactory(recoverySnapshotBinarySuffix)
-}
-
-func snapshotBinaryFactory(suffix string) android.Module {
- module, binary := NewBinary(android.DeviceSupported)
- binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
- binary.baseLinker.Properties.Nocrt = BoolPtr(true)
-
- // Prevent default system libs (libc, libm, and libdl) from being linked
- if binary.baseLinker.Properties.System_shared_libs == nil {
- binary.baseLinker.Properties.System_shared_libs = []string{}
- }
-
- prebuilt := &snapshotBinaryDecorator{
- binaryDecorator: binary,
- }
-
- module.compiler = nil
- module.sanitize = nil
- module.stl = nil
- module.linker = prebuilt
-
- prebuilt.init(module, suffix)
- module.AddProperties(&prebuilt.properties)
- return module.Init()
-}
-
-type vendorSnapshotObjectProperties struct {
- // Prebuilt file for each arch.
- Src *string `android:"arch_variant"`
-}
-
-type snapshotObjectLinker struct {
- vendorSnapshotModuleBase
- objectLinker
- properties vendorSnapshotObjectProperties
- androidMkVendorSuffix bool
-}
-
-func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
- if config.DeviceArch() != p.arch() {
- return false
- }
- if p.properties.Src == nil {
- return false
- }
- return true
-}
-
-func (p *snapshotObjectLinker) link(ctx ModuleContext,
- flags Flags, deps PathDeps, objs Objects) android.Path {
- if !p.matchesWithDevice(ctx.DeviceConfig()) {
- return nil
- }
-
- m := ctx.Module().(*Module)
- p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
- return android.PathForModuleSrc(ctx, *p.properties.Src)
-}
-
-func (p *snapshotObjectLinker) nativeCoverage() bool {
- return false
-}
-
-func VendorSnapshotObjectFactory() android.Module {
- module := newObject()
-
- prebuilt := &snapshotObjectLinker{
- objectLinker: objectLinker{
- baseLinker: NewBaseLinker(nil),
- },
- }
- module.linker = prebuilt
-
- prebuilt.init(module, vendorSnapshotObjectSuffix)
- module.AddProperties(&prebuilt.properties)
- return module.Init()
-}
-
-func RecoverySnapshotObjectFactory() android.Module {
- module := newObject()
-
- prebuilt := &snapshotObjectLinker{
- objectLinker: objectLinker{
- baseLinker: NewBaseLinker(nil),
- },
- }
- module.linker = prebuilt
-
- prebuilt.init(module, recoverySnapshotObjectSuffix)
- module.AddProperties(&prebuilt.properties)
- return module.Init()
-}
-
-func init() {
- vendorImageSingleton.init()
- recoveryImageSingleton.init()
-}
-
var vendorSnapshotSingleton = snapshotSingleton{
"vendor",
"SOONG_VENDOR_SNAPSHOT_ZIP",
android.OptionalPath{},
true,
- vendorImageSingleton,
+ vendorSnapshotImageSingleton,
+ false, /* fake */
+}
+
+var vendorFakeSnapshotSingleton = snapshotSingleton{
+ "vendor",
+ "SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ true,
+ vendorSnapshotImageSingleton,
+ true, /* fake */
}
var recoverySnapshotSingleton = snapshotSingleton{
@@ -639,13 +49,18 @@
"SOONG_RECOVERY_SNAPSHOT_ZIP",
android.OptionalPath{},
false,
- recoveryImageSingleton,
+ recoverySnapshotImageSingleton,
+ false, /* fake */
}
func VendorSnapshotSingleton() android.Singleton {
return &vendorSnapshotSingleton
}
+func VendorFakeSnapshotSingleton() android.Singleton {
+ return &vendorFakeSnapshotSingleton
+}
+
func RecoverySnapshotSingleton() android.Singleton {
return &recoverySnapshotSingleton
}
@@ -667,13 +82,17 @@
// Implementation of the image interface specific to the image
// associated with this snapshot (e.g., specific to the vendor image,
// recovery image, etc.).
- image image
+ image snapshotImage
+
+ // Whether this singleton is for fake snapshot or not.
+ // Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
+ // It is much faster to generate, and can be used to inspect dependencies.
+ fake bool
}
var (
// Modules under following directories are ignored. They are OEM's and vendor's
// proprietary modules(device/, kernel/, vendor/, and hardware/).
- // TODO(b/65377115): Clean up these with more maintainable way
vendorProprietaryDirs = []string{
"device",
"kernel",
@@ -683,9 +102,7 @@
// Modules under following directories are ignored. They are OEM's and vendor's
// proprietary modules(device/, kernel/, vendor/, and hardware/).
- // TODO(b/65377115): Clean up these with more maintainable way
recoveryProprietaryDirs = []string{
- "bootable/recovery",
"device",
"hardware",
"kernel",
@@ -694,7 +111,6 @@
// Modules under following directories are included as they are in AOSP,
// although hardware/ and kernel/ are normally for vendor's own.
- // TODO(b/65377115): Clean up these with more maintainable way
aospDirsUnderProprietary = []string{
"kernel/configs",
"kernel/prebuilts",
@@ -738,10 +154,8 @@
}
func isVendorProprietaryModule(ctx android.BaseModuleContext) bool {
-
// Any module in a vendor proprietary path is a vendor proprietary
// module.
-
if isVendorProprietaryPath(ctx.ModuleDir()) {
return true
}
@@ -750,7 +164,6 @@
// still be a vendor proprietary module. This happens for cc modules
// that are excluded from the vendor snapshot, and it means that the
// vendor has assumed control of the framework-provided module.
-
if c, ok := ctx.Module().(*Module); ok {
if c.ExcludeFromVendorSnapshot() {
return true
@@ -760,27 +173,36 @@
return false
}
-// Determine if a module is going to be included in vendor snapshot or not.
-//
-// Targets of vendor snapshot are "vendor: true" or "vendor_available: true" modules in
-// AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
-// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
-// image and newer system image altogether.
-func isVendorSnapshotModule(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotModule(m, inVendorProprietaryPath, apexInfo, vendorImageSingleton)
+func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
+
+ // 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 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 {
+ if c.ExcludeFromRecoverySnapshot() {
+ return true
+ }
+ }
+
+ return false
}
-func isRecoverySnapshotModule(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotModule(m, inRecoveryProprietaryPath, apexInfo, recoveryImageSingleton)
-}
-
-func isSnapshotModule(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image image) bool {
+// Determines if the module is a candidate for snapshot.
+func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
if !m.Enabled() || m.Properties.HideFromMake {
return false
}
// When android/prebuilt.go selects between source and prebuilt, it sets
- // SkipInstall on the other one to avoid duplicate install rules in make.
- if m.IsSkipInstall() {
+ // HideFromMake on the other one to avoid duplicate install rules in make.
+ if m.IsHideFromMake() {
return false
}
// skip proprietary modules, but (for the vendor snapshot only)
@@ -790,7 +212,7 @@
}
// If the module would be included based on its path, check to see if
// the module is marked to be excluded. If so, skip it.
- if m.ExcludeFromVendorSnapshot() {
+ if image.excludeFromSnapshot(m) {
return false
}
if m.Target().Os.Class != android.Device {
@@ -799,7 +221,7 @@
if m.Target().NativeBridge == android.NativeBridgeEnabled {
return false
}
- // the module must be installed in /vendor
+ // the module must be installed in target image
if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
return false
}
@@ -808,31 +230,39 @@
return false
}
// skip llndk_library and llndk_headers which are backward compatible
+ if m.IsLlndk() {
+ return false
+ }
if _, ok := m.linker.(*llndkStubDecorator); ok {
return false
}
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 {
- // TODO(b/65377115): add full support for sanitizer
if m.sanitize != nil {
// scs and hwasan export both sanitized and unsanitized variants for static and header
// Always use unsanitized variants of them.
- for _, t := range []sanitizerType{scs, hwasan} {
+ for _, t := range []SanitizerType{scs, hwasan} {
if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
return false
}
}
// cfi also exports both variants. But for static, we capture both.
+ // This is because cfi static libraries can't be linked from non-cfi modules,
+ // and vice versa. This isn't the case for scs and hwasan sanitizers.
if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
return false
}
}
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() {
@@ -842,7 +272,7 @@
if !m.IsVndk() {
return true
}
- return m.isVndkExt()
+ return m.IsVndkExt()
}
}
return true
@@ -850,15 +280,41 @@
// Binaries and Objects
if m.binary() || m.object() {
- return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
+ return m.outputFile.Valid()
}
return false
}
+// This is to be saved as .json files, which is for development/vendor_snapshot/update.py.
+// These flags become Android.bp snapshot module properties.
+type snapshotJsonFlags struct {
+ ModuleName string `json:",omitempty"`
+ RelativeInstallPath string `json:",omitempty"`
+
+ // library flags
+ ExportedDirs []string `json:",omitempty"`
+ ExportedSystemDirs []string `json:",omitempty"`
+ ExportedFlags []string `json:",omitempty"`
+ Sanitize string `json:",omitempty"`
+ SanitizeMinimalDep bool `json:",omitempty"`
+ SanitizeUbsanDep bool `json:",omitempty"`
+
+ // binary flags
+ Symlinks []string `json:",omitempty"`
+
+ // dependencies
+ SharedLibs []string `json:",omitempty"`
+ RuntimeLibs []string `json:",omitempty"`
+ Required []string `json:",omitempty"`
+
+ // extra config files
+ InitRc []string `json:",omitempty"`
+ VintfFragments []string `json:",omitempty"`
+}
+
func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- // BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
- if ctx.DeviceConfig().VndkVersion() != "current" {
+ if !c.image.shouldGenerateSnapshot(ctx) {
return
}
@@ -898,6 +354,11 @@
*/
snapshotDir := c.name + "-snapshot"
+ if c.fake {
+ // If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
+ // collision with real snapshot files
+ snapshotDir = filepath.Join("fake", snapshotDir)
+ }
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
includeDir := filepath.Join(snapshotArchDir, "include")
@@ -909,6 +370,17 @@
var headers android.Paths
+ copyFile := copyFileRule
+ if c.fake {
+ // All prebuilt binaries and headers are installed by copyFile function. This makes a fake
+ // snapshot just touch prebuilts and headers, rather than installing real files.
+ copyFile = func(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+ return writeStringToFileRule(ctx, "", out)
+ }
+ }
+
+ // installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
+ // For executables, init_rc and vintf_fragments files are also copied.
installSnapshot := func(m *Module) android.Paths {
targetArch := "arch-" + m.Target().Arch.ArchType.String()
if m.Target().Arch.ArchVariant != "" {
@@ -917,34 +389,11 @@
var ret android.Paths
- prop := struct {
- ModuleName string `json:",omitempty"`
- RelativeInstallPath string `json:",omitempty"`
-
- // library flags
- ExportedDirs []string `json:",omitempty"`
- ExportedSystemDirs []string `json:",omitempty"`
- ExportedFlags []string `json:",omitempty"`
- Sanitize string `json:",omitempty"`
- SanitizeMinimalDep bool `json:",omitempty"`
- SanitizeUbsanDep bool `json:",omitempty"`
-
- // binary flags
- Symlinks []string `json:",omitempty"`
-
- // dependencies
- SharedLibs []string `json:",omitempty"`
- RuntimeLibs []string `json:",omitempty"`
- Required []string `json:",omitempty"`
-
- // extra config files
- InitRc []string `json:",omitempty"`
- VintfFragments []string `json:",omitempty"`
- }{}
+ prop := snapshotJsonFlags{}
// Common properties among snapshots.
prop.ModuleName = ctx.ModuleName(m)
- if c.supportsVndkExt && m.isVndkExt() {
+ if c.supportsVndkExt && m.IsVndkExt() {
// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
if m.isVndkSp() {
prop.RelativeInstallPath = "vndk-sp"
@@ -1053,7 +502,7 @@
ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
return nil
}
- ret = append(ret, writeStringToFile(ctx, string(j), propOut))
+ ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
return ret
}
@@ -1068,31 +517,23 @@
inProprietaryPath := c.image.isProprietaryPath(moduleDir)
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if m.ExcludeFromVendorSnapshot() {
+ if c.image.excludeFromSnapshot(m) {
if inProprietaryPath {
// Error: exclude_from_vendor_snapshot applies
// to framework-path modules only.
ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
return
}
- if Bool(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 !isSnapshotModule(m, inProprietaryPath, apexInfo, c.image) {
+ if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
return
}
+ // installSnapshot installs prebuilts and json flag files
snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
+
+ // just gather headers and notice files here, because they are to be deduplicated
if l, ok := m.linker.(snapshotLibraryInterface); ok {
headers = append(headers, l.snapshotHeaders()...)
}
@@ -1103,16 +544,14 @@
// skip already copied notice file
if !installedNotices[noticeOut] {
installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNotices(
- ctx, m.NoticeFiles(), noticeOut))
+ snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut))
}
}
})
// install all headers after removing duplicates
for _, header := range android.FirstUniquePaths(headers) {
- snapshotOutputs = append(snapshotOutputs, copyFile(
- ctx, header, filepath.Join(includeDir, header.String())))
+ snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String())))
}
// All artifacts are ready. Sort them to normalize ninja and then zip.
@@ -1155,141 +594,3 @@
c.makeVar,
c.snapshotZipFile.String())
}
-
-type snapshotInterface interface {
- matchesWithDevice(config android.DeviceConfig) bool
-}
-
-var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*snapshotObjectLinker)(nil)
-
-// gathers all snapshot modules for vendor, and disable unnecessary snapshots
-// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
-func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- // don't need snapshot if current
- if vndkVersion == "current" || vndkVersion == "" {
- return
- }
-
- module, ok := ctx.Module().(*Module)
- if !ok || !module.Enabled() || module.VndkVersion() != vndkVersion {
- return
- }
-
- if !module.isSnapshotPrebuilt() {
- return
- }
-
- // isSnapshotPrebuilt ensures snapshotInterface
- if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
- // Disable unnecessary snapshot module, but do not disable
- // vndk_prebuilt_shared because they might be packed into vndk APEX
- if !module.IsVndk() {
- module.Disable()
- }
- return
- }
-
- var snapshotMap *snapshotMap
-
- if lib, ok := module.linker.(libraryInterface); ok {
- if lib.static() {
- snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
- } else if lib.shared() {
- snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
- } else {
- // header
- snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
- }
- } else if _, ok := module.linker.(*snapshotBinaryDecorator); ok {
- snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if _, ok := module.linker.(*snapshotObjectLinker); ok {
- snapshotMap = vendorSnapshotObjects(ctx.Config())
- } else {
- return
- }
-
- vendorSnapshotsLock.Lock()
- defer vendorSnapshotsLock.Unlock()
- snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
-}
-
-// Disables source modules which have snapshots
-func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
- if !ctx.Device() {
- return
- }
-
- vndkVersion := ctx.DeviceConfig().VndkVersion()
- // don't need snapshot if current
- if vndkVersion == "current" || vndkVersion == "" {
- return
- }
-
- module, ok := ctx.Module().(*Module)
- if !ok {
- return
- }
-
- // vendor suffix should be added to snapshots if the source module isn't vendor: true.
- if !module.SocSpecific() {
- // But we can't just check SocSpecific() since we already passed the image mutator.
- // Check ramdisk and recovery to see if we are real "vendor: true" module.
- ramdisk_available := module.InRamdisk() && !module.OnlyInRamdisk()
- vendor_ramdisk_available := module.InVendorRamdisk() && !module.OnlyInVendorRamdisk()
- recovery_available := module.InRecovery() && !module.OnlyInRecovery()
-
- if !ramdisk_available && !recovery_available && !vendor_ramdisk_available {
- vendorSnapshotsLock.Lock()
- defer vendorSnapshotsLock.Unlock()
-
- vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
- }
- }
-
- if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() {
- // only non-snapshot modules with BOARD_VNDK_VERSION
- return
- }
-
- // .. and also filter out llndk library
- if module.isLlndk(ctx.Config()) {
- return
- }
-
- var snapshotMap *snapshotMap
-
- if lib, ok := module.linker.(libraryInterface); ok {
- if lib.static() {
- snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
- } else if lib.shared() {
- snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
- } else {
- // header
- snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
- }
- } else if module.binary() {
- snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if module.object() {
- snapshotMap = vendorSnapshotObjects(ctx.Config())
- } else {
- return
- }
-
- if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
- // Corresponding snapshot doesn't exist
- return
- }
-
- // Disables source modules if corresponding snapshot exists.
- if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
- // But do not disable because the shared variant depends on the static variant.
- module.SkipInstall()
- module.Properties.HideFromMake = true
- } else {
- module.Disable()
- }
-}
diff --git a/cc/vndk.go b/cc/vndk.go
index d57cdf7..ff38db5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -21,7 +21,6 @@
"path/filepath"
"sort"
"strings"
- "sync"
"android/soong/android"
"android/soong/cc/config"
@@ -35,6 +34,7 @@
vndkCoreLibrariesTxt = "vndkcore.libraries.txt"
vndkSpLibrariesTxt = "vndksp.libraries.txt"
vndkPrivateLibrariesTxt = "vndkprivate.libraries.txt"
+ vndkProductLibrariesTxt = "vndkproduct.libraries.txt"
vndkUsingCoreVariantLibrariesTxt = "vndkcorevariant.libraries.txt"
)
@@ -45,6 +45,7 @@
vndkCoreLibrariesTxt,
vndkSpLibrariesTxt,
vndkPrivateLibrariesTxt,
+ vndkProductLibrariesTxt,
}
}
// Snapshot vndks have their own *.libraries.VER.txt files.
@@ -54,6 +55,7 @@
insertVndkVersion(vndkCoreLibrariesTxt, vndkVersion),
insertVndkVersion(vndkSpLibrariesTxt, vndkVersion),
insertVndkVersion(vndkPrivateLibrariesTxt, vndkVersion),
+ insertVndkVersion(vndkProductLibrariesTxt, vndkVersion),
}
}
@@ -78,6 +80,13 @@
// VNDK-SP or LL-NDK modules only.
Support_system_process *bool
+ // declared as a VNDK-private module.
+ // This module still creates the vendor and product variants refering
+ // to the `vendor_available: true` and `product_available: true`
+ // properties. However, it is only available to the other VNDK modules
+ // but not to the non-VNDK vendor or product modules.
+ Private *bool
+
// Extending another module
Extends *string
}
@@ -135,30 +144,24 @@
return
}
if !vndk.isVndk() {
- // Non-VNDK modules those installed to /vendor or /system/vendor
- // can't depend on modules marked with vendor_available: false;
- // or those installed to /product or /system/product can't depend
- // on modules marked with product_available: false.
- violation := false
- if lib, ok := to.linker.(*llndkStubDecorator); ok && !Bool(lib.Properties.Vendor_available) {
- violation = true
- } else {
- if _, ok := to.linker.(libraryInterface); ok && to.VendorProperties.Vendor_available != nil && !Bool(to.VendorProperties.Vendor_available) {
- // Vendor_available == nil && !Bool(Vendor_available) should be okay since
- // it means a vendor-only, or product-only library which is a valid dependency
- // for non-VNDK modules.
- violation = true
- }
- }
- if violation {
- ctx.ModuleErrorf("Vendor module that is not VNDK should not link to %q which is marked as `vendor_available: false`", to.Name())
+ // Non-VNDK modules those installed to /vendor, /system/vendor,
+ // /product or /system/product cannot depend on VNDK-private modules
+ // that include VNDK-core-private, VNDK-SP-private and LLNDK-private.
+ if to.IsVndkPrivate() {
+ ctx.ModuleErrorf("non-VNDK module should not link to %q which has `private: true`", to.Name())
}
}
if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
// Check only shared libraries.
- // Other (static and LL-NDK) libraries are allowed to link.
+ // Other (static) libraries are allowed to link.
return
}
+
+ if to.IsLlndk() {
+ // LL-NDK libraries are allowed to link
+ return
+ }
+
if !to.UseVndk() {
ctx.ModuleErrorf("(%s) should not link to %q which is not a vendor-available library",
vndk.typeName(), to.Name())
@@ -177,10 +180,9 @@
to.Name())
return
}
- // TODO(b/150902910): vndk-ext for product must check product_available.
- if !Bool(to.VendorProperties.Vendor_available) {
+ if to.IsVndkPrivate() {
ctx.ModuleErrorf(
- "`extends` refers module %q which does not have `vendor_available: true`",
+ "`extends` refers module %q which has `private: true`",
to.Name())
return
}
@@ -228,55 +230,59 @@
return nil
}
+type moduleListerFunc func(ctx android.SingletonContext) (moduleNames, fileNames []string)
+
var (
- vndkCoreLibrariesKey = android.NewOnceKey("vndkCoreLibrarires")
- vndkSpLibrariesKey = android.NewOnceKey("vndkSpLibrarires")
- llndkLibrariesKey = android.NewOnceKey("llndkLibrarires")
- vndkPrivateLibrariesKey = android.NewOnceKey("vndkPrivateLibrarires")
- vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
- vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
- vndkLibrariesLock sync.Mutex
+ llndkLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !isVestigialLLNDKModule(m) })
+ llndkLibrariesWithoutHWASAN = vndkModuleListRemover(llndkLibraries, "libclang_rt.hwasan-")
+ vndkSPLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
+ vndkCoreLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
+ vndkPrivateLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate && !isVestigialLLNDKModule(m) })
+ vndkProductLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKProduct })
+ vndkUsingCoreVariantLibraries = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKUsingCoreVariant })
)
-func vndkCoreLibraries(config android.Config) map[string]string {
- return config.Once(vndkCoreLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
+// vndkModuleLister takes a predicate that operates on a Module and returns a moduleListerFunc
+// that produces a list of module names and output file names for which the predicate returns true.
+func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
+ return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+ ctx.VisitAllModules(func(m android.Module) {
+ if c, ok := m.(*Module); ok && predicate(c) {
+ filename, err := getVndkFileName(c)
+ if err != nil {
+ ctx.ModuleErrorf(m, "%s", err)
+ }
+ moduleNames = append(moduleNames, ctx.ModuleName(m))
+ fileNames = append(fileNames, filename)
+ }
+ })
+ moduleNames = android.SortedUniqueStrings(moduleNames)
+ fileNames = android.SortedUniqueStrings(fileNames)
+ return
+ }
}
-func vndkSpLibraries(config android.Config) map[string]string {
- return config.Once(vndkSpLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
+// vndkModuleListRemover takes a moduleListerFunc and a prefix and returns a moduleListerFunc
+// that returns the same lists as the input moduleListerFunc, but with modules with the
+// given prefix removed.
+func vndkModuleListRemover(lister moduleListerFunc, prefix string) moduleListerFunc {
+ return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
+ moduleNames, fileNames = lister(ctx)
+ filter := func(in []string) []string {
+ out := make([]string, 0, len(in))
+ for _, lib := range in {
+ if strings.HasPrefix(lib, prefix) {
+ continue
+ }
+ out = append(out, lib)
+ }
+ return out
+ }
+ return filter(moduleNames), filter(fileNames)
+ }
}
-func isLlndkLibrary(baseModuleName string, config android.Config) bool {
- _, ok := llndkLibraries(config)[strings.TrimSuffix(baseModuleName, llndkLibrarySuffix)]
- return ok
-}
-
-func llndkLibraries(config android.Config) map[string]string {
- return config.Once(llndkLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
-
-func isVndkPrivateLibrary(baseModuleName string, config android.Config) bool {
- _, ok := vndkPrivateLibraries(config)[baseModuleName]
- return ok
-}
-
-func vndkPrivateLibraries(config android.Config) map[string]string {
- return config.Once(vndkPrivateLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
-
-func vndkUsingCoreVariantLibraries(config android.Config) map[string]string {
- return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
- return make(map[string]string)
- }).(map[string]string)
-}
+var vndkMustUseVendorVariantListKey = android.NewOnceKey("vndkMustUseVendorVariantListKey")
func vndkMustUseVendorVariantList(cfg android.Config) []string {
return cfg.Once(vndkMustUseVendorVariantListKey, func() interface{} {
@@ -294,29 +300,22 @@
func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
lib := m.linker.(*llndkStubDecorator)
- name := m.ImplementationModuleName(mctx)
- filename := name + ".so"
- vndkLibrariesLock.Lock()
- defer vndkLibrariesLock.Unlock()
-
- llndkLibraries(mctx.Config())[name] = filename
- if !Bool(lib.Properties.Vendor_available) {
- vndkPrivateLibraries(mctx.Config())[name] = filename
- }
-
- if mctx.OtherModuleExists(name) {
- mctx.AddFarVariationDependencies(m.Target().Variations(), llndkImplDep, name)
+ m.VendorProperties.IsLLNDK = true
+ if Bool(lib.Properties.Private) {
+ m.VendorProperties.IsVNDKPrivate = true
}
}
func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
- name := m.BaseModuleName()
- filename, err := getVndkFileName(m)
- if err != nil {
- panic(err)
+ if m.InProduct() {
+ // We may skip the steps for the product variants because they
+ // are already covered by the vendor variants.
+ return
}
+ name := m.BaseModuleName()
+
if lib := m.library; lib != nil && lib.hasStubsVariants() && name != "libz" {
// b/155456180 libz is the ONLY exception here. We don't want to make
// libz an LLNDK library because we in general can't guarantee that
@@ -327,25 +326,23 @@
mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
}
- vndkLibrariesLock.Lock()
- defer vndkLibrariesLock.Unlock()
-
if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
m.Properties.MustUseVendorVariant = true
}
if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
- vndkUsingCoreVariantLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKUsingCoreVariant = true
}
if m.vndkdep.isVndkSp() {
- vndkSpLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKSP = true
} else {
- vndkCoreLibraries(mctx.Config())[name] = filename
+ m.VendorProperties.IsVNDKCore = true
}
- // As `vendor_available` and `product_available` has the same value for VNDK modules,
- // we don't need to check both values.
- if !Bool(m.VendorProperties.Vendor_available) {
- vndkPrivateLibraries(mctx.Config())[name] = filename
+ if m.IsVndkPrivate() {
+ m.VendorProperties.IsVNDKPrivate = true
+ }
+ if Bool(m.VendorProperties.Product_available) {
+ m.VendorProperties.IsVNDKProduct = true
}
}
@@ -389,7 +386,7 @@
useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
- return lib.shared() && m.inVendor() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
+ return lib.shared() && m.InVendor() && m.IsVndk() && !m.IsVndkExt() && !useCoreVariant
}
return false
}
@@ -410,10 +407,32 @@
return
}
- lib, is_lib := m.linker.(*libraryDecorator)
- prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
+ // This is a temporary measure to copy the properties from an llndk_library into the cc_library
+ // that will actually build the stubs. It will be removed once the properties are moved into
+ // the cc_library in the Android.bp files.
+ mergeLLNDKToLib := func(llndk *Module, llndkProperties *llndkLibraryProperties, flagExporter *flagExporter) {
+ if llndkLib := moduleLibraryInterface(llndk); llndkLib != nil {
+ *llndkProperties = llndkLib.(*llndkStubDecorator).Properties
+ flagExporter.Properties = llndkLib.(*llndkStubDecorator).flagExporter.Properties
- if (is_lib && lib.buildShared()) || (is_prebuilt_lib && prebuilt_lib.buildShared()) {
+ m.VendorProperties.IsLLNDK = llndk.VendorProperties.IsLLNDK
+ m.VendorProperties.IsVNDKPrivate = llndk.VendorProperties.IsVNDKPrivate
+ }
+ }
+
+ lib, isLib := m.linker.(*libraryDecorator)
+ prebuiltLib, isPrebuiltLib := m.linker.(*prebuiltLibraryLinker)
+
+ if m.UseVndk() && isLib && lib.hasLLNDKStubs() {
+ llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(lib.Properties.Llndk_stubs))
+ mergeLLNDKToLib(llndk[0].(*Module), &lib.Properties.Llndk, &lib.flagExporter)
+ }
+ if m.UseVndk() && isPrebuiltLib && prebuiltLib.hasLLNDKStubs() {
+ llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(prebuiltLib.Properties.Llndk_stubs))
+ mergeLLNDKToLib(llndk[0].(*Module), &prebuiltLib.Properties.Llndk, &prebuiltLib.flagExporter)
+ }
+
+ if (isLib && lib.buildShared()) || (isPrebuiltLib && prebuiltLib.buildShared()) {
if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
processVndkLibrary(mctx, m)
return
@@ -422,29 +441,91 @@
}
func init() {
- android.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+ RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
}
+func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
+ // Make uses LLNDK_LIBRARIES to determine which libraries to install.
+ // HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+ // Therefore, by removing the library here, we cause it to only be installed if libc
+ // depends on it.
+ ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+ ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
+}
+
type vndkLibrariesTxt struct {
- android.ModuleBase
- outputFile android.OutputPath
+ android.SingletonModuleBase
+
+ lister moduleListerFunc
+ makeVarName string
+
+ properties VndkLibrariesTxtProperties
+
+ outputFile android.OutputPath
+ moduleNames []string
+ fileNames []string
+}
+
+type VndkLibrariesTxtProperties struct {
+ Insert_vndk_version *bool
}
var _ etc.PrebuiltEtcModule = &vndkLibrariesTxt{}
var _ android.OutputFileProducer = &vndkLibrariesTxt{}
-// vndk_libraries_txt is a special kind of module type in that it name is one of
-// - llndk.libraries.txt
-// - vndkcore.libraries.txt
-// - vndksp.libraries.txt
-// - vndkprivate.libraries.txt
-// - vndkcorevariant.libraries.txt
-// A module behaves like a prebuilt_etc but its content is generated by soong.
-// By being a soong module, these files can be referenced by other soong modules.
+// llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
+// generated by Soong but can be referenced by other modules.
// For example, apex_vndk can depend on these files as prebuilt.
-func VndkLibrariesTxtFactory() android.Module {
- m := &vndkLibrariesTxt{}
+func llndkLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(llndkLibrariesWithoutHWASAN, "LLNDK_LIBRARIES")
+}
+
+// vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkSPLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkSPLibraries, "VNDK_SAMEPROCESS_LIBRARIES")
+}
+
+// vndkcore_libraries_txt is a singleton module whose content is a list of VNDK core libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkCoreLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkCoreLibraries, "VNDK_CORE_LIBRARIES")
+}
+
+// vndkprivate_libraries_txt is a singleton module whose content is a list of VNDK private libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkPrivateLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkPrivateLibraries, "VNDK_PRIVATE_LIBRARIES")
+}
+
+// vndkproduct_libraries_txt is a singleton module whose content is a list of VNDK product libraries
+// generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkProductLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkProductLibraries, "VNDK_PRODUCT_LIBRARIES")
+}
+
+// vndkcorevariant_libraries_txt is a singleton module whose content is a list of VNDK libraries
+// that are using the core variant, generated by Soong but can be referenced by other modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func vndkUsingCoreVariantLibrariesTxtFactory() android.SingletonModule {
+ return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
+}
+
+func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
+ m := &vndkLibrariesTxt{
+ lister: lister,
+ makeVarName: makeVarName,
+ }
+ m.AddProperties(&m.properties)
android.InitAndroidModule(m)
return m
}
@@ -457,42 +538,24 @@
}
func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var list []string
- switch txt.Name() {
- case llndkLibrariesTxt:
- for _, filename := range android.SortedStringMapValues(llndkLibraries(ctx.Config())) {
- if strings.HasPrefix(filename, "libclang_rt.hwasan-") {
- continue
- }
- list = append(list, filename)
- }
- case vndkCoreLibrariesTxt:
- list = android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
- case vndkSpLibrariesTxt:
- list = android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
- case vndkPrivateLibrariesTxt:
- list = android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
- case vndkUsingCoreVariantLibrariesTxt:
- list = android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
- default:
- ctx.ModuleErrorf("name(%s) is unknown.", txt.Name())
- return
- }
-
var filename string
- if txt.Name() != vndkUsingCoreVariantLibrariesTxt {
+ if BoolDefault(txt.properties.Insert_vndk_version, true) {
filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
} else {
filename = txt.Name()
}
txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
- android.WriteFileRule(ctx, txt.outputFile, strings.Join(list, "\n"))
installPath := android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(installPath, filename, txt.outputFile)
}
+func (txt *vndkLibrariesTxt) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+ txt.moduleNames, txt.fileNames = txt.lister(ctx)
+ android.WriteFileRule(ctx, txt.outputFile, strings.Join(txt.fileNames, "\n"))
+}
+
func (txt *vndkLibrariesTxt) AndroidMkEntries() []android.AndroidMkEntries {
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "ETC",
@@ -505,6 +568,11 @@
}}
}
+func (txt *vndkLibrariesTxt) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict(txt.makeVarName, strings.Join(txt.moduleNames, " "))
+
+}
+
// PrebuiltEtcModule interface
func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
return txt.outputFile
@@ -533,7 +601,7 @@
vndkSnapshotZipFile android.OptionalPath
}
-func isVndkSnapshotLibrary(config android.DeviceConfig, m *Module,
+func isVndkSnapshotAware(config android.DeviceConfig, m *Module,
apexInfo android.ApexInfo) (i snapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) {
if m.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -542,14 +610,14 @@
// !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants.
// !installable: Snapshot only cares about "installable" modules.
// isSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense.
- if !m.inVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
+ if !m.InVendor() || !m.installable(apexInfo) || m.isSnapshotPrebuilt() {
return nil, "", false
}
l, ok := m.linker.(snapshotLibraryInterface)
if !ok || !l.shared() {
return nil, "", false
}
- if m.VndkVersion() == config.PlatformVndkVersion() && m.IsVndk() && !m.isVndkExt() {
+ if m.VndkVersion() == config.PlatformVndkVersion() && m.IsVndk() && !m.IsVndkExt() {
if m.isVndkSp() {
return l, "vndk-sp", true
} else {
@@ -622,6 +690,9 @@
var headers android.Paths
+ // installVndkSnapshotLib copies built .so file from the module.
+ // Also, if the build artifacts is on, write a json file which contains all exported flags
+ // with FlagExporterInfo.
installVndkSnapshotLib := func(m *Module, vndkType string) (android.Paths, bool) {
var ret android.Paths
@@ -632,7 +703,7 @@
libPath := m.outputFile.Path()
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
- ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+ ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
if ctx.Config().VndkSnapshotBuildArtifacts() {
prop := struct {
@@ -654,7 +725,7 @@
ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
return nil, false
}
- ret = append(ret, writeStringToFile(ctx, string(j), propOut))
+ ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
}
return ret, true
}
@@ -667,11 +738,21 @@
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- l, vndkType, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m, apexInfo)
+ l, vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo)
if !ok {
return
}
+ // For all snapshot candidates, the followings are captured.
+ // - .so files
+ // - notice files
+ //
+ // The followings are also captured if VNDK_SNAPSHOT_BUILD_ARTIFACTS.
+ // - .json files containing exported flags
+ // - exported headers from collectHeadersForSnapshot()
+ //
+ // Headers are deduplicated after visiting all modules.
+
// install .so files for appropriate modules.
// Also install .json files if VNDK_SNAPSHOT_BUILD_ARTIFACTS
libs, ok := installVndkSnapshotLib(m, vndkType)
@@ -690,7 +771,7 @@
// skip already copied notice file
if _, ok := noticeBuilt[noticeName]; !ok {
noticeBuilt[noticeName] = true
- snapshotOutputs = append(snapshotOutputs, combineNotices(
+ snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
ctx, m.NoticeFiles(), filepath.Join(noticeDir, noticeName)))
}
}
@@ -702,7 +783,7 @@
// install all headers after removing duplicates
for _, header := range android.FirstUniquePaths(headers) {
- snapshotOutputs = append(snapshotOutputs, copyFile(
+ snapshotOutputs = append(snapshotOutputs, copyFileRule(
ctx, header, filepath.Join(includeDir, header.String())))
}
@@ -712,38 +793,18 @@
if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
return
}
- snapshotOutputs = append(snapshotOutputs, copyFile(
+ snapshotOutputs = append(snapshotOutputs, copyFileRule(
ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
})
/*
- Dump a map to a list file as:
-
- {key1} {value1}
- {key2} {value2}
- ...
- */
- installMapListFile := func(m map[string]string, path string) android.OutputPath {
- var txtBuilder strings.Builder
- for idx, k := range android.SortedStringKeys(m) {
- if idx > 0 {
- txtBuilder.WriteString("\n")
- }
- txtBuilder.WriteString(k)
- txtBuilder.WriteString(" ")
- txtBuilder.WriteString(m[k])
- }
- return writeStringToFile(ctx, txtBuilder.String(), path)
- }
-
- /*
module_paths.txt contains paths on which VNDK modules are defined.
e.g.,
libbase.so system/libbase
libc.so bionic/libc
...
*/
- snapshotOutputs = append(snapshotOutputs, installMapListFile(modulePaths, filepath.Join(configsDir, "module_paths.txt")))
+ snapshotOutputs = append(snapshotOutputs, installMapListFileRule(ctx, modulePaths, filepath.Join(configsDir, "module_paths.txt")))
/*
module_names.txt contains names as which VNDK modules are defined,
@@ -754,7 +815,7 @@
libprotobuf-cpp-full-3.9.2.so libprotobuf-cpp-full
...
*/
- snapshotOutputs = append(snapshotOutputs, installMapListFile(moduleNames, filepath.Join(configsDir, "module_names.txt")))
+ snapshotOutputs = append(snapshotOutputs, installMapListFileRule(ctx, moduleNames, filepath.Join(configsDir, "module_names.txt")))
// All artifacts are ready. Sort them to normalize ninja and then zip.
sort.Slice(snapshotOutputs, func(i, j int) bool {
@@ -764,7 +825,7 @@
zipPath := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+".zip")
zipRule := android.NewRuleBuilder(pctx, ctx)
- // filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with xargs
+ // filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
zipRule.Command().
Text("tr").
@@ -787,36 +848,32 @@
func getVndkFileName(m *Module) (string, error) {
if library, ok := m.linker.(*libraryDecorator); ok {
- return library.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+ return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
}
if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
- return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+ return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
+ }
+ if library, ok := m.linker.(*llndkStubDecorator); ok {
+ return library.getLibNameHelper(m.BaseModuleName(), true, false) + ".so", nil
}
return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
}
func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
- llndk := android.SortedStringMapValues(llndkLibraries(ctx.Config()))
- vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
- vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
- vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
-
// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
// Since each target have different set of libclang_rt.* files,
// keep the common set of files in vndk.libraries.txt
+ _, llndk := vndkModuleListRemover(llndkLibraries, "libclang_rt.")(ctx)
+ _, vndkcore := vndkModuleListRemover(vndkCoreLibraries, "libclang_rt.")(ctx)
+ _, vndksp := vndkSPLibraries(ctx)
+ _, vndkprivate := vndkPrivateLibraries(ctx)
+ _, vndkproduct := vndkModuleListRemover(vndkProductLibraries, "libclang_rt.")(ctx)
var merged []string
- filterOutLibClangRt := func(libList []string) (filtered []string) {
- for _, lib := range libList {
- if !strings.HasPrefix(lib, "libclang_rt.") {
- filtered = append(filtered, lib)
- }
- }
- return
- }
- merged = append(merged, addPrefix(filterOutLibClangRt(llndk), "LLNDK: ")...)
+ merged = append(merged, addPrefix(llndk, "LLNDK: ")...)
merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
- merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
+ merged = append(merged, addPrefix(vndkcore, "VNDK-core: ")...)
merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
+ merged = append(merged, addPrefix(vndkproduct, "VNDK-product: ")...)
c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
}
@@ -826,13 +883,11 @@
// they been moved to an apex.
movedToApexLlndkLibraries := make(map[string]bool)
ctx.VisitAllModules(func(module android.Module) {
- if m, ok := module.(*Module); ok {
- if llndk, ok := m.linker.(*llndkStubDecorator); ok {
- // Skip bionic libs, they are handled in different manner
- name := llndk.implementationModuleName(m.BaseModuleName())
- if llndk.movedToApex && !isBionic(name) {
- movedToApexLlndkLibraries[name] = true
- }
+ if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
+ // Skip bionic libs, they are handled in different manner
+ name := library.implementationModuleName(module.(*Module).BaseModuleName())
+ if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
+ movedToApexLlndkLibraries[name] = true
}
}
})
@@ -840,25 +895,6 @@
ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES",
strings.Join(android.SortedStringKeys(movedToApexLlndkLibraries), " "))
- // Make uses LLNDK_LIBRARIES to determine which libraries to install.
- // HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
- // Therefore, by removing the library here, we cause it to only be installed if libc
- // depends on it.
- installedLlndkLibraries := []string{}
- for lib := range llndkLibraries(ctx.Config()) {
- if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
- continue
- }
- installedLlndkLibraries = append(installedLlndkLibraries, lib)
- }
- sort.Strings(installedLlndkLibraries)
- ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
-
- ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
- ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
-
ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e6e2ad8..04162cd 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -130,7 +130,7 @@
flags Flags, deps PathDeps, objs Objects) android.Path {
if !p.matchesWithDevice(ctx.DeviceConfig()) {
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
return nil
}
@@ -175,7 +175,7 @@
return in
}
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
return nil
}
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_inject/host_bionic_inject.go
index 629f6cc..ce8b062 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_inject/host_bionic_inject.go
@@ -58,7 +58,7 @@
os.Exit(4)
}
- start_addr, err := parseElf(r, linker)
+ startAddr, err := parseElf(r, linker)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(5)
@@ -71,7 +71,7 @@
}
defer w.Close()
- err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", start_addr)
+ err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", startAddr)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(7)
@@ -128,7 +128,7 @@
// Check that all of the PT_LOAD segments have been embedded properly
func checkLinker(file, linker *elf.File, fileSyms []elf.Symbol) error {
- dlwrap_linker_offset, err := findSymbol(fileSyms, "__dlwrap_linker_offset")
+ dlwrapLinkerOffset, err := findSymbol(fileSyms, "__dlwrap_linker_offset")
if err != nil {
return err
}
@@ -138,7 +138,7 @@
continue
}
- laddr := lprog.Vaddr + dlwrap_linker_offset.Value
+ laddr := lprog.Vaddr + dlwrapLinkerOffset.Value
found := false
for _, prog := range file.Progs {
@@ -163,7 +163,7 @@
}
if !found {
return fmt.Errorf("Linker prog %d (0x%x) not found at offset 0x%x",
- i, lprog.Vaddr, dlwrap_linker_offset.Value)
+ i, lprog.Vaddr, dlwrapLinkerOffset.Value)
}
}
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0a9b156..7dd50f6 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -50,12 +50,30 @@
var buildVariant = flag.String("variant", "eng", "build variant to use")
-var skipProducts = flag.String("skip-products", "", "comma-separated list of products to skip (known failures, etc)")
-var includeProducts = flag.String("products", "", "comma-separated list of products to build")
-
var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
var shard = flag.Int("shard", 1, "1-indexed shard to execute")
+var skipProducts multipleStringArg
+var includeProducts multipleStringArg
+
+func init() {
+ flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
+ flag.Var(&includeProducts, "products", "comma-separated list of products to build")
+}
+
+// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
+// []string. The argument can be passed multiple times to append more values.
+type multipleStringArg []string
+
+func (m *multipleStringArg) String() string {
+ return strings.Join(*m, `, `)
+}
+
+func (m *multipleStringArg) Set(s string) error {
+ *m = append(*m, strings.Split(s, ",")...)
+ return nil
+}
+
const errorLeadingLines = 20
const errorTrailingLines = 20
@@ -185,7 +203,11 @@
Status: stat,
}}
- config := build.NewConfig(buildCtx)
+ args := ""
+ if *alternateResultDir {
+ args = "dist"
+ }
+ config := build.NewConfig(buildCtx, args)
if *outDir == "" {
name := "multiproduct"
if !*incremental {
@@ -212,15 +234,10 @@
os.MkdirAll(logsDir, 0777)
build.SetupOutDir(buildCtx, config)
- if *alternateResultDir {
- distLogsDir := filepath.Join(config.DistDir(), "logs")
- os.MkdirAll(distLogsDir, 0777)
- log.SetOutput(filepath.Join(distLogsDir, "soong.log"))
- trace.SetOutput(filepath.Join(distLogsDir, "build.trace"))
- } else {
- log.SetOutput(filepath.Join(config.OutDir(), "soong.log"))
- trace.SetOutput(filepath.Join(config.OutDir(), "build.trace"))
- }
+
+ os.MkdirAll(config.LogsDir(), 0777)
+ log.SetOutput(filepath.Join(config.LogsDir(), "soong.log"))
+ trace.SetOutput(filepath.Join(config.LogsDir(), "build.trace"))
var jobs = *numJobs
if jobs < 1 {
@@ -251,9 +268,9 @@
var productsList []string
allProducts := strings.Fields(vars["all_named_products"])
- if *includeProducts != "" {
- missingProducts := []string{}
- for _, product := range strings.Split(*includeProducts, ",") {
+ if len(includeProducts) > 0 {
+ var missingProducts []string
+ for _, product := range includeProducts {
if inList(product, allProducts) {
productsList = append(productsList, product)
} else {
@@ -268,9 +285,8 @@
}
finalProductsList := make([]string, 0, len(productsList))
- skipList := strings.Split(*skipProducts, ",")
skipProduct := func(p string) bool {
- for _, s := range skipList {
+ for _, s := range skipProducts {
if p == s {
return true
}
@@ -344,7 +360,7 @@
FileArgs: []zip.FileArg{
{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
},
- OutputFilePath: filepath.Join(config.DistDir(), "logs.zip"),
+ OutputFilePath: filepath.Join(config.RealDistDir(), "logs.zip"),
NumParallelJobs: runtime.NumCPU(),
CompressionLevel: 5,
}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index a4f57ea..f8919a4 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -337,7 +337,7 @@
for _, copyPair := range copies {
fromPath := joinPath(fromDir, copyPair.GetFrom())
toPath := joinPath(toDir, copyPair.GetTo())
- err := copyOneFile(fromPath, toPath)
+ err := copyOneFile(fromPath, toPath, copyPair.GetExecutable())
if err != nil {
return fmt.Errorf("error copying %q to %q: %w", fromPath, toPath, err)
}
@@ -346,7 +346,7 @@
}
// copyOneFile copies a file.
-func copyOneFile(from string, to string) error {
+func copyOneFile(from string, to string, executable bool) error {
err := os.MkdirAll(filepath.Dir(to), 0777)
if err != nil {
return err
@@ -358,6 +358,9 @@
}
perm := stat.Mode()
+ if executable {
+ perm = perm | 0100 // u+x
+ }
in, err := os.Open(from)
if err != nil {
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
index 6584bdf..79bb90c 100644
--- a/cmd/sbox/sbox_proto/sbox.pb.go
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -156,8 +156,10 @@
// are relative to is specific to the context the Copy is used in and will be different for
// from and to.
type Copy struct {
- From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
- To *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
+ From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
+ To *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
+ // If true, make the file executable after copying it.
+ Executable *bool `protobuf:"varint,3,opt,name=executable" json:"executable,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -202,6 +204,13 @@
return ""
}
+func (m *Copy) GetExecutable() bool {
+ if m != nil && m.Executable != nil {
+ return *m.Executable
+ }
+ return false
+}
+
func init() {
proto.RegisterType((*Manifest)(nil), "sbox.Manifest")
proto.RegisterType((*Command)(nil), "sbox.Command")
@@ -213,21 +222,22 @@
}
var fileDescriptor_9d0425bf0de86ed1 = []byte{
- // 252 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x41, 0x4b, 0xc3, 0x40,
- 0x10, 0x85, 0x49, 0x9a, 0xd2, 0x66, 0x6a, 0x7b, 0x18, 0x3c, 0xec, 0x45, 0x08, 0x01, 0x21, 0x55,
- 0xe8, 0xc1, 0x7f, 0x60, 0xf5, 0xe0, 0xc5, 0xcb, 0x1e, 0x45, 0x08, 0xdb, 0x64, 0x97, 0x04, 0x4c,
- 0x66, 0xd9, 0xdd, 0x82, 0xfd, 0x57, 0xfe, 0x44, 0xd9, 0x49, 0xea, 0xc5, 0xdb, 0xcc, 0xfb, 0x78,
- 0xf3, 0x1e, 0x03, 0xe0, 0x4f, 0xf4, 0x7d, 0xb0, 0x8e, 0x02, 0x61, 0x16, 0xe7, 0xf2, 0x13, 0xd6,
- 0xef, 0x6a, 0xec, 0x8d, 0xf6, 0x01, 0xf7, 0xb0, 0x6e, 0x68, 0x18, 0xd4, 0xd8, 0x7a, 0x91, 0x14,
- 0x8b, 0x6a, 0xf3, 0xb4, 0x3d, 0xb0, 0xe1, 0x65, 0x52, 0xe5, 0x1f, 0xc6, 0x7b, 0xd8, 0xd1, 0x39,
- 0xd8, 0x73, 0xa8, 0x5b, 0x6d, 0x4d, 0xff, 0xa5, 0x45, 0x5a, 0x24, 0x55, 0x2e, 0xb7, 0x93, 0xfa,
- 0x3a, 0x89, 0xe5, 0x4f, 0x02, 0xab, 0xd9, 0x8c, 0x8f, 0xb0, 0x69, 0xc8, 0x5e, 0xea, 0x93, 0x36,
- 0xe4, 0xf4, 0x1c, 0x00, 0xd7, 0x00, 0x7b, 0x91, 0x10, 0xf1, 0x91, 0x29, 0xde, 0xc2, 0xb2, 0xe9,
- 0xda, 0xde, 0xf1, 0xd9, 0xb5, 0x9c, 0x16, 0x14, 0xb0, 0x9a, 0x1b, 0x88, 0x45, 0x91, 0x56, 0xb9,
- 0xbc, 0xae, 0xb8, 0x07, 0x76, 0xd7, 0xca, 0x04, 0xed, 0x44, 0xf6, 0xef, 0x76, 0x1e, 0xe9, 0x73,
- 0x84, 0x78, 0x07, 0xd0, 0x8f, 0xb1, 0x79, 0xa7, 0x7c, 0x27, 0x96, 0x5c, 0x3b, 0x67, 0xe5, 0x4d,
- 0xf9, 0xae, 0x7c, 0x80, 0x2c, 0x3a, 0x10, 0x21, 0x33, 0x8e, 0x06, 0x91, 0x70, 0x10, 0xcf, 0xb8,
- 0x83, 0x34, 0x90, 0x48, 0x59, 0x49, 0x03, 0x1d, 0x6f, 0x3e, 0xf8, 0xa1, 0x35, 0x3f, 0xf4, 0x37,
- 0x00, 0x00, 0xff, 0xff, 0x95, 0x4d, 0xee, 0x7d, 0x5d, 0x01, 0x00, 0x00,
+ // 268 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0x4f, 0x4b, 0xc3, 0x40,
+ 0x10, 0xc5, 0xc9, 0x9f, 0xd2, 0x64, 0x6a, 0x7b, 0x18, 0x3c, 0xec, 0x45, 0x09, 0x01, 0x21, 0x45,
+ 0xe8, 0xc1, 0x6f, 0x60, 0xf5, 0x20, 0x82, 0x97, 0x1c, 0x45, 0x08, 0x9b, 0x64, 0x43, 0x02, 0x4d,
+ 0x26, 0xec, 0x6e, 0xa0, 0xfd, 0x56, 0x7e, 0x44, 0xd9, 0x49, 0x2a, 0x82, 0xb7, 0x99, 0xdf, 0xe3,
+ 0xcd, 0x7b, 0x0c, 0x80, 0x29, 0xe9, 0x7c, 0x18, 0x35, 0x59, 0xc2, 0xd0, 0xcd, 0xe9, 0x17, 0x44,
+ 0x1f, 0x72, 0xe8, 0x1a, 0x65, 0x2c, 0xee, 0x21, 0xaa, 0xa8, 0xef, 0xe5, 0x50, 0x1b, 0xe1, 0x25,
+ 0x41, 0xb6, 0x79, 0xda, 0x1e, 0xd8, 0xf0, 0x32, 0xd3, 0xfc, 0x57, 0xc6, 0x07, 0xd8, 0xd1, 0x64,
+ 0xc7, 0xc9, 0x16, 0xb5, 0x1a, 0x9b, 0xee, 0xa4, 0x84, 0x9f, 0x78, 0x59, 0x9c, 0x6f, 0x67, 0xfa,
+ 0x3a, 0xc3, 0xf4, 0xdb, 0x83, 0xf5, 0x62, 0xc6, 0x47, 0xd8, 0x54, 0x34, 0x5e, 0x8a, 0x52, 0x35,
+ 0xa4, 0xd5, 0x12, 0x00, 0xd7, 0x80, 0xf1, 0x92, 0x83, 0x93, 0x8f, 0xac, 0xe2, 0x2d, 0xac, 0xaa,
+ 0xb6, 0xee, 0x34, 0x9f, 0x8d, 0xf2, 0x79, 0x41, 0x01, 0xeb, 0xa5, 0x81, 0x08, 0x12, 0x3f, 0x8b,
+ 0xf3, 0xeb, 0x8a, 0x7b, 0x60, 0x77, 0x21, 0x1b, 0xab, 0xb4, 0x08, 0xff, 0xdd, 0x8e, 0x9d, 0xfa,
+ 0xec, 0x44, 0xbc, 0x03, 0xe8, 0x06, 0xd7, 0xbc, 0x95, 0xa6, 0x15, 0x2b, 0xae, 0x1d, 0x33, 0x79,
+ 0x93, 0xa6, 0x4d, 0xdf, 0x21, 0x74, 0x0e, 0x44, 0x08, 0x1b, 0x4d, 0xbd, 0xf0, 0x38, 0x88, 0x67,
+ 0xdc, 0x81, 0x6f, 0x49, 0xf8, 0x4c, 0x7c, 0x4b, 0x78, 0x0f, 0xa0, 0xce, 0xaa, 0x9a, 0xac, 0x2c,
+ 0x4f, 0x4a, 0x04, 0x5c, 0xf5, 0x0f, 0x39, 0xde, 0x7c, 0xf2, 0xc3, 0x0b, 0x7e, 0xf8, 0x4f, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x78, 0x37, 0x3e, 0x6a, 0x7d, 0x01, 0x00, 0x00,
}
diff --git a/cmd/sbox/sbox_proto/sbox.proto b/cmd/sbox/sbox_proto/sbox.proto
index ab95545..695b0e8 100644
--- a/cmd/sbox/sbox_proto/sbox.proto
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -55,4 +55,7 @@
message Copy {
required string from = 1;
required string to = 2;
+
+ // If true, make the file executable after copying it.
+ optional bool executable = 3;
}
\ No newline at end of file
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 441ea0d..6714978 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -20,6 +20,7 @@
"golang-protobuf-proto",
"soong",
"soong-android",
+ "soong-bp2build",
"soong-env",
"soong-ui-metrics_proto",
],
@@ -27,10 +28,6 @@
"main.go",
"writedocs.go",
"queryview.go",
- "queryview_templates.go",
- ],
- testSrcs: [
- "queryview_test.go",
],
primaryBuilder: true,
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index d758de2..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 (
@@ -51,10 +52,16 @@
return android.NewNameResolver(exportFilter)
}
-func newContext(srcDir string, configuration android.Config) *android.Context {
+// bazelConversionRequested checks that the user is intending to convert
+// Blueprint to Bazel BUILD files.
+func bazelConversionRequested(configuration android.Config) bool {
+ return configuration.IsEnvTrue("GENERATE_BAZEL_FILES")
+}
+
+func newContext(configuration android.Config) *android.Context {
ctx := android.NewContext(configuration)
ctx.Register()
- if !shouldPrepareBuildActions() {
+ if !shouldPrepareBuildActions(configuration) {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
ctx.SetNameInterface(newNameResolver(configuration))
@@ -88,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.
@@ -108,12 +124,14 @@
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...)
}
+
+ // Convert the Soong module graph into Bazel BUILD files.
if bazelQueryViewDir != "" {
if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
@@ -130,7 +148,7 @@
// TODO(ccross): make this a command line argument. Requires plumbing through blueprint
// to affect the command line of the primary builder.
- if shouldPrepareBuildActions() {
+ if shouldPrepareBuildActions(configuration) {
metricsFile := filepath.Join(bootstrap.BuildDir, "soong_build_metrics.pb")
err := android.WriteMetrics(configuration, metricsFile)
if err != nil {
@@ -140,8 +158,35 @@
}
}
-func shouldPrepareBuildActions() bool {
- // If we're writing soong_docs or queryview, don't write build.ninja or
- // collect metrics.
- return docFile == "" && bazelQueryViewDir == ""
+// 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 {
+ // Generating Soong docs
+ if docFile != "" {
+ return false
+ }
+
+ // Generating a directory for Soong query (queryview)
+ if bazelQueryViewDir != "" {
+ return false
+ }
+
+ // Generating a directory for converted Bazel BUILD files
+ return !bazelConversionRequested(configuration)
}
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index f5aa685..305a224 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -16,512 +16,45 @@
import (
"android/soong/android"
- "fmt"
+ "android/soong/bp2build"
"io/ioutil"
"os"
"path/filepath"
- "reflect"
- "strings"
-
- "github.com/google/blueprint"
- "github.com/google/blueprint/bootstrap/bpdoc"
- "github.com/google/blueprint/proptools"
)
-var (
- // An allowlist of prop types that are surfaced from module props to rule
- // attributes. (nested) dictionaries are notably absent here, because while
- // Soong supports multi value typed and nested dictionaries, Bazel's rule
- // attr() API supports only single-level string_dicts.
- allowedPropTypes = map[string]bool{
- "int": true, // e.g. 42
- "bool": true, // e.g. True
- "string_list": true, // e.g. ["a", "b"]
- "string": true, // e.g. "a"
- }
-
- // Certain module property names are blocklisted/ignored here, for the reasons commented.
- ignoredPropNames = map[string]bool{
- "name": true, // redundant, since this is explicitly generated for every target
- "from": true, // reserved keyword
- "in": true, // reserved keyword
- "arch": true, // interface prop type is not supported yet.
- "multilib": true, // interface prop type is not supported yet.
- "target": true, // interface prop type is not supported yet.
- "visibility": true, // Bazel has native visibility semantics. Handle later.
- "features": true, // There is already a built-in attribute 'features' which cannot be overridden.
- }
-)
-
-func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
- name := ""
- if c.ModuleSubDir(logicModule) != "" {
- // TODO(b/162720883): Figure out a way to drop the "--" variant suffixes.
- name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
- } else {
- name = c.ModuleName(logicModule)
- }
-
- return strings.Replace(name, "//", "", 1)
-}
-
-func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
- return "//" +
- packagePath(c, logicModule) +
- ":" +
- targetNameWithVariant(c, logicModule)
-}
-
-func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
- return filepath.Dir(c.BlueprintFile(logicModule))
-}
-
-func escapeString(s string) string {
- s = strings.ReplaceAll(s, "\\", "\\\\")
- return strings.ReplaceAll(s, "\"", "\\\"")
-}
-
-func makeIndent(indent int) string {
- if indent < 0 {
- panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
- }
- return strings.Repeat(" ", indent)
-}
-
-// prettyPrint a property value into the equivalent Starlark representation
-// recursively.
-func prettyPrint(propertyValue reflect.Value, indent int) (string, error) {
- if isZero(propertyValue) {
- // A property value being set or unset actually matters -- Soong does set default
- // values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
- // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
- //
- // In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
- // value of unset attributes.
- return "", nil
- }
-
- var ret string
- switch propertyValue.Kind() {
- case reflect.String:
- ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
- case reflect.Bool:
- ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
- case reflect.Int, reflect.Uint, reflect.Int64:
- ret = fmt.Sprintf("%v", propertyValue.Interface())
- case reflect.Ptr:
- return prettyPrint(propertyValue.Elem(), indent)
- case reflect.Slice:
- ret = "[\n"
- for i := 0; i < propertyValue.Len(); i++ {
- indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1)
- if err != nil {
- return "", err
- }
-
- if indexedValue != "" {
- ret += makeIndent(indent + 1)
- ret += indexedValue
- ret += ",\n"
- }
- }
- ret += makeIndent(indent)
- ret += "]"
- case reflect.Struct:
- ret = "{\n"
- // Sort and print the struct props by the key.
- structProps := extractStructProperties(propertyValue, indent)
- for _, k := range android.SortedStringKeys(structProps) {
- ret += makeIndent(indent + 1)
- ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
- }
- ret += makeIndent(indent)
- ret += "}"
- case reflect.Interface:
- // TODO(b/164227191): implement pretty print for interfaces.
- // Interfaces are used for for arch, multilib and target properties.
- return "", nil
- default:
- return "", fmt.Errorf(
- "unexpected kind for property struct field: %s", propertyValue.Kind())
- }
- return ret, nil
-}
-
-// Converts a reflected property struct value into a map of property names and property values,
-// which each property value correctly pretty-printed and indented at the right nest level,
-// since property structs can be nested. In Starlark, nested structs are represented as nested
-// dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
- if structValue.Kind() != reflect.Struct {
- panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
- }
-
- ret := map[string]string{}
- structType := structValue.Type()
- for i := 0; i < structValue.NumField(); i++ {
- field := structType.Field(i)
- if field.PkgPath != "" {
- // Skip unexported fields. Some properties are
- // internal to Soong only, and these fields do not have PkgPath.
- continue
- }
- if proptools.HasTag(field, "blueprint", "mutated") {
- continue
- }
-
- fieldValue := structValue.Field(i)
- if isZero(fieldValue) {
- // Ignore zero-valued fields
- continue
- }
-
- propertyName := proptools.PropertyNameForField(field.Name)
- prettyPrintedValue, err := prettyPrint(fieldValue, indent+1)
- if err != nil {
- panic(
- fmt.Errorf(
- "Error while parsing property: %q. %s",
- propertyName,
- err))
- }
- if prettyPrintedValue != "" {
- ret[propertyName] = prettyPrintedValue
- }
- }
-
- return ret
-}
-
-func isStructPtr(t reflect.Type) bool {
- return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
-}
-
-// Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(aModule android.Module) map[string]string {
- ret := map[string]string{}
-
- // Iterate over this android.Module's property structs.
- for _, properties := range aModule.GetProperties() {
- propertiesValue := reflect.ValueOf(properties)
- // Check that propertiesValue is a pointer to the Properties struct, like
- // *cc.BaseLinkerProperties or *java.CompilerProperties.
- //
- // propertiesValue can also be type-asserted to the structs to
- // manipulate internal props, if needed.
- if isStructPtr(propertiesValue.Type()) {
- structValue := propertiesValue.Elem()
- for k, v := range extractStructProperties(structValue, 0) {
- ret[k] = v
- }
- } else {
- panic(fmt.Errorf(
- "properties must be a pointer to a struct, got %T",
- propertiesValue.Interface()))
- }
-
- }
-
- return ret
-}
-
-// FIXME(b/168089390): In Bazel, rules ending with "_test" needs to be marked as
-// testonly = True, forcing other rules that depend on _test rules to also be
-// marked as testonly = True. This semantic constraint is not present in Soong.
-// To work around, rename "*_test" rules to "*_test_".
-func canonicalizeModuleType(moduleName string) string {
- if strings.HasSuffix(moduleName, "_test") {
- return moduleName + "_"
- }
-
- return moduleName
-}
-
-type RuleShim struct {
- // The rule class shims contained in a bzl file. e.g. ["cc_object", "cc_library", ..]
- rules []string
-
- // The generated string content of the bzl file.
- content string
-}
-
-// Create <module>.bzl containing Bazel rule shims for every module type available in Soong and
-// user-specified Go plugins.
-//
-// This function reuses documentation generation APIs to ensure parity between modules-as-docs
-// and modules-as-code, including the names and types of module properties.
-func createRuleShims(packages []*bpdoc.Package) (map[string]RuleShim, error) {
- var propToAttr func(prop bpdoc.Property, propName string) string
- propToAttr = func(prop bpdoc.Property, propName string) string {
- // dots are not allowed in Starlark attribute names. Substitute them with double underscores.
- propName = strings.ReplaceAll(propName, ".", "__")
- if !shouldGenerateAttribute(propName) {
- return ""
- }
-
- // Canonicalize and normalize module property types to Bazel attribute types
- starlarkAttrType := prop.Type
- if starlarkAttrType == "list of string" {
- starlarkAttrType = "string_list"
- } else if starlarkAttrType == "int64" {
- starlarkAttrType = "int"
- } else if starlarkAttrType == "" {
- var attr string
- for _, nestedProp := range prop.Properties {
- nestedAttr := propToAttr(nestedProp, propName+"__"+nestedProp.Name)
- if nestedAttr != "" {
- // TODO(b/167662930): Fix nested props resulting in too many attributes.
- // Let's still generate these, but comment them out.
- attr += "# " + nestedAttr
- }
- }
- return attr
- }
-
- if !allowedPropTypes[starlarkAttrType] {
- return ""
- }
-
- return fmt.Sprintf(" %q: attr.%s(),\n", propName, starlarkAttrType)
- }
-
- ruleShims := map[string]RuleShim{}
- for _, pkg := range packages {
- content := "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
-
- bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
- bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
- bzlFileName = strings.ReplaceAll(bzlFileName, "/", "_")
-
- rules := []string{}
-
- for _, moduleTypeTemplate := range moduleTypeDocsToTemplates(pkg.ModuleTypes) {
- attrs := `{
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
-`
- for _, prop := range moduleTypeTemplate.Properties {
- attrs += propToAttr(prop, prop.Name)
- }
-
- moduleTypeName := moduleTypeTemplate.Name
-
- // Certain SDK-related module types dynamically inject properties, instead of declaring
- // them as structs. These properties are registered in an SdkMemberTypesRegistry. If
- // the module type name matches, add these properties into the rule definition.
- var registeredTypes []android.SdkMemberType
- if moduleTypeName == "module_exports" || moduleTypeName == "module_exports_snapshot" {
- registeredTypes = android.ModuleExportsMemberTypes.RegisteredTypes()
- } else if moduleTypeName == "sdk" || moduleTypeName == "sdk_snapshot" {
- registeredTypes = android.SdkMemberTypes.RegisteredTypes()
- }
- for _, memberType := range registeredTypes {
- attrs += fmt.Sprintf(" %q: attr.string_list(),\n", memberType.SdkPropertyName())
- }
-
- attrs += " },"
-
- rule := canonicalizeModuleType(moduleTypeTemplate.Name)
- content += fmt.Sprintf(moduleRuleShim, rule, attrs)
- rules = append(rules, rule)
- }
-
- ruleShims[bzlFileName] = RuleShim{content: content, rules: rules}
- }
- return ruleShims, nil
-}
-
func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
- blueprintCtx := ctx.Context
- blueprintCtx.VisitAllModules(func(module blueprint.Module) {
- buildFile, err := buildFileForModule(blueprintCtx, module, bazelQueryViewDir)
- if err != nil {
- panic(err)
- }
+ ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
+ buildToTargets := bp2build.GenerateSoongModuleTargets(*ctx, false)
- buildFile.Write([]byte(generateSoongModuleTarget(blueprintCtx, module) + "\n\n"))
- buildFile.Close()
- })
- var err error
-
- // Write top level files: WORKSPACE and BUILD. These files are empty.
- if err = writeReadOnlyFile(bazelQueryViewDir, "WORKSPACE", ""); err != nil {
- return err
- }
-
- // Used to denote that the top level directory is a package.
- if err = writeReadOnlyFile(bazelQueryViewDir, "BUILD", ""); err != nil {
- return err
- }
-
- packages, err := getPackages(ctx)
- if err != nil {
- return err
- }
- ruleShims, err := createRuleShims(packages)
- if err != nil {
- return err
- }
-
- // Write .bzl Starlark files into the bazel_rules top level directory (provider and rule definitions)
- bazelRulesDir := bazelQueryViewDir + "/build/bazel/queryview_rules"
- if err = writeReadOnlyFile(bazelRulesDir, "BUILD", ""); err != nil {
- return err
- }
- if err = writeReadOnlyFile(bazelRulesDir, "providers.bzl", providersBzl); err != nil {
- return err
- }
-
- for bzlFileName, ruleShim := range ruleShims {
- if err = writeReadOnlyFile(bazelRulesDir, bzlFileName+".bzl", ruleShim.content); err != nil {
+ filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, false)
+ for _, f := range filesToWrite {
+ if err := writeReadOnlyFile(bazelQueryViewDir, f); err != nil {
return err
}
}
- return writeReadOnlyFile(bazelRulesDir, "soong_module.bzl", generateSoongModuleBzl(ruleShims))
+ return nil
}
-// Generate the content of soong_module.bzl with the rule shim load statements
-// and mapping of module_type to rule shim map for every module type in Soong.
-func generateSoongModuleBzl(bzlLoads map[string]RuleShim) string {
- var loadStmts string
- var moduleRuleMap string
- for bzlFileName, ruleShim := range bzlLoads {
- loadStmt := "load(\"//build/bazel/queryview_rules:"
- loadStmt += bzlFileName
- loadStmt += ".bzl\""
- for _, rule := range ruleShim.rules {
- loadStmt += fmt.Sprintf(", %q", rule)
- moduleRuleMap += " \"" + rule + "\": " + rule + ",\n"
- }
- loadStmt += ")\n"
- loadStmts += loadStmt
- }
-
- return fmt.Sprintf(soongModuleBzl, loadStmts, moduleRuleMap)
-}
-
-func shouldGenerateAttribute(prop string) bool {
- return !ignoredPropNames[prop]
-}
-
-// props is an unsorted map. This function ensures that
-// the generated attributes are sorted to ensure determinism.
-func propsToAttributes(props map[string]string) string {
- var attributes string
- for _, propName := range android.SortedStringKeys(props) {
- if shouldGenerateAttribute(propName) {
- attributes += fmt.Sprintf(" %s = %s,\n", propName, props[propName])
- }
- }
- return attributes
-}
-
-// Convert a module and its deps and props into a Bazel macro/rule
-// representation in the BUILD file.
-func generateSoongModuleTarget(
- blueprintCtx *blueprint.Context,
- module blueprint.Module) string {
-
- var props map[string]string
- if aModule, ok := module.(android.Module); ok {
- props = extractModuleProperties(aModule)
- }
- attributes := propsToAttributes(props)
-
- // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
- // items, if the modules are added using different DependencyTag. Figure
- // out the implications of that.
- depLabels := map[string]bool{}
- blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
- depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
- })
-
- depLabelList := "[\n"
- for depLabel, _ := range depLabels {
- depLabelList += fmt.Sprintf(" %q,\n", depLabel)
- }
- depLabelList += " ]"
-
- return fmt.Sprintf(
- soongModuleTarget,
- targetNameWithVariant(blueprintCtx, module),
- blueprintCtx.ModuleName(module),
- canonicalizeModuleType(blueprintCtx.ModuleType(module)),
- blueprintCtx.ModuleSubDir(module),
- depLabelList,
- attributes)
-}
-
-func buildFileForModule(
- ctx *blueprint.Context, module blueprint.Module, bazelQueryViewDir string) (*os.File, error) {
- // Create nested directories for the BUILD file
- dirPath := filepath.Join(bazelQueryViewDir, packagePath(ctx, module))
- createDirectoryIfNonexistent(dirPath)
- // Open the file for appending, and create it if it doesn't exist
- f, err := os.OpenFile(
- filepath.Join(dirPath, "BUILD.bazel"),
- os.O_APPEND|os.O_CREATE|os.O_WRONLY,
- 0644)
- if err != nil {
- return nil, err
- }
-
- // If the file is empty, add the load statement for the `soong_module` rule
- fi, err := f.Stat()
- if err != nil {
- return nil, err
- }
- if fi.Size() == 0 {
- f.Write([]byte(soongModuleLoad + "\n"))
- }
-
- return f, nil
-}
-
-func createDirectoryIfNonexistent(dir string) {
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- os.MkdirAll(dir, os.ModePerm)
- }
-}
-
-// The QueryView directory should be read-only, sufficient for bazel query. The files
+// The auto-conversion directory should be read-only, sufficient for bazel query. The files
// are not intended to be edited by end users.
-func writeReadOnlyFile(dir string, baseName string, content string) error {
- createDirectoryIfNonexistent(dir)
- pathToFile := filepath.Join(dir, baseName)
+func writeReadOnlyFile(dir string, f bp2build.BazelFile) error {
+ dir = filepath.Join(dir, f.Dir)
+ if err := createDirectoryIfNonexistent(dir); err != nil {
+ return err
+ }
+ pathToFile := filepath.Join(dir, f.Basename)
+
// 0444 is read-only
- return ioutil.WriteFile(pathToFile, []byte(content), 0444)
+ err := ioutil.WriteFile(pathToFile, []byte(f.Contents), 0444)
+
+ return err
}
-func isZero(value reflect.Value) bool {
- switch value.Kind() {
- case reflect.Func, reflect.Map, reflect.Slice:
- return value.IsNil()
- case reflect.Array:
- valueIsZero := true
- for i := 0; i < value.Len(); i++ {
- valueIsZero = valueIsZero && isZero(value.Index(i))
- }
- return valueIsZero
- case reflect.Struct:
- valueIsZero := true
- for i := 0; i < value.NumField(); i++ {
- if value.Field(i).CanSet() {
- valueIsZero = valueIsZero && isZero(value.Field(i))
- }
- }
- return valueIsZero
- case reflect.Ptr:
- if !value.IsNil() {
- return isZero(reflect.Indirect(value))
- } else {
- return true
- }
- default:
- zeroValue := reflect.Zero(value.Type())
- result := value.Interface() == zeroValue.Interface()
- return result
+func createDirectoryIfNonexistent(dir string) error {
+ if _, err := os.Stat(dir); os.IsNotExist(err) {
+ return os.MkdirAll(dir, os.ModePerm)
+ } else {
+ return err
}
}
diff --git a/cmd/soong_build/queryview_test.go b/cmd/soong_build/queryview_test.go
deleted file mode 100644
index 9471a91..0000000
--- a/cmd/soong_build/queryview_test.go
+++ /dev/null
@@ -1,470 +0,0 @@
-// 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 main
-
-import (
- "android/soong/android"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/google/blueprint/bootstrap/bpdoc"
-)
-
-var buildDir string
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
-func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
-}
-
-type customModule struct {
- android.ModuleBase
-}
-
-// OutputFiles is needed because some instances of this module use dist with a
-// tag property which requires the module implements OutputFileProducer.
-func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
- return android.PathsForTesting("path" + tag), nil
-}
-
-func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // nothing for now.
-}
-
-func customModuleFactory() android.Module {
- module := &customModule{}
- android.InitAndroidModule(module)
- return module
-}
-
-func TestGenerateBazelQueryViewFromBlueprint(t *testing.T) {
- testCases := []struct {
- bp string
- expectedBazelTarget string
- }{
- {
- bp: `custom {
- name: "foo",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- ramdisk: true,
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- ramdisk = True,
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- required = [
- "bar",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- target_required: ["qux", "bazqux"],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- dist: {
- targets: ["goal_foo"],
- tag: ".foo",
- },
- dists: [
- {
- targets: ["goal_bar"],
- tag: ".bar",
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dist = {
- "tag": ".foo",
- "targets": [
- "goal_foo",
- ],
- },
- dists = [
- {
- "tag": ".bar",
- "targets": [
- "goal_bar",
- ],
- },
- ],
-)`,
- },
- {
- bp: `custom {
- name: "foo",
- required: ["bar"],
- target_required: ["qux", "bazqux"],
- ramdisk: true,
- owner: "custom_owner",
- dists: [
- {
- tag: ".tag",
- targets: ["my_goal"],
- },
- ],
-}
- `,
- expectedBazelTarget: `soong_module(
- name = "foo",
- module_name = "foo",
- module_type = "custom",
- module_variant = "",
- module_deps = [
- ],
- dists = [
- {
- "tag": ".tag",
- "targets": [
- "my_goal",
- ],
- },
- ],
- owner = "custom_owner",
- ramdisk = True,
- required = [
- "bar",
- ],
- target_required = [
- "qux",
- "bazqux",
- ],
-)`,
- },
- }
-
- for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register()
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
-
- module := ctx.ModuleForTests("foo", "").Module().(*customModule)
- blueprintCtx := ctx.Context.Context
-
- actualBazelTarget := generateSoongModuleTarget(blueprintCtx, module)
- if actualBazelTarget != testCase.expectedBazelTarget {
- t.Errorf(
- "Expected generated Bazel target to be '%s', got '%s'",
- testCase.expectedBazelTarget,
- actualBazelTarget,
- )
- }
- }
-}
-
-func createPackageFixtures() []*bpdoc.Package {
- properties := []bpdoc.Property{
- bpdoc.Property{
- Name: "int64_prop",
- Type: "int64",
- },
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- bpdoc.Property{
- Name: "string_list_prop",
- Type: "list of string",
- },
- bpdoc.Property{
- Name: "nested_prop",
- Type: "",
- Properties: []bpdoc.Property{
- bpdoc.Property{
- Name: "int_prop",
- Type: "int",
- },
- bpdoc.Property{
- Name: "bool_prop",
- Type: "bool",
- },
- bpdoc.Property{
- Name: "string_prop",
- Type: "string",
- },
- },
- },
- bpdoc.Property{
- Name: "unknown_type",
- Type: "unknown",
- },
- }
-
- fooPropertyStruct := &bpdoc.PropertyStruct{
- Name: "FooProperties",
- Properties: properties,
- }
-
- moduleTypes := []*bpdoc.ModuleType{
- &bpdoc.ModuleType{
- Name: "foo_library",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
-
- &bpdoc.ModuleType{
- Name: "foo_binary",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- &bpdoc.ModuleType{
- Name: "foo_test",
- PropertyStructs: []*bpdoc.PropertyStruct{
- fooPropertyStruct,
- },
- },
- }
-
- return [](*bpdoc.Package){
- &bpdoc.Package{
- Name: "foo_language",
- Path: "android/soong/foo",
- ModuleTypes: moduleTypes,
- },
- }
-}
-
-func TestGenerateModuleRuleShims(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
-
- if len(ruleShims) != 1 {
- t.Errorf("Expected to generate 1 rule shim, but got %d", len(ruleShims))
- }
-
- fooRuleShim := ruleShims["foo"]
- expectedRules := []string{"foo_binary", "foo_library", "foo_test_"}
-
- if len(fooRuleShim.rules) != 3 {
- t.Errorf("Expected 3 rules, but got %d", len(fooRuleShim.rules))
- }
-
- for i, rule := range fooRuleShim.rules {
- if rule != expectedRules[i] {
- t.Errorf("Expected rule shim to contain %s, but got %s", expectedRules[i], rule)
- }
- }
-
- expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
-
-def _foo_binary_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_binary = rule(
- implementation = _foo_binary_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_library_impl(ctx):
- return [SoongModuleInfo()]
-
-foo_library = rule(
- implementation = _foo_library_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-
-def _foo_test__impl(ctx):
- return [SoongModuleInfo()]
-
-foo_test_ = rule(
- implementation = _foo_test__impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- "bool_prop": attr.bool(),
- "int64_prop": attr.int(),
- "int_prop": attr.int(),
-# "nested_prop__int_prop": attr.int(),
-# "nested_prop__bool_prop": attr.bool(),
-# "nested_prop__string_prop": attr.string(),
- "string_list_prop": attr.string_list(),
- "string_prop": attr.string(),
- },
-)
-`
-
- if fooRuleShim.content != expectedBzl {
- t.Errorf(
- "Expected the generated rule shim bzl to be:\n%s\nbut got:\n%s",
- expectedBzl,
- fooRuleShim.content)
- }
-}
-
-func TestGenerateSoongModuleBzl(t *testing.T) {
- ruleShims, err := createRuleShims(createPackageFixtures())
- if err != nil {
- panic(err)
- }
- actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
-
- expectedLoad := "load(\"//build/bazel/queryview_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
- expectedRuleMap := `soong_module_rule_map = {
- "foo_binary": foo_binary,
- "foo_library": foo_library,
- "foo_test_": foo_test_,
-}`
- if !strings.Contains(actualSoongModuleBzl, expectedLoad) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the load statement in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedLoad)
- }
-
- if !strings.Contains(actualSoongModuleBzl, expectedRuleMap) {
- t.Errorf(
- "Generated soong_module.bzl:\n\n%s\n\n"+
- "Could not find the module -> rule map in the generated soong_module.bzl:\n%s",
- actualSoongModuleBzl,
- expectedRuleMap)
- }
-}
diff --git a/cmd/soong_build/writedocs.go b/cmd/soong_build/writedocs.go
index 5fb6e6b..f2c2c9b 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -20,7 +20,6 @@
"html/template"
"io/ioutil"
"path/filepath"
- "reflect"
"sort"
"github.com/google/blueprint/bootstrap"
@@ -44,9 +43,10 @@
"name": 0,
"src": 1,
"srcs": 2,
- "defaults": 3,
- "host_supported": 4,
- "device_supported": 5,
+ "exclude_srcs": 3,
+ "defaults": 4,
+ "host_supported": 5,
+ "device_supported": 6,
}
// For each module type, extract its documentation and convert it to the template data.
@@ -96,12 +96,8 @@
}
func getPackages(ctx *android.Context) ([]*bpdoc.Package, error) {
- moduleTypeFactories := android.ModuleTypeFactories()
- bpModuleTypeFactories := make(map[string]reflect.Value)
- for moduleType, factory := range moduleTypeFactories {
- bpModuleTypeFactories[moduleType] = reflect.ValueOf(factory)
- }
- return bootstrap.ModuleTypeDocs(ctx.Context, bpModuleTypeFactories)
+ moduleTypeFactories := android.ModuleTypeFactoriesForDocs()
+ return bootstrap.ModuleTypeDocs(ctx.Context, moduleTypeFactories)
}
func writeDocs(ctx *android.Context, filename string) error {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 29030d6..74ede68 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -18,6 +18,7 @@
"context"
"flag"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -173,16 +174,18 @@
build.SetupOutDir(buildCtx, config)
- // Set up files to be outputted in the log directory.
- logsDir := config.OutDir()
- if config.Dist() {
- logsDir = filepath.Join(config.DistDir(), "logs")
+ if config.UseBazel() && config.Dist() {
+ defer populateExternalDistDir(buildCtx, config)
}
+ // Set up files to be outputted in the log directory.
+ logsDir := config.LogsDir()
+
+ // Common list of metric file definition.
buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
- defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+
build.PrintOutDirWarning(buildCtx, config)
os.MkdirAll(logsDir, 0777)
@@ -198,8 +201,22 @@
buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
- defer met.Dump(soongMetricsFile)
- defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
+ {
+ // The order of the function calls is important. The last defer function call
+ // is the first one that is executed to save the rbe metrics to a protobuf
+ // file. The soong metrics file is then next. Bazel profiles are written
+ // before the uploadMetrics is invoked. The written files are then uploaded
+ // if the uploading of the metrics is enabled.
+ files := []string{
+ buildErrorFile, // build error strings
+ rbeMetricsFile, // high level metrics related to remote build execution.
+ soongMetricsFile, // high level metrics related to this build system.
+ config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
+ }
+ defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
+ defer met.Dump(soongMetricsFile)
+ defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
+ }
// Read the time at the starting point.
if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
@@ -513,3 +530,78 @@
// command not found
return nil, nil, fmt.Errorf("Command not found: %q", args)
}
+
+// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
+func populateExternalDistDir(ctx build.Context, config build.Config) {
+ // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
+ var err error
+ var internalDistDirPath string
+ var externalDistDirPath string
+ if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
+ ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
+ }
+ if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
+ ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
+ }
+ if externalDistDirPath == internalDistDirPath {
+ return
+ }
+
+ // Make sure the internal DIST_DIR actually exists before trying to read from it
+ if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
+ ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
+ return
+ }
+
+ // Make sure the external DIST_DIR actually exists before trying to write to it
+ if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
+ ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
+ }
+
+ ctx.Println("Populating external DIST_DIR...")
+
+ populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
+}
+
+func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
+ files, err := ioutil.ReadDir(internalDistDirPath)
+ if err != nil {
+ ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
+ }
+ for _, f := range files {
+ internalFilePath := filepath.Join(internalDistDirPath, f.Name())
+ externalFilePath := filepath.Join(externalDistDirPath, f.Name())
+
+ if f.IsDir() {
+ // Moving a directory - check if there is an existing directory to merge with
+ externalLstat, err := os.Lstat(externalFilePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
+ }
+ // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
+ } else {
+ if externalLstat.IsDir() {
+ // Existing dir - try to merge the directories?
+ populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
+ continue
+ } else {
+ // Existing file being replaced with a directory. Delete the existing file...
+ if err := os.RemoveAll(externalFilePath); err != nil {
+ ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
+ }
+ }
+ }
+ } else {
+ // Moving a file (not a dir) - delete any existing file or directory
+ if err := os.RemoveAll(externalFilePath); err != nil {
+ ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
+ }
+ }
+
+ // The actual move - do a rename instead of a copy in order to save disk space.
+ if err := os.Rename(internalFilePath, externalFilePath); err != nil {
+ ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
+ }
+ }
+}
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 3759217..532d8fc 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "sort"
"strconv"
"strings"
@@ -238,8 +239,8 @@
AndroidTestMock,
}
var CompatUsesLibs29 = []string{
- AndroidHidlBase,
AndroidHidlManager,
+ AndroidHidlBase,
}
var OptionalCompatUsesLibs = append(android.CopyOf(OptionalCompatUsesLibs28), OptionalCompatUsesLibs30...)
var CompatUsesLibs = android.CopyOf(CompatUsesLibs29)
@@ -254,24 +255,13 @@
// Add class loader context for the given library to the map entry for the given SDK version.
func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
-
- // If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
- // not found. However, this is likely to result is disabling dexpreopt, as it won't be
- // possible to construct class loader context without on-host and on-device library paths.
- strict = strict && !ctx.Config().AllowMissingDependencies()
-
- if hostPath == nil && strict {
- return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
- }
+ hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
devicePath := UnknownInstallLibraryPath
if installPath == nil {
if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
// Assume that compatibility libraries are installed in /system/framework.
installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
- } else if strict {
- return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
} else {
// For some stub libraries the only known thing is the name of their implementation
// library, but the library itself is unavailable (missing or part of a prebuilt). In
@@ -309,39 +299,19 @@
return nil
}
-// Wrapper around addContext that reports errors.
-func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
- hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
+// Add class loader context for the given SDK version. Don't fail on unknown build/install paths, as
+// libraries with unknown paths still need to be processed by manifest_fixer (which doesn't care
+// about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
+// are validated later before CLC is used (in validateClassLoaderContext).
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
+ lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
- err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
+ err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
if err != nil {
ctx.ModuleErrorf(err.Error())
}
}
-// Add class loader context. Fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path) {
-
- clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
-}
-
-// Add class loader context if the library exists. Don't fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
- hostPath, installPath android.Path) {
-
- if lib != nil {
- clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
- }
-}
-
-// Add class loader context for the given SDK version. Fail on unknown build/install paths.
-func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
- lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
-
- clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
-}
-
// Merge the other class loader context map into this one, do not override existing entries.
// The implicitRootLib parameter is the name of the library for which the other class loader
// context map was constructed. If the implicitRootLib is itself a <uses-library>, it should be
@@ -437,7 +407,11 @@
if sdkVer == AnySdkVersion {
// Return error if dexpreopt doesn't know paths to one of the <uses-library>
// dependencies. In the future we may need to relax this and just disable dexpreopt.
- return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
+ if clc.Host == nil {
+ return false, fmt.Errorf("invalid build path for <uses-library> \"%s\"", clc.Name)
+ } else {
+ return false, fmt.Errorf("invalid install path for <uses-library> \"%s\"", clc.Name)
+ }
} else {
// No error for compatibility libraries, as Soong doesn't know if they are needed
// (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
@@ -455,7 +429,26 @@
// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
// Return the resulting string and a slice of on-host build paths to all library dependencies.
func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
- for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
+ // CLC for different SDK versions should come in specific order that agrees with PackageManager.
+ // Since PackageManager processes SDK versions in ascending order and prepends compatibility
+ // libraries at the front, the required order is descending, except for AnySdkVersion that has
+ // numerically the largest order, but must be the last one. Example of correct order: [30, 29,
+ // 28, AnySdkVersion]. There are Soong tests to ensure that someone doesn't change this by
+ // accident, but there is no way to guard against changes in the PackageManager, except for
+ // grepping logcat on the first boot for absence of the following messages:
+ //
+ // `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
+ //
+ versions := make([]int, 0, len(clcMap))
+ for ver, _ := range clcMap {
+ if ver != AnySdkVersion {
+ versions = append(versions, ver)
+ }
+ }
+ sort.Sort(sort.Reverse(sort.IntSlice(versions))) // descending order
+ versions = append(versions, AnySdkVersion)
+
+ for _, sdkVer := range versions {
sdkVerStr := fmt.Sprintf("%d", sdkVer)
if sdkVer == AnySdkVersion {
sdkVerStr = "any" // a special keyword that means any SDK version
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index df68563..86f7871 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -18,6 +18,7 @@
// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
import (
+ "fmt"
"reflect"
"strings"
"testing"
@@ -50,36 +51,30 @@
m := make(ClassLoaderContextMap)
- m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
- m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
-
- // "Maybe" variant in the good case: add as usual.
- c := "c"
- m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
-
- // "Maybe" variant in the bad case: don't add library with unknown name, keep going.
- m.MaybeAddContext(ctx, nil, nil, nil)
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
// Add some libraries with nested subcontexts.
m1 := make(ClassLoaderContextMap)
- m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
- m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+ m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+ m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
m2 := make(ClassLoaderContextMap)
- m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
- m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
- m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+ m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+ m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
m3 := make(ClassLoaderContextMap)
- m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
- m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+ m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+ m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
// When the same library is both in conditional and unconditional context, it should be removed
// from conditional context.
- m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
- m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
// Merge map with implicit root library that is among toplevel contexts => does nothing.
m.AddContextMap(m1, "c")
@@ -88,12 +83,12 @@
m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
- m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+ m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
valid, validationError := validateClassLoaderContext(m)
@@ -160,31 +155,19 @@
})
}
-// Test that an unexpected unknown build path causes immediate error.
-func TestCLCUnknownBuildPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
- checkError(t, err, "unknown build path to <uses-library> \"a\"")
-}
-
-// Test that an unexpected unknown install path causes immediate error.
-func TestCLCUnknownInstallPath(t *testing.T) {
- ctx := testContext()
- m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
- checkError(t, err, "unknown install path to <uses-library> \"a\"")
-}
-
-func TestCLCMaybeAdd(t *testing.T) {
+// Test that unknown library paths cause a validation error.
+func testCLCUnknownPath(t *testing.T, whichPath string) {
ctx := testContext()
m := make(ClassLoaderContextMap)
- a := "a"
- m.MaybeAddContext(ctx, &a, nil, nil)
+ if whichPath == "build" {
+ m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+ } else {
+ m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+ }
// The library should be added to <uses-library> tags by the manifest_fixer.
- t.Run("maybe add", func(t *testing.T) {
+ t.Run("uses libs", func(t *testing.T) {
haveUsesLibs := m.UsesLibs()
wantUsesLibs := []string{"a"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
@@ -192,23 +175,73 @@
}
})
- // But class loader context in such cases should raise an error on validation.
- t.Run("validate", func(t *testing.T) {
- _, err := validateClassLoaderContext(m)
- checkError(t, err, "invalid path for <uses-library> \"a\"")
- })
+ // But CLC cannot be constructed: there is a validation error.
+ _, err := validateClassLoaderContext(m)
+ checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath))
+}
+
+// Test that unknown build path is an error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+ testCLCUnknownPath(t, "build")
+}
+
+// Test that unknown install path is an error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+ testCLCUnknownPath(t, "install")
}
// An attempt to add conditional nested subcontext should fail.
func TestCLCNestedConditional(t *testing.T) {
ctx := testContext()
m1 := make(ClassLoaderContextMap)
- m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
m := make(ClassLoaderContextMap)
- err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+ err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
checkError(t, err, "nested class loader context shouldn't have conditional part")
}
+// Test for SDK version order in conditional CLC: no matter in what order the libraries are added,
+// they end up in the order that agrees with PackageManager.
+func TestCLCSdkVersionOrder(t *testing.T) {
+ ctx := testContext()
+ m := make(ClassLoaderContextMap)
+ m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+ m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+ m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+
+ valid, validationError := validateClassLoaderContext(m)
+
+ fixClassLoaderContext(m)
+
+ var haveStr string
+ if valid && validationError == nil {
+ haveStr, _ = ComputeClassLoaderContext(m)
+ }
+
+ // Test that validation is successful (all paths are known).
+ t.Run("validate", func(t *testing.T) {
+ if !(valid && validationError == nil) {
+ t.Errorf("invalid class loader context")
+ }
+ })
+
+ // Test that class loader context structure is correct.
+ t.Run("string", func(t *testing.T) {
+ wantStr := " --host-context-for-sdk 30 PCL[out/c.jar]" +
+ " --target-context-for-sdk 30 PCL[/system/c.jar]" +
+ " --host-context-for-sdk 29 PCL[out/b.jar]" +
+ " --target-context-for-sdk 29 PCL[/system/b.jar]" +
+ " --host-context-for-sdk 28 PCL[out/a.jar]" +
+ " --target-context-for-sdk 28 PCL[/system/a.jar]" +
+ " --host-context-for-sdk any PCL[out/d.jar]" +
+ " --target-context-for-sdk any PCL[/system/d.jar]"
+ if wantStr != haveStr {
+ t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
+ }
+ })
+}
+
func checkError(t *testing.T, have error, want string) {
if have == nil {
t.Errorf("\nwant error: '%s'\nhave: none", want)
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 c6181bc..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,12 +56,16 @@
// 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
}
-var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+var dependencyTag = struct {
+ blueprint.BaseDependencyTag
+ android.InstallAlwaysNeededDependencyTag
+}{}
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
f.AddDeps(ctx, dependencyTag)
@@ -69,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)
-
- f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ 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
@@ -94,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
@@ -109,3 +169,16 @@
},
}}
}
+
+// Filesystem is the public interface for the filesystem struct. Currently, it's only for the apex
+// package to have access to the output file.
+type Filesystem interface {
+ android.Module
+ OutputPath() android.Path
+}
+
+var _ Filesystem = (*filesystem)(nil)
+
+func (f *filesystem) OutputPath() android.Path {
+ return f.output
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 93938c9..8df32f2 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -139,7 +139,6 @@
// number of shards the input files are sharded into.
taskGenerator taskFunc
- deps android.Paths
rule blueprint.Rule
rawCommands []string
@@ -206,7 +205,7 @@
if ok {
var bazelOutputFiles android.Paths
for _, bazelOutputFile := range filePaths {
- bazelOutputFiles = append(bazelOutputFiles, android.PathForSource(ctx, bazelOutputFile))
+ bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
}
c.outputFiles = bazelOutputFiles
c.outputDeps = bazelOutputFiles
@@ -244,6 +243,8 @@
}
}
+ var tools android.Paths
+ var packagedTools []android.PackagingSpec
if len(g.properties.Tools) > 0 {
seenTools := make(map[string]bool)
@@ -251,37 +252,52 @@
switch tag := ctx.OtherModuleDependencyTag(module).(type) {
case hostToolDependencyTag:
tool := ctx.OtherModuleName(module)
- var path android.OptionalPath
- if t, ok := module.(android.HostToolProvider); ok {
+ switch t := module.(type) {
+ case android.HostToolProvider:
+ // A HostToolProvider provides the path to a tool, which will be copied
+ // into the sandbox.
if !t.(android.Module).Enabled() {
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{tool})
} else {
ctx.ModuleErrorf("depends on disabled module %q", tool)
}
- break
+ return
}
- path = t.HostToolPath()
- } else if t, ok := module.(bootstrap.GoBinaryTool); ok {
+ path := t.HostToolPath()
+ if !path.Valid() {
+ ctx.ModuleErrorf("host tool %q missing output file", tool)
+ return
+ }
+ if specs := t.TransitivePackagingSpecs(); specs != nil {
+ // If the HostToolProvider has PackgingSpecs, which are definitions of the
+ // required relative locations of the tool and its dependencies, use those
+ // instead. They will be copied to those relative locations in the sbox
+ // sandbox.
+ packagedTools = append(packagedTools, specs...)
+ // Assume that the first PackagingSpec of the module is the tool.
+ addLocationLabel(tag.label, []string{android.SboxPathForPackagedTool(specs[0])})
+ } else {
+ tools = append(tools, path.Path())
+ addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, path.Path())})
+ }
+ case bootstrap.GoBinaryTool:
+ // A GoBinaryTool provides the install path to a tool, which will be copied.
if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
- path = android.OptionalPathForPath(android.PathForOutput(ctx, s))
+ toolPath := android.PathForOutput(ctx, s)
+ tools = append(tools, toolPath)
+ addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, toolPath)})
} else {
ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
- break
+ return
}
- } else {
+ default:
ctx.ModuleErrorf("%q is not a host tool provider", tool)
- break
+ return
}
- if path.Valid() {
- g.deps = append(g.deps, path.Path())
- addLocationLabel(tag.label, []string{path.Path().String()})
- seenTools[tag.label] = true
- } else {
- ctx.ModuleErrorf("host tool %q missing output file", tool)
- }
+ seenTools[tag.label] = true
}
})
@@ -305,8 +321,12 @@
for _, toolFile := range g.properties.Tool_files {
paths := android.PathsForModuleSrc(ctx, []string{toolFile})
- g.deps = append(g.deps, paths...)
- addLocationLabel(toolFile, paths.Strings())
+ tools = append(tools, paths...)
+ var sandboxPaths []string
+ for _, path := range paths {
+ sandboxPaths = append(sandboxPaths, android.SboxPathForTool(ctx, path))
+ }
+ addLocationLabel(toolFile, sandboxPaths)
}
var srcFiles android.Paths
@@ -358,7 +378,7 @@
manifestPath := android.PathForModuleOut(ctx, manifestName)
// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
- rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
cmd := rule.Command()
for _, out := range task.out {
@@ -448,8 +468,9 @@
cmd.Text(rawCommand)
cmd.ImplicitOutputs(task.out)
cmd.Implicits(task.in)
- cmd.Implicits(g.deps)
- cmd.Implicits(task.extraTools)
+ cmd.ImplicitTools(tools)
+ cmd.ImplicitTools(task.extraTools)
+ cmd.ImplicitPackagedTools(packagedTools)
if Bool(g.properties.Depfile) {
cmd.ImplicitDepFile(task.depFile)
}
@@ -547,6 +568,9 @@
}
}
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// Because generated outputs are checked by client modules(e.g. cc_library, ...)
@@ -616,7 +640,7 @@
// TODO(ccross): this RuleBuilder is a hack to be able to call
// rule.Command().PathForOutput. Replace this with passing the rule into the
// generator.
- rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil)
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
for _, in := range shard {
outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -669,7 +693,8 @@
outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
- depFixerTool.String(), strings.Join(commandDepFiles, " "))
+ android.SboxPathForTool(ctx, depFixerTool),
+ strings.Join(commandDepFiles, " "))
extraTools = append(extraTools, depFixerTool)
}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 8d3cfcb..2f5605e 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -141,7 +141,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "empty location tool2",
@@ -150,7 +150,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "empty location tool file",
@@ -159,7 +159,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "empty location tool file fg",
@@ -168,7 +168,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "empty location tool and tool file",
@@ -178,7 +178,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool",
@@ -187,7 +187,7 @@
out: ["out"],
cmd: "$(location tool) > $(out)",
`,
- expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool2",
@@ -196,7 +196,7 @@
out: ["out"],
cmd: "$(location :tool) > $(out)",
`,
- expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool file",
@@ -205,7 +205,7 @@
out: ["out"],
cmd: "$(location tool_file1) > $(out)",
`,
- expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool file fg",
@@ -214,7 +214,7 @@
out: ["out"],
cmd: "$(location :1tool_file) > $(out)",
`,
- expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool files",
@@ -223,7 +223,7 @@
out: ["out"],
cmd: "$(locations :tool_files) > $(out)",
`,
- expect: "tool_file1 tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "in1",
@@ -600,7 +600,7 @@
cmd: "$(location) $(in) > $(out)",
`,
cmds: []string{
- "bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
@@ -614,8 +614,8 @@
shard_size: 2,
`,
cmds: []string{
- "bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
- "bash -c 'out/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
},
deps: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
@@ -736,7 +736,8 @@
}
gen := ctx.ModuleForTests("foo", "").Module().(*Module)
- expectedOutputFiles := []string{"bazelone.txt", "bazeltwo.txt"}
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
+ "outputbase/execroot/__main__/bazeltwo.txt"}
if !reflect.DeepEqual(gen.outputFiles.Strings(), expectedOutputFiles) {
t.Errorf("Expected output files: %q, actual: %q", expectedOutputFiles, gen.outputFiles)
}
@@ -757,7 +758,7 @@
}
func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+ t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
}
func (t *testTool) HostToolPath() android.OptionalPath {
diff --git a/java/Android.bp b/java/Android.bp
index 39502b3..9c28968 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -22,6 +22,8 @@
"androidmk.go",
"app_builder.go",
"app.go",
+ "app_import.go",
+ "app_set.go",
"boot_jars.go",
"builder.go",
"device_host_converter.go",
@@ -46,6 +48,7 @@
"prebuilt_apis.go",
"proto.go",
"robolectric.go",
+ "rro.go",
"sdk.go",
"sdk_library.go",
"sdk_library_external.go",
@@ -57,6 +60,8 @@
],
testSrcs: [
"androidmk_test.go",
+ "app_import_test.go",
+ "app_set_test.go",
"app_test.go",
"device_host_converter_test.go",
"dexpreopt_test.go",
@@ -66,6 +71,7 @@
"jdeps_test.go",
"kotlin_test.go",
"plugin_test.go",
+ "rro_test.go",
"sdk_test.go",
],
pluginFor: ["soong_build"],
diff --git a/java/aar.go b/java/aar.go
index 1940d7f..e3ad252 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "strconv"
"strings"
"android/soong/android"
@@ -192,22 +193,31 @@
rroDirs = append(rroDirs, resRRODirs...)
}
- var assetFiles android.Paths
- for _, dir := range assetDirs {
- assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
+ var assetDeps android.Paths
+ for i, dir := range assetDirs {
+ // Add a dependency on every file in the asset directory. This ensures the aapt2
+ // rule will be rerun if one of the files in the asset directory is modified.
+ assetDeps = append(assetDeps, androidResourceGlob(ctx, dir)...)
+
+ // Add a dependency on a file that contains a list of all the files in the asset directory.
+ // This ensures the aapt2 rule will be run if a file is removed from the asset directory,
+ // or a file is added whose timestamp is older than the output of aapt2.
+ assetFileListFile := android.PathForModuleOut(ctx, "asset_dir_globs", strconv.Itoa(i)+".glob")
+ androidResourceGlobList(ctx, dir, assetFileListFile)
+ assetDeps = append(assetDeps, assetFileListFile)
}
assetDirStrings := assetDirs.Strings()
if a.noticeFile.Valid() {
assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
- assetFiles = append(assetFiles, a.noticeFile.Path())
+ assetDeps = append(assetDeps, a.noticeFile.Path())
}
linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
linkDeps = append(linkDeps, manifestPath)
linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
- linkDeps = append(linkDeps, assetFiles...)
+ linkDeps = append(linkDeps, assetDeps...)
// SDK version flags
minSdkVersion, err := sdkContext.minSdkVersion().effectiveVersionString(ctx)
@@ -406,7 +416,7 @@
}
ctx.VisitDirectDeps(func(module android.Module) {
- depName := ctx.OtherModuleName(module)
+ depTag := ctx.OtherModuleDependencyTag(module)
var exportPackage android.Path
aarDep, _ := module.(AndroidLibraryDependency)
@@ -414,22 +424,13 @@
exportPackage = aarDep.ExportPackage()
}
- switch ctx.OtherModuleDependencyTag(module) {
+ switch depTag {
case instrumentationForTag:
// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
case libTag:
if exportPackage != nil {
sharedLibs = append(sharedLibs, exportPackage)
}
-
- // If the module is (or possibly could be) a component of a java_sdk_library
- // (including the java_sdk_library) itself then append any implicit sdk library
- // names to the list of sdk libraries to be added to the manifest.
- if component, ok := module.(SdkLibraryComponentDependency); ok {
- classLoaderContexts.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(),
- component.DexJarBuildPath(), component.DexJarInstallPath())
- }
-
case frameworkResTag:
if exportPackage != nil {
sharedLibs = append(sharedLibs, exportPackage)
@@ -439,7 +440,6 @@
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
- classLoaderContexts.AddContextMap(aarDep.ClassLoaderContexts(), depName)
if aarDep.ExportedAssets().Valid() {
assets = append(assets, aarDep.ExportedAssets().Path())
}
@@ -458,11 +458,7 @@
}
}
- // Add nested dependencies after processing the direct dependency: if it is a <uses-library>,
- // nested context is added as its subcontext, and should not be re-added at the top-level.
- if dep, ok := module.(Dependency); ok {
- classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), depName)
- }
+ addCLCFromDep(ctx, module, classLoaderContexts)
})
deps = append(deps, sharedLibs...)
@@ -844,10 +840,14 @@
return nil, nil
}
+var _ android.ApexModule = (*AARImport)(nil)
+
+// Implements android.ApexModule
func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return a.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
return nil
diff --git a/java/android_resources.go b/java/android_resources.go
index 720d3a5..4d420cf 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -38,10 +38,21 @@
"*~",
}
+// androidResourceGlob returns the list of files in the given directory, using the standard
+// exclusion patterns for Android resources.
func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths {
return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
}
+// androidResourceGlobList creates a rule to write the list of files in the given directory, using
+// the standard exclusion patterns for Android resources, to the given output file.
+func androidResourceGlobList(ctx android.ModuleContext, dir android.Path,
+ fileListFile android.WritablePath) {
+
+ android.GlobToListFileRule(ctx, filepath.Join(dir.String(), "**/*"),
+ androidResourceIgnoreFilenames, fileListFile)
+}
+
type overlayType int
const (
diff --git a/java/androidmk.go b/java/androidmk.go
index fc573c8..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())
@@ -274,7 +277,7 @@
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
fmt.Fprintln(w, "jar_installed_module := $(LOCAL_INSTALLED_MODULE)")
},
},
@@ -289,7 +292,7 @@
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
// Ensure that the wrapper script timestamp is always updated when the jar is updated
fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): $(jar_installed_module)")
fmt.Fprintln(w, "jar_installed_module :=")
@@ -393,7 +396,7 @@
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
if app.noticeOutputs.Merged.Valid() {
fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
app.installApkName, app.noticeOutputs.Merged.String(), app.installApkName+"_NOTICE")
@@ -548,7 +551,7 @@
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
if dstubs.apiFile != nil {
fmt.Fprintf(w, ".PHONY: %s %s.txt\n", dstubs.Name(), dstubs.Name())
fmt.Fprintf(w, "%s %s.txt: %s\n", dstubs.Name(), dstubs.Name(), dstubs.apiFile)
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/app.go b/java/app.go
index 4bf9d33..e6c9a2d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -14,13 +14,12 @@
package java
-// This file contains the module types for compiling Android apps.
+// This file contains the module implementations for android_app, android_test, and some more
+// related module types, including their override variants.
import (
"path/filepath"
- "reflect"
"sort"
- "strconv"
"strings"
"github.com/google/blueprint"
@@ -32,12 +31,8 @@
"android/soong/tradefed"
)
-var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
-
func init() {
RegisterAppBuildComponents(android.InitRegistrationContext)
-
- initAndroidAppImportVariantGroupTypes()
}
func RegisterAppBuildComponents(ctx android.RegistrationContext) {
@@ -47,139 +42,6 @@
ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
- ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
- ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
- ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
- ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
- ctx.RegisterModuleType("android_app_set", AndroidApkSetFactory)
-}
-
-type AndroidAppSetProperties struct {
- // APK Set path
- Set *string
-
- // Specifies that this app should be installed to the priv-app directory,
- // where the system will grant it additional privileges not available to
- // normal apps.
- Privileged *bool
-
- // APKs in this set use prerelease SDK version
- Prerelease *bool
-
- // Names of modules to be overridden. Listed modules can only be other apps
- // (in Make or Soong).
- Overrides []string
-}
-
-type AndroidAppSet struct {
- android.ModuleBase
- android.DefaultableModuleBase
- prebuilt android.Prebuilt
-
- properties AndroidAppSetProperties
- packedOutput android.WritablePath
- installFile string
- apkcertsFile android.ModuleOutPath
-}
-
-func (as *AndroidAppSet) Name() string {
- return as.prebuilt.Name(as.ModuleBase.Name())
-}
-
-func (as *AndroidAppSet) IsInstallable() bool {
- return true
-}
-
-func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
- return &as.prebuilt
-}
-
-func (as *AndroidAppSet) Privileged() bool {
- return Bool(as.properties.Privileged)
-}
-
-func (as *AndroidAppSet) OutputFile() android.Path {
- return as.packedOutput
-}
-
-func (as *AndroidAppSet) InstallFile() string {
- return as.installFile
-}
-
-func (as *AndroidAppSet) APKCertsFile() android.Path {
- return as.apkcertsFile
-}
-
-var TargetCpuAbi = map[string]string{
- "arm": "ARMEABI_V7A",
- "arm64": "ARM64_V8A",
- "x86": "X86",
- "x86_64": "X86_64",
-}
-
-func SupportedAbis(ctx android.ModuleContext) []string {
- abiName := func(targetIdx int, deviceArch string) string {
- if abi, found := TargetCpuAbi[deviceArch]; found {
- return abi
- }
- ctx.ModuleErrorf("Target %d has invalid Arch: %s", targetIdx, deviceArch)
- return "BAD_ABI"
- }
-
- var result []string
- for i, target := range ctx.Config().Targets[android.Android] {
- result = append(result, abiName(i, target.Arch.ArchType.String()))
- }
- return result
-}
-
-func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
- as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
- // We are assuming here that the install file in the APK
- // set has `.apk` suffix. If it doesn't the build will fail.
- // APK sets containing APEX files are handled elsewhere.
- as.installFile = as.BaseModuleName() + ".apk"
- screenDensities := "all"
- if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
- screenDensities = strings.ToUpper(strings.Join(dpis, ","))
- }
- // TODO(asmundak): handle locales.
- // TODO(asmundak): do we support device features
- ctx.Build(pctx,
- android.BuildParams{
- Rule: extractMatchingApks,
- Description: "Extract APKs from APK set",
- Output: as.packedOutput,
- ImplicitOutput: as.apkcertsFile,
- Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
- Args: map[string]string{
- "abis": strings.Join(SupportedAbis(ctx), ","),
- "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
- "screen-densities": screenDensities,
- "sdk-version": ctx.Config().PlatformSdkVersion().String(),
- "stem": as.BaseModuleName(),
- "apkcerts": as.apkcertsFile.String(),
- "partition": as.PartitionTag(ctx.DeviceConfig()),
- },
- })
-}
-
-// android_app_set extracts a set of APKs based on the target device
-// configuration and installs this set as "split APKs".
-// The extracted set always contains an APK whose name is
-// _module_name_.apk and every split APK matching target device.
-// The extraction of the density-specific splits depends on
-// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
-// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
-// splits will be extracted. Otherwise all density-specific splits
-// will be extracted.
-func AndroidApkSetFactory() android.Module {
- module := &AndroidAppSet{}
- module.AddProperties(&module.properties)
- InitJavaModule(module, android.DeviceSupported)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
- return module
}
// AndroidManifest.xml merging
@@ -273,15 +135,6 @@
Rename_resources_package *bool
}
-// runtime_resource_overlay properties that can be overridden by override_runtime_resource_overlay
-type OverridableRuntimeResourceOverlayProperties struct {
- // the package name of this app. The package name in the manifest file is used if one was not given.
- Package_name *string
-
- // the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
- Target_package_name *string
-}
-
type AndroidApp struct {
Library
aapt
@@ -1052,6 +905,8 @@
&module.appProperties,
&module.overridableAppProperties)
+ module.usesLibrary.enforce = true
+
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
android.InitOverridableModule(module, &module.appProperties.Overrides)
@@ -1298,619 +1153,6 @@
return m
}
-type OverrideRuntimeResourceOverlay struct {
- android.ModuleBase
- android.OverrideModuleBase
-}
-
-func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
- // All the overrides happen in the base module.
- // TODO(jungjw): Check the base module type.
-}
-
-// override_runtime_resource_overlay is used to create a module based on another
-// runtime_resource_overlay module by overriding some of its properties.
-func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
- m := &OverrideRuntimeResourceOverlay{}
- m.AddProperties(&OverridableRuntimeResourceOverlayProperties{})
-
- android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
- android.InitOverrideModule(m)
- return m
-}
-
-type AndroidAppImport struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.ApexModuleBase
- prebuilt android.Prebuilt
-
- properties AndroidAppImportProperties
- dpiVariants interface{}
- archVariants interface{}
-
- outputFile android.Path
- certificate Certificate
-
- dexpreopter
-
- usesLibrary usesLibrary
-
- preprocessed bool
-
- installPath android.InstallPath
-
- hideApexVariantFromMake bool
-}
-
-type AndroidAppImportProperties struct {
- // A prebuilt apk to import
- Apk *string
-
- // The name of a certificate in the default certificate directory or an android_app_certificate
- // module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
- Certificate *string
-
- // Set this flag to true if the prebuilt apk is already signed. The certificate property must not
- // be set for presigned modules.
- Presigned *bool
-
- // Name of the signing certificate lineage file.
- Lineage *string
-
- // Sign with the default system dev certificate. Must be used judiciously. Most imported apps
- // need to either specify a specific certificate or be presigned.
- Default_dev_cert *bool
-
- // Specifies that this app should be installed to the priv-app directory,
- // where the system will grant it additional privileges not available to
- // normal apps.
- Privileged *bool
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-
- // Optional name for the installed app. If unspecified, it is derived from the module name.
- Filename *string
-}
-
-func (a *AndroidAppImport) IsInstallable() bool {
- return true
-}
-
-// Updates properties with variant-specific values.
-func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
- config := ctx.Config()
-
- dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
- // Try DPI variant matches in the reverse-priority order so that the highest priority match
- // overwrites everything else.
- // TODO(jungjw): Can we optimize this by making it priority order?
- for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
- }
- if config.ProductAAPTPreferredConfig() != "" {
- MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
- }
-
- archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
- archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
- MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
-}
-
-func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
- dst interface{}, variantGroup reflect.Value, variant string) {
- src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
- if !src.IsValid() {
- return
- }
-
- err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
- if err != nil {
- if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
- ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
- } else {
- panic(err)
- }
- }
-}
-
-func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- cert := android.SrcIsModule(String(a.properties.Certificate))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- a.usesLibrary.deps(ctx, true)
-}
-
-func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
- // with them may invalidate pre-existing signature data.
- if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Output: outputPath,
- Input: inputPath,
- })
- return
- }
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'lib/**/*.so'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
-}
-
-// Returns whether this module should have the dex file stored uncompressed in the APK.
-func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
- if ctx.Config().UnbundledBuild() || a.preprocessed {
- return false
- }
-
- // Uncompress dex in APKs of privileged apps
- if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
- return true
- }
-
- return shouldUncompressDex(ctx, &a.dexpreopter)
-}
-
-func (a *AndroidAppImport) uncompressDex(
- ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool("zip2zip").
- FlagWithInput("-i ", inputPath).
- FlagWithOutput("-o ", outputPath).
- FlagWithArg("-0 ", "'classes*.dex'").
- Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build("uncompress-dex", "Uncompress dex files")
-}
-
-func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.generateAndroidBuildActions(ctx)
-}
-
-func (a *AndroidAppImport) InstallApkName() string {
- return a.BaseModuleName()
-}
-
-func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
- apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !apexInfo.IsForPlatform() {
- a.hideApexVariantFromMake = true
- }
-
- numCertPropsSet := 0
- if String(a.properties.Certificate) != "" {
- numCertPropsSet++
- }
- if Bool(a.properties.Presigned) {
- numCertPropsSet++
- }
- if Bool(a.properties.Default_dev_cert) {
- numCertPropsSet++
- }
- if numCertPropsSet != 1 {
- ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
- }
-
- _, certificates := collectAppDeps(ctx, a, false, false)
-
- // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
- // TODO: LOCAL_PACKAGE_SPLITS
-
- srcApk := a.prebuilt.SingleSourcePath(ctx)
-
- if a.usesLibrary.enforceUsesLibraries() {
- srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
- }
-
- // TODO: Install or embed JNI libraries
-
- // Uncompress JNI libraries in the apk
- jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
-
- var installDir android.InstallPath
- if Bool(a.properties.Privileged) {
- installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
- } else if ctx.InstallInTestcases() {
- installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
- } else {
- installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
- }
-
- a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
- a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
- a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
-
- a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
- a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-
- dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
- if a.dexpreopter.uncompressedDex {
- dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
- a.uncompressDex(ctx, dexOutput, dexUncompressed.OutputPath)
- dexOutput = dexUncompressed
- }
-
- apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
-
- // TODO: Handle EXTERNAL
-
- // Sign or align the package if package has not been preprocessed
- if a.preprocessed {
- a.outputFile = srcApk
- a.certificate = PresignedCertificate
- } else if !Bool(a.properties.Presigned) {
- // If the certificate property is empty at this point, default_dev_cert must be set to true.
- // Which makes processMainCert's behavior for the empty cert string WAI.
- certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
- if len(certificates) != 1 {
- ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
- }
- a.certificate = certificates[0]
- signed := android.PathForModuleOut(ctx, "signed", apkFilename)
- var lineageFile android.Path
- if lineage := String(a.properties.Lineage); lineage != "" {
- lineageFile = android.PathForModuleSrc(ctx, lineage)
- }
- SignAppPackage(ctx, signed, dexOutput, certificates, nil, lineageFile)
- a.outputFile = signed
- } else {
- alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
- TransformZipAlign(ctx, alignedApk, dexOutput)
- a.outputFile = alignedApk
- a.certificate = PresignedCertificate
- }
-
- // TODO: Optionally compress the output apk.
-
- if apexInfo.IsForPlatform() {
- a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
- }
-
- // TODO: androidmk converter jni libs
-}
-
-func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
- return &a.prebuilt
-}
-
-func (a *AndroidAppImport) Name() string {
- return a.prebuilt.Name(a.ModuleBase.Name())
-}
-
-func (a *AndroidAppImport) OutputFile() android.Path {
- return a.outputFile
-}
-
-func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
- return nil
-}
-
-func (a *AndroidAppImport) Certificate() Certificate {
- return a.certificate
-}
-
-var dpiVariantGroupType reflect.Type
-var archVariantGroupType reflect.Type
-
-func initAndroidAppImportVariantGroupTypes() {
- dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
-
- archNames := make([]string, len(android.ArchTypeList()))
- for i, archType := range android.ArchTypeList() {
- archNames[i] = archType.Name
- }
- archVariantGroupType = createVariantGroupType(archNames, "Arch")
-}
-
-// Populates all variant struct properties at creation time.
-func (a *AndroidAppImport) populateAllVariantStructs() {
- a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
- a.AddProperties(a.dpiVariants)
-
- a.archVariants = reflect.New(archVariantGroupType).Interface()
- a.AddProperties(a.archVariants)
-}
-
-func (a *AndroidAppImport) Privileged() bool {
- return Bool(a.properties.Privileged)
-}
-
-func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
- // android_app_import might have extra dependencies via uses_libs property.
- // Don't track the dependency as we don't automatically add those libraries
- // to the classpath. It should be explicitly added to java_libs property of APEX
- return false
-}
-
-func (a *AndroidAppImport) sdkVersion() sdkSpec {
- return sdkSpecFrom("")
-}
-
-func (a *AndroidAppImport) minSdkVersion() sdkSpec {
- return sdkSpecFrom("")
-}
-
-func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
- sdkVersion android.ApiLevel) error {
- // Do not check for prebuilts against the min_sdk_version of enclosing APEX
- return nil
-}
-
-func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
- props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
-
- variantFields := make([]reflect.StructField, len(variants))
- for i, variant := range variants {
- variantFields[i] = reflect.StructField{
- Name: proptools.FieldNameForProperty(variant),
- Type: props,
- }
- }
-
- variantGroupStruct := reflect.StructOf(variantFields)
- return reflect.StructOf([]reflect.StructField{
- {
- Name: variantGroupName,
- Type: variantGroupStruct,
- },
- })
-}
-
-// android_app_import imports a prebuilt apk with additional processing specified in the module.
-// DPI-specific apk source files can be specified using dpi_variants. Example:
-//
-// android_app_import {
-// name: "example_import",
-// apk: "prebuilts/example.apk",
-// dpi_variants: {
-// mdpi: {
-// apk: "prebuilts/example_mdpi.apk",
-// },
-// xhdpi: {
-// apk: "prebuilts/example_xhdpi.apk",
-// },
-// },
-// certificate: "PRESIGNED",
-// }
-func AndroidAppImportFactory() android.Module {
- module := &AndroidAppImport{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.dexpreoptProperties)
- module.AddProperties(&module.usesLibrary.usesLibraryProperties)
- module.populateAllVariantStructs()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- module.processVariants(ctx)
- })
-
- android.InitApexModule(module)
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
-
- return module
-}
-
-type androidTestImportProperties struct {
- // Whether the prebuilt apk can be installed without additional processing. Default is false.
- Preprocessed *bool
-}
-
-type AndroidTestImport struct {
- AndroidAppImport
-
- testProperties testProperties
-
- testImportProperties androidTestImportProperties
-
- data android.Paths
-}
-
-func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- a.preprocessed = Bool(a.testImportProperties.Preprocessed)
-
- a.generateAndroidBuildActions(ctx)
-
- a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
-}
-
-func (a *AndroidTestImport) InstallInTestcases() bool {
- return true
-}
-
-// android_test_import imports a prebuilt test apk with additional processing specified in the
-// module. DPI or arch variant configurations can be made as with android_app_import.
-func AndroidTestImportFactory() android.Module {
- module := &AndroidTestImport{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.dexpreoptProperties)
- module.AddProperties(&module.testProperties)
- module.AddProperties(&module.testImportProperties)
- module.populateAllVariantStructs()
- android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- module.processVariants(ctx)
- })
-
- module.dexpreopter.isTest = true
-
- android.InitApexModule(module)
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
-
- return module
-}
-
-type RuntimeResourceOverlay struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.OverridableModuleBase
- aapt
-
- properties RuntimeResourceOverlayProperties
- overridableProperties OverridableRuntimeResourceOverlayProperties
-
- certificate Certificate
-
- outputFile android.Path
- installDir android.InstallPath
-}
-
-type RuntimeResourceOverlayProperties struct {
- // the name of a certificate in the default certificate directory or an android_app_certificate
- // module name in the form ":module".
- Certificate *string
-
- // Name of the signing certificate lineage file.
- Lineage *string
-
- // optional theme name. If specified, the overlay package will be applied
- // only when the ro.boot.vendor.overlay.theme system property is set to the same value.
- Theme *string
-
- // if not blank, set to the version of the sdk to compile against.
- // Defaults to compiling against the current platform.
- Sdk_version *string
-
- // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
- // Defaults to sdk_version if not set.
- Min_sdk_version *string
-
- // list of android_library modules whose resources are extracted and linked against statically
- Static_libs []string
-
- // list of android_app modules whose resources are extracted and linked against
- Resource_libs []string
-
- // Names of modules to be overridden. Listed modules can only be other overlays
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden overlays, but if both
- // overlays would be installed by default (in PRODUCT_PACKAGES) the other overlay will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-}
-
-// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
-// a RuntimeResourceOverlay module.
-type RuntimeResourceOverlayModule interface {
- android.Module
- OutputFile() android.Path
- Certificate() Certificate
- Theme() string
-}
-
-func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
- sdkDep := decodeSdkDep(ctx, sdkContext(r))
- if sdkDep.hasFrameworkLibs() {
- r.aapt.deps(ctx, sdkDep)
- }
-
- cert := android.SrcIsModule(String(r.properties.Certificate))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
- ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
-}
-
-func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // Compile and link resources
- r.aapt.hasNoCode = true
- // Do not remove resources without default values nor dedupe resource configurations with the same value
- aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
- // Allow the override of "package name" and "overlay target package name"
- manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
- if overridden || r.overridableProperties.Package_name != nil {
- // The product override variable has a priority over the package_name property.
- if !overridden {
- manifestPackageName = *r.overridableProperties.Package_name
- }
- aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, false)...)
- }
- if r.overridableProperties.Target_package_name != nil {
- aaptLinkFlags = append(aaptLinkFlags,
- "--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
- }
- r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
-
- // Sign the built package
- _, certificates := collectAppDeps(ctx, r, false, false)
- certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
- signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
- var lineageFile android.Path
- if lineage := String(r.properties.Lineage); lineage != "" {
- lineageFile = android.PathForModuleSrc(ctx, lineage)
- }
- SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile)
- r.certificate = certificates[0]
-
- r.outputFile = signed
- r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
- ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
-}
-
-func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
- return sdkSpecFrom(String(r.properties.Sdk_version))
-}
-
-func (r *RuntimeResourceOverlay) systemModules() string {
- return ""
-}
-
-func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
- if r.properties.Min_sdk_version != nil {
- return sdkSpecFrom(*r.properties.Min_sdk_version)
- }
- return r.sdkVersion()
-}
-
-func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
- return r.sdkVersion()
-}
-
-func (r *RuntimeResourceOverlay) Certificate() Certificate {
- return r.certificate
-}
-
-func (r *RuntimeResourceOverlay) OutputFile() android.Path {
- return r.outputFile
-}
-
-func (r *RuntimeResourceOverlay) Theme() string {
- return String(r.properties.Theme)
-}
-
-// runtime_resource_overlay generates a resource-only apk file that can overlay application and
-// system resources at run time.
-func RuntimeResourceOverlayFactory() android.Module {
- module := &RuntimeResourceOverlay{}
- module.AddProperties(
- &module.properties,
- &module.aaptProperties,
- &module.overridableProperties)
-
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitOverridableModule(module, &module.properties.Overrides)
- return module
-}
-
type UsesLibraryProperties struct {
// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
Uses_libs []string
@@ -1935,6 +1177,9 @@
// with knowledge of their shared libraries.
type usesLibrary struct {
usesLibraryProperties UsesLibraryProperties
+
+ // Whether to enforce verify_uses_library check.
+ enforce bool
}
func (u *usesLibrary) addLib(lib string, optional bool) {
@@ -1980,8 +1225,8 @@
ctx.VisitDirectDeps(func(m android.Module) {
if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
- if lib, ok := m.(Dependency); ok {
- clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
+ if lib, ok := m.(UsesLibraryDependency); ok {
+ clcMap.AddContext(ctx, tag.sdkVersion, dep,
lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep})
@@ -2001,7 +1246,7 @@
func (u *usesLibrary) enforceUsesLibraries() bool {
defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs) > 0 ||
len(u.usesLibraryProperties.Optional_uses_libs) > 0
- return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
+ return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, u.enforce || defaultEnforceUsesLibs)
}
// Freeze the value of `enforce_uses_libs` based on the current values of `uses_libs` and `optional_uses_libs`.
diff --git a/java/app_builder.go b/java/app_builder.go
index 69e462c..b53c15a 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -32,7 +32,7 @@
var (
Signapk, SignapkRE = remoteexec.StaticRules(pctx, "signapk",
blueprint.RuleParams{
- Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
+ Command: `rm -f $out && $reTemplate${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
`-jar ${config.SignapkCmd} $flags $certificates $in $out`,
CommandDeps: []string{"${config.SignapkCmd}", "${config.SignapkJniLibrary}"},
},
diff --git a/java/app_import.go b/java/app_import.go
new file mode 100644
index 0000000..df940f1
--- /dev/null
+++ b/java/app_import.go
@@ -0,0 +1,486 @@
+// 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 java
+
+// This file contains the module implementations for android_app_import and android_test_import.
+
+import (
+ "reflect"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ RegisterAppImportBuildComponents(android.InitRegistrationContext)
+
+ initAndroidAppImportVariantGroupTypes()
+}
+
+func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+ ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
+}
+
+type AndroidAppImport struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.ApexModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppImportProperties
+ dpiVariants interface{}
+ archVariants interface{}
+
+ outputFile android.Path
+ certificate Certificate
+
+ dexpreopter
+
+ usesLibrary usesLibrary
+
+ preprocessed bool
+
+ installPath android.InstallPath
+
+ hideApexVariantFromMake bool
+}
+
+type AndroidAppImportProperties struct {
+ // A prebuilt apk to import
+ Apk *string
+
+ // The name of a certificate in the default certificate directory or an android_app_certificate
+ // module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
+ Certificate *string
+
+ // Set this flag to true if the prebuilt apk is already signed. The certificate property must not
+ // be set for presigned modules.
+ Presigned *bool
+
+ // Name of the signing certificate lineage file.
+ Lineage *string
+
+ // Sign with the default system dev certificate. Must be used judiciously. Most imported apps
+ // need to either specify a specific certificate or be presigned.
+ Default_dev_cert *bool
+
+ // Specifies that this app should be installed to the priv-app directory,
+ // where the system will grant it additional privileges not available to
+ // normal apps.
+ Privileged *bool
+
+ // Names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // Optional name for the installed app. If unspecified, it is derived from the module name.
+ Filename *string
+}
+
+func (a *AndroidAppImport) IsInstallable() bool {
+ return true
+}
+
+// Updates properties with variant-specific values.
+func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
+ config := ctx.Config()
+
+ dpiProps := reflect.ValueOf(a.dpiVariants).Elem().FieldByName("Dpi_variants")
+ // Try DPI variant matches in the reverse-priority order so that the highest priority match
+ // overwrites everything else.
+ // TODO(jungjw): Can we optimize this by making it priority order?
+ for i := len(config.ProductAAPTPrebuiltDPI()) - 1; i >= 0; i-- {
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPrebuiltDPI()[i])
+ }
+ if config.ProductAAPTPreferredConfig() != "" {
+ MergePropertiesFromVariant(ctx, &a.properties, dpiProps, config.ProductAAPTPreferredConfig())
+ }
+
+ archProps := reflect.ValueOf(a.archVariants).Elem().FieldByName("Arch")
+ archType := ctx.Config().AndroidFirstDeviceTarget.Arch.ArchType
+ MergePropertiesFromVariant(ctx, &a.properties, archProps, archType.Name)
+
+ if String(a.properties.Apk) == "" {
+ // Disable this module since the apk property is still empty after processing all matching
+ // variants. This likely means there is no matching variant, and the default variant doesn't
+ // have an apk property value either.
+ a.Disable()
+ }
+}
+
+func MergePropertiesFromVariant(ctx android.EarlyModuleContext,
+ dst interface{}, variantGroup reflect.Value, variant string) {
+ src := variantGroup.FieldByName(proptools.FieldNameForProperty(variant))
+ if !src.IsValid() {
+ return
+ }
+
+ err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, proptools.OrderAppend)
+ if err != nil {
+ if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
+ ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
+ } else {
+ panic(err)
+ }
+ }
+}
+
+func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
+ cert := android.SrcIsModule(String(a.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+
+ a.usesLibrary.deps(ctx, true)
+}
+
+func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
+ ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+ // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
+ // with them may invalidate pre-existing signature data.
+ if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Output: outputPath,
+ Input: inputPath,
+ })
+ return
+ }
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", inputPath).
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-0 ", "'lib/**/*.so'").
+ Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
+ rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+}
+
+// Returns whether this module should have the dex file stored uncompressed in the APK.
+func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
+ if ctx.Config().UnbundledBuild() || a.preprocessed {
+ return false
+ }
+
+ // Uncompress dex in APKs of privileged apps
+ if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
+ return true
+ }
+
+ return shouldUncompressDex(ctx, &a.dexpreopter)
+}
+
+func (a *AndroidAppImport) uncompressDex(
+ ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
+ BuiltTool("zip2zip").
+ FlagWithInput("-i ", inputPath).
+ FlagWithOutput("-o ", outputPath).
+ FlagWithArg("-0 ", "'classes*.dex'").
+ Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
+ rule.Build("uncompress-dex", "Uncompress dex files")
+}
+
+func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidAppImport) InstallApkName() string {
+ return a.BaseModuleName()
+}
+
+func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ a.hideApexVariantFromMake = true
+ }
+
+ numCertPropsSet := 0
+ if String(a.properties.Certificate) != "" {
+ numCertPropsSet++
+ }
+ if Bool(a.properties.Presigned) {
+ numCertPropsSet++
+ }
+ if Bool(a.properties.Default_dev_cert) {
+ numCertPropsSet++
+ }
+ if numCertPropsSet != 1 {
+ ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
+ }
+
+ _, certificates := collectAppDeps(ctx, a, false, false)
+
+ // TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
+ // TODO: LOCAL_PACKAGE_SPLITS
+
+ srcApk := a.prebuilt.SingleSourcePath(ctx)
+
+ if a.usesLibrary.enforceUsesLibraries() {
+ srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+ }
+
+ // TODO: Install or embed JNI libraries
+
+ // Uncompress JNI libraries in the apk
+ jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
+ a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
+
+ var installDir android.InstallPath
+ if Bool(a.properties.Privileged) {
+ installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+ } else if ctx.InstallInTestcases() {
+ installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
+ } else {
+ installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+ }
+
+ a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
+ a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
+ a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
+
+ a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+ a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+
+ a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
+ if a.dexpreopter.uncompressedDex {
+ dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
+ a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+ jnisUncompressed = dexUncompressed
+ }
+
+ apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
+
+ // TODO: Handle EXTERNAL
+
+ // Sign or align the package if package has not been preprocessed
+ if a.preprocessed {
+ a.outputFile = srcApk
+ a.certificate = PresignedCertificate
+ } else if !Bool(a.properties.Presigned) {
+ // If the certificate property is empty at this point, default_dev_cert must be set to true.
+ // Which makes processMainCert's behavior for the empty cert string WAI.
+ certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+ if len(certificates) != 1 {
+ ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+ }
+ a.certificate = certificates[0]
+ signed := android.PathForModuleOut(ctx, "signed", apkFilename)
+ var lineageFile android.Path
+ if lineage := String(a.properties.Lineage); lineage != "" {
+ lineageFile = android.PathForModuleSrc(ctx, lineage)
+ }
+ SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
+ a.outputFile = signed
+ } else {
+ alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
+ TransformZipAlign(ctx, alignedApk, jnisUncompressed)
+ a.outputFile = alignedApk
+ a.certificate = PresignedCertificate
+ }
+
+ // TODO: Optionally compress the output apk.
+
+ if apexInfo.IsForPlatform() {
+ a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
+ }
+
+ // TODO: androidmk converter jni libs
+}
+
+func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
+ return &a.prebuilt
+}
+
+func (a *AndroidAppImport) Name() string {
+ return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+func (a *AndroidAppImport) OutputFile() android.Path {
+ return a.outputFile
+}
+
+func (a *AndroidAppImport) JacocoReportClassesFile() android.Path {
+ return nil
+}
+
+func (a *AndroidAppImport) Certificate() Certificate {
+ return a.certificate
+}
+
+var dpiVariantGroupType reflect.Type
+var archVariantGroupType reflect.Type
+var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
+
+func initAndroidAppImportVariantGroupTypes() {
+ dpiVariantGroupType = createVariantGroupType(supportedDpis, "Dpi_variants")
+
+ archNames := make([]string, len(android.ArchTypeList()))
+ for i, archType := range android.ArchTypeList() {
+ archNames[i] = archType.Name
+ }
+ archVariantGroupType = createVariantGroupType(archNames, "Arch")
+}
+
+// Populates all variant struct properties at creation time.
+func (a *AndroidAppImport) populateAllVariantStructs() {
+ a.dpiVariants = reflect.New(dpiVariantGroupType).Interface()
+ a.AddProperties(a.dpiVariants)
+
+ a.archVariants = reflect.New(archVariantGroupType).Interface()
+ a.AddProperties(a.archVariants)
+}
+
+func (a *AndroidAppImport) Privileged() bool {
+ return Bool(a.properties.Privileged)
+}
+
+func (a *AndroidAppImport) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
+ // android_app_import might have extra dependencies via uses_libs property.
+ // Don't track the dependency as we don't automatically add those libraries
+ // to the classpath. It should be explicitly added to java_libs property of APEX
+ return false
+}
+
+func (a *AndroidAppImport) sdkVersion() sdkSpec {
+ return sdkSpecFrom("")
+}
+
+func (a *AndroidAppImport) minSdkVersion() sdkSpec {
+ return sdkSpecFrom("")
+}
+
+var _ android.ApexModule = (*AndroidAppImport)(nil)
+
+// Implements android.ApexModule
+func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+ sdkVersion android.ApiLevel) error {
+ // Do not check for prebuilts against the min_sdk_version of enclosing APEX
+ return nil
+}
+
+func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
+ props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
+
+ variantFields := make([]reflect.StructField, len(variants))
+ for i, variant := range variants {
+ variantFields[i] = reflect.StructField{
+ Name: proptools.FieldNameForProperty(variant),
+ Type: props,
+ }
+ }
+
+ variantGroupStruct := reflect.StructOf(variantFields)
+ return reflect.StructOf([]reflect.StructField{
+ {
+ Name: variantGroupName,
+ Type: variantGroupStruct,
+ },
+ })
+}
+
+// android_app_import imports a prebuilt apk with additional processing specified in the module.
+// DPI-specific apk source files can be specified using dpi_variants. Example:
+//
+// android_app_import {
+// name: "example_import",
+// apk: "prebuilts/example.apk",
+// dpi_variants: {
+// mdpi: {
+// apk: "prebuilts/example_mdpi.apk",
+// },
+// xhdpi: {
+// apk: "prebuilts/example_xhdpi.apk",
+// },
+// },
+// certificate: "PRESIGNED",
+// }
+func AndroidAppImportFactory() android.Module {
+ module := &AndroidAppImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+ module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+ module.populateAllVariantStructs()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.processVariants(ctx)
+ })
+
+ android.InitApexModule(module)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+ module.usesLibrary.enforce = true
+
+ return module
+}
+
+type androidTestImportProperties struct {
+ // Whether the prebuilt apk can be installed without additional processing. Default is false.
+ Preprocessed *bool
+}
+
+type AndroidTestImport struct {
+ AndroidAppImport
+
+ testProperties testProperties
+
+ testImportProperties androidTestImportProperties
+
+ data android.Paths
+}
+
+func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.preprocessed = Bool(a.testImportProperties.Preprocessed)
+
+ a.generateAndroidBuildActions(ctx)
+
+ a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+}
+
+func (a *AndroidTestImport) InstallInTestcases() bool {
+ return true
+}
+
+// android_test_import imports a prebuilt test apk with additional processing specified in the
+// module. DPI or arch variant configurations can be made as with android_app_import.
+func AndroidTestImportFactory() android.Module {
+ module := &AndroidTestImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+ module.AddProperties(&module.testProperties)
+ module.AddProperties(&module.testImportProperties)
+ module.populateAllVariantStructs()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.processVariants(ctx)
+ })
+
+ module.dexpreopter.isTest = true
+
+ android.InitApexModule(module)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+ return module
+}
diff --git a/java/app_import_test.go b/java/app_import_test.go
new file mode 100644
index 0000000..344d23b
--- /dev/null
+++ b/java/app_import_test.go
@@ -0,0 +1,495 @@
+// 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 java
+
+import (
+ "reflect"
+ "regexp"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func TestAndroidAppImport(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+
+ // Check cert signing flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ dex_preopt: {
+ enabled: false,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs. They shouldn't exist.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
+ t.Errorf("dexpreopt shouldn't have run.")
+ }
+}
+
+func TestAndroidAppImport_Presigned(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+ // Make sure signing was skipped and aligning was done.
+ if variant.MaybeOutput("signed/foo.apk").Rule != nil {
+ t.Errorf("signing rule shouldn't be included.")
+ }
+ if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
+ t.Errorf("can't find aligning rule")
+ }
+}
+
+func TestAndroidAppImport_SigningLineage(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ lineage: "lineage.bin",
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check cert signing lineage flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["flags"]
+ expected := "--lineage lineage.bin"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ default_dev_cert: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+
+ // Check dexpreopt outputs.
+ if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
+ variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
+ t.Errorf("can't find dexpreopt outputs")
+ }
+
+ // Check cert signing flag.
+ signedApk := variant.Output("signed/foo.apk")
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+}
+
+func TestAndroidAppImport_DpiVariants(t *testing.T) {
+ bp := `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ dpi_variants: {
+ xhdpi: {
+ apk: "prebuilts/apk/app_xhdpi.apk",
+ },
+ xxhdpi: {
+ apk: "prebuilts/apk/app_xxhdpi.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `
+ testCases := []struct {
+ name string
+ aaptPreferredConfig *string
+ aaptPrebuiltDPI []string
+ expected string
+ }{
+ {
+ name: "no preferred",
+ aaptPreferredConfig: nil,
+ aaptPrebuiltDPI: []string{},
+ expected: "verify_uses_libraries/apk/app.apk",
+ },
+ {
+ name: "AAPTPreferredConfig matches",
+ aaptPreferredConfig: proptools.StringPtr("xhdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ },
+ {
+ name: "AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
+ expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
+ },
+ {
+ name: "non-first AAPTPrebuiltDPI matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
+ expected: "verify_uses_libraries/apk/app_xhdpi.apk",
+ },
+ {
+ name: "no matches",
+ aaptPreferredConfig: proptools.StringPtr("mdpi"),
+ aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
+ expected: "verify_uses_libraries/apk/app.apk",
+ },
+ }
+
+ jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+ for _, test := range testCases {
+ config := testAppConfig(nil, bp, nil)
+ config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
+ config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ ctx := testContext(config)
+
+ run(t, ctx, config)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+ if len(matches) != 2 {
+ t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+ }
+ if strings.HasSuffix(matches[1], test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ }
+ }
+}
+
+func TestAndroidAppImport_Filename(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ }
+
+ android_app_import {
+ name: "bar",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ filename: "bar_sample.apk"
+ }
+ `)
+
+ testCases := []struct {
+ name string
+ expected string
+ }{
+ {
+ name: "foo",
+ expected: "foo.apk",
+ },
+ {
+ name: "bar",
+ expected: "bar_sample.apk",
+ },
+ }
+
+ for _, test := range testCases {
+ variant := ctx.ModuleForTests(test.name, "android_common")
+ if variant.MaybeOutput(test.expected).Rule == nil {
+ t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
+ }
+
+ a := variant.Module().(*AndroidAppImport)
+ expectedValues := []string{test.expected}
+ actualValues := android.AndroidMkEntriesForTest(
+ t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
+ if !reflect.DeepEqual(actualValues, expectedValues) {
+ t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
+ actualValues, expectedValues)
+ }
+ }
+}
+
+func TestAndroidAppImport_ArchVariants(t *testing.T) {
+ // The test config's target arch is ARM64.
+ testCases := []struct {
+ name string
+ bp string
+ expected string
+ }{
+ {
+ name: "matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm64: {
+ apk: "prebuilts/apk/app_arm64.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "verify_uses_libraries/apk/app_arm64.apk",
+ },
+ {
+ name: "no matching arch",
+ bp: `
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ arch: {
+ arm: {
+ apk: "prebuilts/apk/app_arm.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "verify_uses_libraries/apk/app.apk",
+ },
+ {
+ name: "no matching arch without default",
+ bp: `
+ android_app_import {
+ name: "foo",
+ arch: {
+ arm: {
+ apk: "prebuilts/apk/app_arm.apk",
+ },
+ },
+ presigned: true,
+ dex_preopt: {
+ enabled: true,
+ },
+ }
+ `,
+ expected: "",
+ },
+ }
+
+ jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
+ for _, test := range testCases {
+ ctx, _ := testJava(t, test.bp)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ if test.expected == "" {
+ if variant.Module().Enabled() {
+ t.Error("module should have been disabled, but wasn't")
+ }
+ continue
+ }
+ jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
+ if len(matches) != 2 {
+ t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
+ }
+ if strings.HasSuffix(matches[1], test.expected) {
+ t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
+ }
+ }
+}
+
+func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_app {
+ name: "foo",
+ srcs: ["a.java"],
+ enabled: false,
+ }
+
+ android_app_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "platform",
+ prefer: true,
+ }
+ `)
+
+ variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
+ a := variant.Module().(*AndroidAppImport)
+ // The prebuilt module should still be enabled and active even if the source-based counterpart
+ // is disabled.
+ if !a.prebuilt.UsePrebuilt() {
+ t.Errorf("prebuilt foo module is not active")
+ }
+ if !a.Enabled() {
+ t.Errorf("prebuilt foo module is disabled")
+ }
+}
+
+func TestAndroidTestImport(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ data: [
+ "testdata/data",
+ ],
+ }
+ `)
+
+ test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+
+ // Check android mks.
+ entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
+ expected := []string{"tests"}
+ actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
+ }
+ expected = []string{"testdata/data:testdata/data"}
+ actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
+ }
+}
+
+func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "cert/new_cert",
+ data: [
+ "testdata/data",
+ ],
+ }
+
+ android_test_import {
+ name: "foo_presigned",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ data: [
+ "testdata/data",
+ ],
+ }
+ `)
+
+ variant := ctx.ModuleForTests("foo", "android_common")
+ jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
+ if !strings.HasPrefix(jniRule, "if (zipinfo") {
+ t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
+ }
+
+ variant = ctx.ModuleForTests("foo_presigned", "android_common")
+ jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
+ if jniRule != android.Cp.String() {
+ t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+ }
+ if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
+ t.Errorf("Presigned test apk should be aligned")
+ }
+}
+
+func TestAndroidTestImport_Preprocessed(t *testing.T) {
+ ctx, _ := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ preprocessed: true,
+ }
+
+ android_test_import {
+ name: "foo_cert",
+ apk: "prebuilts/apk/app.apk",
+ certificate: "cert/new_cert",
+ preprocessed: true,
+ }
+ `)
+
+ testModules := []string{"foo", "foo_cert"}
+ for _, m := range testModules {
+ apkName := m + ".apk"
+ variant := ctx.ModuleForTests(m, "android_common")
+ jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
+ if jniRule != android.Cp.String() {
+ t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+ }
+
+ // Make sure signing and aligning were skipped.
+ if variant.MaybeOutput("signed/"+apkName).Rule != nil {
+ t.Errorf("signing rule shouldn't be included for preprocessed.")
+ }
+ if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
+ t.Errorf("aligning rule shouldn't be for preprocessed")
+ }
+ }
+}
diff --git a/java/app_set.go b/java/app_set.go
new file mode 100644
index 0000000..6b25638
--- /dev/null
+++ b/java/app_set.go
@@ -0,0 +1,162 @@
+// 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 java
+
+// This file contains the module implementation for android_app_set.
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+)
+
+func init() {
+ RegisterAppSetBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterAppSetBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_app_set", AndroidAppSetFactory)
+}
+
+type AndroidAppSetProperties struct {
+ // APK Set path
+ Set *string
+
+ // Specifies that this app should be installed to the priv-app directory,
+ // where the system will grant it additional privileges not available to
+ // normal apps.
+ Privileged *bool
+
+ // APKs in this set use prerelease SDK version
+ Prerelease *bool
+
+ // Names of modules to be overridden. Listed modules can only be other apps
+ // (in Make or Soong).
+ Overrides []string
+}
+
+type AndroidAppSet struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ prebuilt android.Prebuilt
+
+ properties AndroidAppSetProperties
+ packedOutput android.WritablePath
+ installFile string
+ apkcertsFile android.ModuleOutPath
+}
+
+func (as *AndroidAppSet) Name() string {
+ return as.prebuilt.Name(as.ModuleBase.Name())
+}
+
+func (as *AndroidAppSet) IsInstallable() bool {
+ return true
+}
+
+func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
+ return &as.prebuilt
+}
+
+func (as *AndroidAppSet) Privileged() bool {
+ return Bool(as.properties.Privileged)
+}
+
+func (as *AndroidAppSet) OutputFile() android.Path {
+ return as.packedOutput
+}
+
+func (as *AndroidAppSet) InstallFile() string {
+ return as.installFile
+}
+
+func (as *AndroidAppSet) APKCertsFile() android.Path {
+ return as.apkcertsFile
+}
+
+var TargetCpuAbi = map[string]string{
+ "arm": "ARMEABI_V7A",
+ "arm64": "ARM64_V8A",
+ "x86": "X86",
+ "x86_64": "X86_64",
+}
+
+func SupportedAbis(ctx android.ModuleContext) []string {
+ abiName := func(targetIdx int, deviceArch string) string {
+ if abi, found := TargetCpuAbi[deviceArch]; found {
+ return abi
+ }
+ ctx.ModuleErrorf("Target %d has invalid Arch: %s", targetIdx, deviceArch)
+ return "BAD_ABI"
+ }
+
+ var result []string
+ for i, target := range ctx.Config().Targets[android.Android] {
+ result = append(result, abiName(i, target.Arch.ArchType.String()))
+ }
+ return result
+}
+
+func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+ as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
+ // We are assuming here that the install file in the APK
+ // set has `.apk` suffix. If it doesn't the build will fail.
+ // APK sets containing APEX files are handled elsewhere.
+ as.installFile = as.BaseModuleName() + ".apk"
+ screenDensities := "all"
+ if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
+ screenDensities = strings.ToUpper(strings.Join(dpis, ","))
+ }
+ // TODO(asmundak): handle locales.
+ // TODO(asmundak): do we support device features
+ ctx.Build(pctx,
+ android.BuildParams{
+ Rule: extractMatchingApks,
+ Description: "Extract APKs from APK set",
+ Output: as.packedOutput,
+ ImplicitOutput: as.apkcertsFile,
+ Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+ Args: map[string]string{
+ "abis": strings.Join(SupportedAbis(ctx), ","),
+ "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
+ "screen-densities": screenDensities,
+ "sdk-version": ctx.Config().PlatformSdkVersion().String(),
+ "stem": as.BaseModuleName(),
+ "apkcerts": as.apkcertsFile.String(),
+ "partition": as.PartitionTag(ctx.DeviceConfig()),
+ },
+ })
+}
+
+// android_app_set extracts a set of APKs based on the target device
+// configuration and installs this set as "split APKs".
+// The extracted set always contains an APK whose name is
+// _module_name_.apk and every split APK matching target device.
+// The extraction of the density-specific splits depends on
+// PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should
+// be a list density names: LDPI, MDPI, HDPI, etc.), only listed
+// splits will be extracted. Otherwise all density-specific splits
+// will be extracted.
+func AndroidAppSetFactory() android.Module {
+ module := &AndroidAppSet{}
+ module.AddProperties(&module.properties)
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+ return module
+}
diff --git a/java/app_set_test.go b/java/app_set_test.go
new file mode 100644
index 0000000..d31900d
--- /dev/null
+++ b/java/app_set_test.go
@@ -0,0 +1,115 @@
+// 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 java
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestAndroidAppSet(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ prerelease: true,
+ }`)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "foo.zip"
+ params := module.Output(packedSplitApks)
+ if params.Rule == nil {
+ t.Errorf("expected output %s is missing", packedSplitApks)
+ }
+ if s := params.Args["allow-prereleased"]; s != "true" {
+ t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
+ }
+ if s := params.Args["partition"]; s != "system" {
+ t.Errorf("wrong partition value: '%s', expected 'system'", s)
+ }
+ mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
+ expectedInstallFile := []string{"foo.apk"}
+ if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
+ t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
+ actualInstallFile, expectedInstallFile)
+ }
+}
+
+func TestAndroidAppSet_Variants(t *testing.T) {
+ bp := `
+ android_app_set {
+ name: "foo",
+ set: "prebuilts/apks/app.apks",
+ }`
+ testCases := []struct {
+ name string
+ targets []android.Target
+ aaptPrebuiltDPI []string
+ sdkVersion int
+ expected map[string]string
+ }{
+ {
+ name: "One",
+ targets: []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
+ },
+ aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
+ sdkVersion: 29,
+ expected: map[string]string{
+ "abis": "X86",
+ "allow-prereleased": "false",
+ "screen-densities": "LDPI,XXHDPI",
+ "sdk-version": "29",
+ "stem": "foo",
+ },
+ },
+ {
+ name: "Two",
+ targets: []android.Target{
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64}},
+ {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
+ },
+ aaptPrebuiltDPI: nil,
+ sdkVersion: 30,
+ expected: map[string]string{
+ "abis": "X86_64,X86",
+ "allow-prereleased": "false",
+ "screen-densities": "all",
+ "sdk-version": "30",
+ "stem": "foo",
+ },
+ },
+ }
+
+ for _, test := range testCases {
+ config := testAppConfig(nil, bp, nil)
+ config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+ config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
+ config.Targets[android.Android] = test.targets
+ ctx := testContext(config)
+ run(t, ctx, config)
+ module := ctx.ModuleForTests("foo", "android_common")
+ const packedSplitApks = "foo.zip"
+ params := module.Output(packedSplitApks)
+ for k, v := range test.expected {
+ if actual := params.Args[k]; actual != v {
+ t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
+ test.name, k, actual, v)
+ }
+ }
+ }
+}
diff --git a/java/app_test.go b/java/app_test.go
index 6efb0dc..b1abe3d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -18,7 +18,6 @@
"fmt"
"path/filepath"
"reflect"
- "regexp"
"sort"
"strings"
"testing"
@@ -141,99 +140,6 @@
}
}
-func TestAndroidAppSet(t *testing.T) {
- ctx, config := testJava(t, `
- android_app_set {
- name: "foo",
- set: "prebuilts/apks/app.apks",
- prerelease: true,
- }`)
- module := ctx.ModuleForTests("foo", "android_common")
- const packedSplitApks = "foo.zip"
- params := module.Output(packedSplitApks)
- if params.Rule == nil {
- t.Errorf("expected output %s is missing", packedSplitApks)
- }
- if s := params.Args["allow-prereleased"]; s != "true" {
- t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
- }
- if s := params.Args["partition"]; s != "system" {
- t.Errorf("wrong partition value: '%s', expected 'system'", s)
- }
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
- actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"]
- expectedInstallFile := []string{"foo.apk"}
- if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) {
- t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',",
- actualInstallFile, expectedInstallFile)
- }
-}
-
-func TestAndroidAppSet_Variants(t *testing.T) {
- bp := `
- android_app_set {
- name: "foo",
- set: "prebuilts/apks/app.apks",
- }`
- testCases := []struct {
- name string
- targets []android.Target
- aaptPrebuiltDPI []string
- sdkVersion int
- expected map[string]string
- }{
- {
- name: "One",
- targets: []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
- },
- aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
- sdkVersion: 29,
- expected: map[string]string{
- "abis": "X86",
- "allow-prereleased": "false",
- "screen-densities": "LDPI,XXHDPI",
- "sdk-version": "29",
- "stem": "foo",
- },
- },
- {
- name: "Two",
- targets: []android.Target{
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64}},
- {Os: android.Android, Arch: android.Arch{ArchType: android.X86}},
- },
- aaptPrebuiltDPI: nil,
- sdkVersion: 30,
- expected: map[string]string{
- "abis": "X86_64,X86",
- "allow-prereleased": "false",
- "screen-densities": "all",
- "sdk-version": "30",
- "stem": "foo",
- },
- },
- }
-
- for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
- config.Targets[android.Android] = test.targets
- ctx := testContext(config)
- run(t, ctx, config)
- module := ctx.ModuleForTests("foo", "android_common")
- const packedSplitApks = "foo.zip"
- params := module.Output(packedSplitApks)
- for k, v := range test.expected {
- if actual := params.Args[k]; actual != v {
- t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
- test.name, k, actual, v)
- }
- }
- }
-}
-
func TestPlatformAPIs(t *testing.T) {
testJava(t, `
android_app {
@@ -2226,423 +2132,6 @@
}
}
-func TestAndroidAppImport(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
-
- // Check cert signing flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- dex_preopt: {
- enabled: false,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs. They shouldn't exist.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
- t.Errorf("dexpreopt shouldn't have run.")
- }
-}
-
-func TestAndroidAppImport_Presigned(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
- // Make sure signing was skipped and aligning was done.
- if variant.MaybeOutput("signed/foo.apk").Rule != nil {
- t.Errorf("signing rule shouldn't be included.")
- }
- if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
- t.Errorf("can't find aligning rule")
- }
-}
-
-func TestAndroidAppImport_SigningLineage(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "platform",
- lineage: "lineage.bin",
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check cert signing lineage flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["flags"]
- expected := "--lineage lineage.bin"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
- ctx, _ := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- default_dev_cert: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
-
- // Check dexpreopt outputs.
- if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
- variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
- t.Errorf("can't find dexpreopt outputs")
- }
-
- // Check cert signing flag.
- signedApk := variant.Output("signed/foo.apk")
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
-}
-
-func TestAndroidAppImport_DpiVariants(t *testing.T) {
- bp := `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- dpi_variants: {
- xhdpi: {
- apk: "prebuilts/apk/app_xhdpi.apk",
- },
- xxhdpi: {
- apk: "prebuilts/apk/app_xxhdpi.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `
- testCases := []struct {
- name string
- aaptPreferredConfig *string
- aaptPrebuiltDPI []string
- expected string
- }{
- {
- name: "no preferred",
- aaptPreferredConfig: nil,
- aaptPrebuiltDPI: []string{},
- expected: "prebuilts/apk/app.apk",
- },
- {
- name: "AAPTPreferredConfig matches",
- aaptPreferredConfig: proptools.StringPtr("xhdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
- },
- {
- name: "AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
- expected: "prebuilts/apk/app_xxhdpi.apk",
- },
- {
- name: "non-first AAPTPrebuiltDPI matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
- expected: "prebuilts/apk/app_xhdpi.apk",
- },
- {
- name: "no matches",
- aaptPreferredConfig: proptools.StringPtr("mdpi"),
- aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
- expected: "prebuilts/apk/app.apk",
- },
- }
-
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
- for _, test := range testCases {
- config := testAppConfig(nil, bp, nil)
- config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
- config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testContext(config)
-
- run(t, ctx, config)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if test.expected != matches[1] {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
- }
- }
-}
-
-func TestAndroidAppImport_Filename(t *testing.T) {
- ctx, config := testJava(t, `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- }
-
- android_app_import {
- name: "bar",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- filename: "bar_sample.apk"
- }
- `)
-
- testCases := []struct {
- name string
- expected string
- }{
- {
- name: "foo",
- expected: "foo.apk",
- },
- {
- name: "bar",
- expected: "bar_sample.apk",
- },
- }
-
- for _, test := range testCases {
- variant := ctx.ModuleForTests(test.name, "android_common")
- if variant.MaybeOutput(test.expected).Rule == nil {
- t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
- }
-
- a := variant.Module().(*AndroidAppImport)
- expectedValues := []string{test.expected}
- actualValues := android.AndroidMkEntriesForTest(
- t, config, "", a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
- if !reflect.DeepEqual(actualValues, expectedValues) {
- t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
- actualValues, expectedValues)
- }
- }
-}
-
-func TestAndroidAppImport_ArchVariants(t *testing.T) {
- // The test config's target arch is ARM64.
- testCases := []struct {
- name string
- bp string
- expected string
- }{
- {
- name: "matching arch",
- bp: `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- arch: {
- arm64: {
- apk: "prebuilts/apk/app_arm64.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `,
- expected: "prebuilts/apk/app_arm64.apk",
- },
- {
- name: "no matching arch",
- bp: `
- android_app_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- arch: {
- arm: {
- apk: "prebuilts/apk/app_arm.apk",
- },
- },
- presigned: true,
- dex_preopt: {
- enabled: true,
- },
- }
- `,
- expected: "prebuilts/apk/app.apk",
- },
- }
-
- jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
- for _, test := range testCases {
- ctx, _ := testJava(t, test.bp)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
- if len(matches) != 2 {
- t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
- }
- if test.expected != matches[1] {
- t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
- }
- }
-}
-
-func TestAndroidTestImport(t *testing.T) {
- ctx, config := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- data: [
- "testdata/data",
- ],
- }
- `)
-
- test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
-
- // Check android mks.
- entries := android.AndroidMkEntriesForTest(t, config, "", test)[0]
- expected := []string{"tests"}
- actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
- }
- expected = []string{"testdata/data:testdata/data"}
- actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
- if !reflect.DeepEqual(expected, actual) {
- t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
- }
-}
-
-func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
- ctx, _ := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- certificate: "cert/new_cert",
- data: [
- "testdata/data",
- ],
- }
-
- android_test_import {
- name: "foo_presigned",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- data: [
- "testdata/data",
- ],
- }
- `)
-
- variant := ctx.ModuleForTests("foo", "android_common")
- jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
- if !strings.HasPrefix(jniRule, "if (zipinfo") {
- t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
- }
-
- variant = ctx.ModuleForTests("foo_presigned", "android_common")
- jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
- if jniRule != android.Cp.String() {
- t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
- }
- if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
- t.Errorf("Presigned test apk should be aligned")
- }
-}
-
-func TestAndroidTestImport_Preprocessed(t *testing.T) {
- ctx, _ := testJava(t, `
- android_test_import {
- name: "foo",
- apk: "prebuilts/apk/app.apk",
- presigned: true,
- preprocessed: true,
- }
-
- android_test_import {
- name: "foo_cert",
- apk: "prebuilts/apk/app.apk",
- certificate: "cert/new_cert",
- preprocessed: true,
- }
- `)
-
- testModules := []string{"foo", "foo_cert"}
- for _, m := range testModules {
- apkName := m + ".apk"
- variant := ctx.ModuleForTests(m, "android_common")
- jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
- if jniRule != android.Cp.String() {
- t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
- }
-
- // Make sure signing and aligning were skipped.
- if variant.MaybeOutput("signed/"+apkName).Rule != nil {
- t.Errorf("signing rule shouldn't be included for preprocessed.")
- }
- if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
- t.Errorf("aligning rule shouldn't be for preprocessed")
- }
- }
-}
-
func TestStl(t *testing.T) {
ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
@@ -2730,6 +2219,13 @@
}
java_sdk_library {
+ name: "fred",
+ srcs: ["a.java"],
+ api_packages: ["fred"],
+ sdk_version: "current",
+ }
+
+ java_sdk_library {
name: "bar",
srcs: ["a.java"],
api_packages: ["bar"],
@@ -2753,7 +2249,12 @@
name: "app",
srcs: ["a.java"],
libs: ["qux", "quuz.stubs"],
- static_libs: ["static-runtime-helper"],
+ static_libs: [
+ "static-runtime-helper",
+ // statically linked component libraries should not pull their SDK libraries,
+ // so "fred" should not be added to class loader context
+ "fred.stubs",
+ ],
uses_libs: ["foo"],
sdk_version: "current",
optional_uses_libs: [
@@ -2832,8 +2333,8 @@
// Test conditional context for target SDK version 29.
if w := `--target-context-for-sdk 29` +
- ` PCL[/system/framework/android.hidl.base-V1.0-java.jar]` +
- `#PCL[/system/framework/android.hidl.manager-V1.0-java.jar] `; !strings.Contains(cmd, w) {
+ ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]` +
+ `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `; !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
@@ -3173,356 +2674,6 @@
}
}
-func TestRuntimeResourceOverlay(t *testing.T) {
- fs := map[string][]byte{
- "baz/res/res/values/strings.xml": nil,
- "bar/res/res/values/strings.xml": nil,
- }
- bp := `
- runtime_resource_overlay {
- name: "foo",
- certificate: "platform",
- lineage: "lineage.bin",
- product_specific: true,
- static_libs: ["bar"],
- resource_libs: ["baz"],
- aaptflags: ["--keep-raw-values"],
- }
-
- runtime_resource_overlay {
- name: "foo_themed",
- certificate: "platform",
- product_specific: true,
- theme: "faza",
- overrides: ["foo"],
- }
-
- android_library {
- name: "bar",
- resource_dirs: ["bar/res"],
- }
-
- android_app {
- name: "baz",
- sdk_version: "current",
- resource_dirs: ["baz/res"],
- }
- `
- config := testAppConfig(nil, bp, fs)
- ctx := testContext(config)
- run(t, ctx, config)
-
- m := ctx.ModuleForTests("foo", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags := m.Output("package-res.apk").Args["flags"]
- expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
- absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
- if len(absentFlags) > 0 {
- t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
- }
-
- // Check overlay.list output for static_libs dependency.
- overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
- staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
- if !inList(staticLibPackage, overlayList) {
- t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
- }
-
- // Check AAPT2 link flags for resource_libs dependency.
- resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
- if !strings.Contains(aapt2Flags, resourceLibFlag) {
- t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
- }
-
- // Check cert signing flag.
- signedApk := m.Output("signed/foo.apk")
- lineageFlag := signedApk.Args["flags"]
- expectedLineageFlag := "--lineage lineage.bin"
- if expectedLineageFlag != lineageFlag {
- t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
- }
- signingFlag := signedApk.Args["certificates"]
- expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
- if expected != signingFlag {
- t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
- }
- androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
- path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
- expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
- }
-
- // Check device location.
- path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-
- // A themed module has a different device location
- m = ctx.ModuleForTests("foo_themed", "android_common")
- androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
- path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-
- overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
- expectedOverrides := []string{"foo"}
- if !reflect.DeepEqual(overrides, expectedOverrides) {
- t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
- }
-}
-
-func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
- ctx, config := testJava(t, `
- java_defaults {
- name: "rro_defaults",
- theme: "default_theme",
- product_specific: true,
- aaptflags: ["--keep-raw-values"],
- }
-
- runtime_resource_overlay {
- name: "foo_with_defaults",
- defaults: ["rro_defaults"],
- }
-
- runtime_resource_overlay {
- name: "foo_barebones",
- }
- `)
-
- //
- // RRO module with defaults
- //
- m := ctx.ModuleForTests("foo_with_defaults", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
- expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
- absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
- if len(absentFlags) > 0 {
- t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
- }
-
- // Check device location.
- path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
- }
-
- //
- // RRO module without defaults
- //
- m = ctx.ModuleForTests("foo_barebones", "android_common")
-
- // Check AAPT2 link flags.
- aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
- unexpectedFlags := "--keep-raw-values"
- if inList(unexpectedFlags, aapt2Flags) {
- t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
- }
-
- // Check device location.
- path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
- if !reflect.DeepEqual(path, expectedPath) {
- t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
- }
-}
-
-func TestOverrideRuntimeResourceOverlay(t *testing.T) {
- ctx, _ := testJava(t, `
- runtime_resource_overlay {
- name: "foo_overlay",
- certificate: "platform",
- product_specific: true,
- sdk_version: "current",
- }
-
- override_runtime_resource_overlay {
- name: "bar_overlay",
- base: "foo_overlay",
- package_name: "com.android.bar.overlay",
- target_package_name: "com.android.bar",
- }
- `)
-
- expectedVariants := []struct {
- moduleName string
- variantName string
- apkPath string
- overrides []string
- targetVariant string
- packageFlag string
- targetPackageFlag string
- }{
- {
- variantName: "android_common",
- apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
- overrides: nil,
- targetVariant: "android_common",
- packageFlag: "",
- targetPackageFlag: "",
- },
- {
- variantName: "android_common_bar_overlay",
- apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
- overrides: []string{"foo_overlay"},
- targetVariant: "android_common_bar",
- packageFlag: "com.android.bar.overlay",
- targetPackageFlag: "com.android.bar",
- },
- }
- for _, expected := range expectedVariants {
- variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
-
- // Check the final apk name
- outputs := variant.AllOutputs()
- expectedApkPath := buildDir + expected.apkPath
- found := false
- for _, o := range outputs {
- if o == expectedApkPath {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
- }
-
- // Check if the overrides field values are correctly aggregated.
- mod := variant.Module().(*RuntimeResourceOverlay)
- if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
- t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
- expected.overrides, mod.properties.Overrides)
- }
-
- // Check aapt2 flags.
- res := variant.Output("package-res.apk")
- aapt2Flags := res.Args["flags"]
- checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
- checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
- checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
- }
-}
-
-func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
- testCases := []struct {
- name string
- enforceRROTargets []string
- enforceRROExemptTargets []string
- rroDirs map[string][]string
- }{
- {
- name: "no RRO",
- enforceRROTargets: nil,
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": nil,
- "bar": nil,
- },
- },
- {
- name: "enforce RRO on all",
- enforceRROTargets: []string{"*"},
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": {"product/vendor/blah/overlay/lib2/res"},
- },
- },
- {
- name: "enforce RRO on foo",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: nil,
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": {"product/vendor/blah/overlay/lib2/res"},
- },
- },
- {
- name: "enforce RRO on foo, bar exempted",
- enforceRROTargets: []string{"foo"},
- enforceRROExemptTargets: []string{"bar"},
- rroDirs: map[string][]string{
- "foo": {"product/vendor/blah/overlay/lib2/res"},
- "bar": nil,
- },
- },
- }
-
- productResourceOverlays := []string{
- "product/vendor/blah/overlay",
- }
-
- fs := map[string][]byte{
- "lib2/res/values/strings.xml": nil,
- "product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
- }
-
- bp := `
- android_app {
- name: "foo",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib"],
- }
-
- android_app {
- name: "bar",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib"],
- }
-
- android_library {
- name: "lib",
- sdk_version: "current",
- resource_dirs: [],
- static_libs: ["lib2"],
- }
-
- android_library {
- name: "lib2",
- sdk_version: "current",
- resource_dirs: ["lib2/res"],
- }
- `
-
- for _, testCase := range testCases {
- t.Run(testCase.name, func(t *testing.T) {
- config := testAppConfig(nil, bp, fs)
- config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
- if testCase.enforceRROTargets != nil {
- config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
- }
- if testCase.enforceRROExemptTargets != nil {
- config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
- }
-
- ctx := testContext(config)
- run(t, ctx, config)
-
- modules := []string{"foo", "bar"}
- for _, moduleName := range modules {
- module := ctx.ModuleForTests(moduleName, "android_common")
- mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
- actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
- if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
- t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
- moduleName, testCase.rroDirs[moduleName], actualRRODirs)
- }
- }
- })
- }
-}
-
func TestExportedProguardFlagFiles(t *testing.T) {
ctx, _ := testJava(t, `
android_app {
diff --git a/java/builder.go b/java/builder.go
index cd35245..995160d 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -42,7 +42,7 @@
// TODO(b/143658984): goma can't handle the --system argument to javac.
javac, javacRE = remoteexec.MultiCommandStaticRules(pctx, "javac",
blueprint.RuleParams{
- Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
+ Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" "$out" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 67738d4..b5830c7 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -112,13 +112,13 @@
return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
}
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) {
// TODO(b/148690468): The check on d.installPath is to bail out in cases where
// the dexpreopter struct hasn't been fully initialized before we're called,
// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
// disabled, even if installable is true.
if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." {
- return dexJarFile
+ return
}
globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
@@ -213,12 +213,10 @@
dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
if err != nil {
ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
- return dexJarFile
+ return
}
dexpreoptRule.Build("dexpreopt", "dexpreopt")
d.builtInstalled = dexpreoptRule.Installs().String()
-
- return dexJarFile
}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f16ddf1..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 {
@@ -418,43 +441,56 @@
dumpOatRules(ctx, d.defaultBootImage)
}
-func isHostdex(module android.Module) bool {
- if lib, ok := module.(*Library); ok {
- return Bool(lib.deviceProperties.Hostdex)
- }
- return false
-}
-
// Inspect this module to see if it contains a bootclasspath dex jar.
// 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) {
- // All apex Java libraries have non-installable platform variants, skip them.
- if module.IsSkipInstall() {
- return -1, nil
- }
-
- jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
- if !hasJar {
- return -1, nil
- }
-
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
}
- // Check that this module satisfies constraints for a particular boot image.
- _, isApexModule := module.(android.ApexModule)
+ // It is an error if a module configured in the boot image does not support accessing the dex jar.
+ // This is safe because every module that has the same name has to have the same module type.
+ jar, hasJar := module.(interface{ DexJarBuildPath() android.Path })
+ if !hasJar {
+ ctx.Errorf("module %q configured in boot image %q does not support accessing dex jar", module, image.name)
+ return -1, nil
+ }
+
+ // It is also an error if the module is not an ApexModule.
+ if _, ok := module.(android.ApexModule); !ok {
+ ctx.Errorf("module %q configured in boot image %q does not support being added to an apex", module, image.name)
+ return -1, nil
+ }
+
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- fromUpdatableApex := isApexModule && apexInfo.Updatable
- if image.name == artBootImageName {
- if isApexModule && len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
- // ok: found the jar in the ART apex
- } else if isApexModule && apexInfo.IsForPlatform() && isHostdex(module) {
- // exception (skip and continue): special "hostdex" platform variant
+
+ // Now match the apex part of the boot image configuration.
+ requiredApex := image.modules.Apex(index)
+ if requiredApex == "platform" {
+ if len(apexInfo.InApexes) != 0 {
+ // A platform variant is required but this is for an apex so ignore it.
return -1, nil
+ }
+ } else if !android.InList(requiredApex, apexInfo.InApexes) {
+ // An apex variant for a specific apex is required but this is the wrong apex.
+ return -1, nil
+ }
+
+ // Check that this module satisfies any boot image specific constraints.
+ fromUpdatableApex := apexInfo.Updatable
+
+ switch image.name {
+ case artBootImageName:
+ if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
+ // ok: found the jar in the ART apex
} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
// exception (skip and continue): Jacoco platform variant for a coverage build
return -1, nil
@@ -465,14 +501,15 @@
// error: this jar is part of the platform or a non-updatable apex
ctx.Errorf("module %q is not allowed in the ART boot image", name)
}
- } else if image.name == frameworkBootImageName {
+
+ case frameworkBootImageName:
if !fromUpdatableApex {
// ok: this jar is part of the platform or a non-updatable apex
} else {
// error: this jar is part of an updatable apex
ctx.Errorf("module %q from updatable apexes %q is not allowed in the framework boot image", name, apexInfo.InApexes)
}
- } else {
+ default:
panic("unknown boot image: " + image.name)
}
@@ -495,6 +532,12 @@
bootDexJars := make(android.Paths, image.modules.Len())
ctx.VisitAllModules(func(module android.Module) {
if i, j := getBootImageJar(ctx, image, module); i != -1 {
+ if existing := bootDexJars[i]; existing != nil {
+ ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
+ image.modules.Apex(i), image.modules.Jar(i), existing, j)
+ return
+ }
+
bootDexJars[i] = j
}
})
@@ -506,7 +549,7 @@
m := image.modules.Jar(i)
if ctx.Config().AllowMissingDependencies() {
missingDeps = append(missingDeps, m)
- bootDexJars[i] = android.PathForOutput(ctx, "missing")
+ bootDexJars[i] = android.PathForOutput(ctx, "missing/module", m, "from/apex", image.modules.Apex(i))
} else {
ctx.Errorf("failed to find a dex jar path for module '%s'"+
", note that some jars may be filtered out by module constraints", m)
@@ -605,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 {
@@ -707,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{} {
@@ -779,7 +824,7 @@
bootFrameworkProfile = path.Path()
} else {
missingDeps = append(missingDeps, defaultProfile)
- bootFrameworkProfile = android.PathForOutput(ctx, "missing")
+ bootFrameworkProfile = android.PathForOutput(ctx, "missing", defaultProfile)
}
profile := image.dir.Join(ctx, "boot.bprof")
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/droiddoc.go b/java/droiddoc.go
index 9c88a3c..c74009e 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -192,24 +192,6 @@
Current ApiToCheck
- // The java_sdk_library module generates references to modules (i.e. filegroups)
- // from which information about the latest API version can be obtained. As those
- // modules may not exist (e.g. because a previous version has not been released) it
- // sets ignore_missing_latest_api=true on the droidstubs modules it creates so
- // that droidstubs can ignore those references if the modules do not yet exist.
- //
- // If true then this will ignore module references for modules that do not exist
- // in properties that supply the previous version of the API.
- //
- // There are two sets of those:
- // * Api_file, Removed_api_file in check_api.last_released
- // * New_since in check_api.api_lint.new_since
- //
- // The first two must be set as a pair, so either they should both exist or neither
- // should exist - in which case when this property is true they are ignored. If one
- // exists and the other does not then it is an error.
- Ignore_missing_latest_api *bool `blueprint:"mutated"`
-
Api_lint struct {
Enabled *bool
@@ -304,25 +286,6 @@
return false
}
-func ignoreMissingModules(ctx android.BottomUpMutatorContext, apiToCheck *ApiToCheck) {
- api_file := String(apiToCheck.Api_file)
- removed_api_file := String(apiToCheck.Removed_api_file)
-
- api_module := android.SrcIsModule(api_file)
- removed_api_module := android.SrcIsModule(removed_api_file)
-
- if api_module == "" || removed_api_module == "" {
- return
- }
-
- if ctx.OtherModuleExists(api_module) || ctx.OtherModuleExists(removed_api_module) {
- return
- }
-
- apiToCheck.Api_file = nil
- apiToCheck.Removed_api_file = nil
-}
-
// Used by xsd_config
type ApiFilePath interface {
ApiFilePath() android.Path
@@ -1079,20 +1042,6 @@
func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
d.Javadoc.addDeps(ctx)
- // If requested clear any properties that provide information about the latest version
- // of an API and which reference non-existent modules.
- if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
- ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
-
- // If the new_since references a module, e.g. :module-latest-api and the module
- // does not exist then clear it.
- newSinceSrc := d.properties.Check_api.Api_lint.New_since
- newSinceSrcModule := android.SrcIsModule(proptools.String(newSinceSrc))
- if newSinceSrcModule != "" && !ctx.OtherModuleExists(newSinceSrcModule) {
- d.properties.Check_api.Api_lint.New_since = nil
- }
- }
-
if len(d.properties.Merge_annotations_dirs) != 0 {
for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
diff --git a/java/gen.go b/java/gen.go
index 5766a94..4f928d5 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -78,10 +78,7 @@
rule.Command().
Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
- // TODO(b/124333557): this can't use -srcjar for now, aidl on parcelables generates java files
- // without a package statement, which causes -srcjar to put them in the top level of the zip file.
- // Once aidl skips parcelables we can use -srcjar.
- //Flag("-srcjar").
+ Flag("-srcjar").
Flag("-write_if_changed").
FlagWithOutput("-o ", srcJarFile).
FlagWithArg("-C ", outDir.String()).
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index ce8410e..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)
@@ -177,12 +190,13 @@
for moduleList, pathList := range moduleListToPathList {
for i := range pathList {
if pathList[i] == nil {
- pathList[i] = android.PathForOutput(ctx, "missing")
+ moduleName := (*moduleList)[i]
+ pathList[i] = android.PathForOutput(ctx, "missing/module", moduleName)
if ctx.Config().AllowMissingDependencies() {
- missingDeps = append(missingDeps, (*moduleList)[i])
+ missingDeps = append(missingDeps, moduleName)
} else {
ctx.Errorf("failed to find dex jar path for module %q",
- (*moduleList)[i])
+ moduleName)
}
}
}
@@ -211,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 {
@@ -250,6 +277,8 @@
FlagWithInput("--unsupported ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-unsupported.txt")).
FlagWithInput("--unsupported ", combinedRemovedApis).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed").
+ FlagWithInput("--max-target-r ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-r-loprio.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--max-target-q ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-q.txt")).
FlagWithInput("--max-target-p ",
@@ -258,8 +287,6 @@
ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--blocked ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
- FlagWithInput("--blocked ",
- android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--unsupported ", android.PathForSource(
ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
FlagWithOutput("--output ", tempPath)
@@ -390,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 34a4856..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,11 +82,11 @@
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"})
+ `, []string{":foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
- want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+ want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
}
@@ -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 95a4e88..59ec94d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -40,20 +40,55 @@
// Register sdk member types.
android.RegisterSdkMemberType(javaHeaderLibsSdkMemberType)
+ // Register java implementation libraries for use only in module_exports (not sdk).
android.RegisterSdkMemberType(&librarySdkMemberType{
android.SdkMemberTypeBase{
PropertyName: "java_libs",
},
- func(j *Library) android.Path {
+ func(_ android.SdkMemberContext, j *Library) android.Path {
implementationJars := j.ImplementationAndResourcesJars()
if len(implementationJars) != 1 {
panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
}
-
return implementationJars[0]
},
+ sdkSnapshotFilePathForJar,
+ copyEverythingToSnapshot,
})
+ // Register java boot libraries for use in sdk.
+ //
+ // The build has some implicit dependencies (via the boot jars configuration) on a number of
+ // modules, e.g. core-oj, apache-xml, that are part of the java boot class path and which are
+ // provided by mainline modules (e.g. art, conscrypt, runtime-i18n) but which are not otherwise
+ // used outside those mainline modules.
+ //
+ // As they are not needed outside the mainline modules adding them to the sdk/module-exports as
+ // either java_libs, or java_header_libs would end up exporting more information than was strictly
+ // necessary. The java_boot_libs property to allow those modules to be exported as part of the
+ // sdk/module_exports without exposing any unnecessary information.
+ android.RegisterSdkMemberType(&librarySdkMemberType{
+ android.SdkMemberTypeBase{
+ PropertyName: "java_boot_libs",
+ SupportsSdk: true,
+ },
+ func(ctx android.SdkMemberContext, j *Library) android.Path {
+ // Java boot libs are only provided in the SDK to provide access to their dex implementation
+ // jar for use by dexpreopting and boot jars package check. They do not need to provide an
+ // actual implementation jar but the java_import will need a file that exists so just copy an
+ // empty file. Any attempt to use that file as a jar will cause a build error.
+ return ctx.SnapshotBuilder().EmptyFile()
+ },
+ func(osPrefix, name string) string {
+ // Create a special name for the implementation jar to try and provide some useful information
+ // to a developer that attempts to compile against this.
+ // TODO(b/175714559): Provide a proper error message in Soong not ninja.
+ return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
+ },
+ onlyCopyJarToSnapshot,
+ })
+
+ // Register java test libraries for use only in module_exports (not sdk).
android.RegisterSdkMemberType(&testSdkMemberType{
SdkMemberTypeBase: android.SdkMemberTypeBase{
PropertyName: "java_tests",
@@ -304,6 +339,9 @@
// whether to generate Binder#GetTransaction name method.
Generate_get_transaction_name *bool
+
+ // list of flags that will be passed to the AIDL compiler
+ Flags []string
}
// If true, export a copy of the module as a -hostdex module for host testing.
@@ -510,6 +548,7 @@
type UsesLibraryDependency interface {
DexJarBuildPath() android.Path
DexJarInstallPath() android.Path
+ ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
}
type Dependency interface {
@@ -518,7 +557,6 @@
ImplementationJars() android.Paths
ResourceJars() android.Paths
AidlIncludeDirs() android.Paths
- ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
ExportedPlugins() (android.Paths, []string, bool)
SrcJarArgs() ([]string, android.Paths)
BaseModuleName() string
@@ -872,6 +910,8 @@
var flags []string
var deps android.Paths
+ flags = append(flags, j.deviceProperties.Aidl.Flags...)
+
if aidlPreprocess.Valid() {
flags = append(flags, "-p"+aidlPreprocess.String())
deps = append(deps, aidlPreprocess.Path())
@@ -1081,9 +1121,6 @@
switch tag {
case libTag:
deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
- // names of sdk libs that are directly depended are exported
- j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(),
- dep.DexJarBuildPath(), dep.DexJarInstallPath())
case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
}
@@ -1093,8 +1130,6 @@
deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
case libTag, instrumentationForTag:
deps.classpath = append(deps.classpath, dep.HeaderJars()...)
- // sdk lib names from dependencies are re-exported
- j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1106,8 +1141,6 @@
deps.staticJars = append(deps.staticJars, dep.ImplementationJars()...)
deps.staticHeaderJars = append(deps.staticHeaderJars, dep.HeaderJars()...)
deps.staticResourceJars = append(deps.staticResourceJars, dep.ResourceJars()...)
- // sdk lib names from dependencies are re-exported
- j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1182,6 +1215,8 @@
deps.systemModules = &systemModules{outputDir, outputDeps}
}
}
+
+ addCLCFromDep(ctx, module, j.classLoaderContexts)
})
return deps
@@ -1446,6 +1481,9 @@
kotlincFlags := j.properties.Kotlincflags
CheckKotlincFlags(ctx, kotlincFlags)
+ // Dogfood the JVM_IR backend.
+ kotlincFlags = append(kotlincFlags, "-Xuse-ir")
+
// If there are kotlin files, compile them first but pass all the kotlin and java files
// kotlinc will use the java files to resolve types referenced by the kotlin files, but
// won't emit any classes for them.
@@ -1507,11 +1545,11 @@
j.compiledJavaSrcs = uniqueSrcFiles
j.compiledSrcJars = srcJars
- enable_sharding := false
+ enableSharding := false
var headerJarFileWithoutJarjar android.Path
if ctx.Device() && !ctx.Config().IsEnvFalse("TURBINE_ENABLED") && !deps.disableTurbine {
if j.properties.Javac_shard_size != nil && *(j.properties.Javac_shard_size) > 0 {
- enable_sharding = true
+ enableSharding = true
// Formerly, there was a check here that prevented annotation processors
// from being used when sharding was enabled, as some annotation processors
// do not function correctly in sharded environments. It was removed to
@@ -1537,7 +1575,7 @@
extraJarDeps = append(extraJarDeps, errorprone)
}
- if enable_sharding {
+ if enableSharding {
flags.classpath = append(flags.classpath, headerJarFileWithoutJarjar)
shardSize := int(*(j.properties.Javac_shard_size))
var shardSrcs []android.Paths
@@ -1784,7 +1822,7 @@
j.dexJarFile = dexOutputFile
// Dexpreopting
- dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+ j.dexpreopt(ctx, dexOutputFile)
j.maybeStrippedDexJarFile = dexOutputFile
@@ -2019,10 +2057,12 @@
return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
}
+// Implements android.ApexModule
func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return j.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
sdkSpec := j.minSdkVersion()
@@ -2068,6 +2108,8 @@
InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
}
+var _ android.ApexModule = (*Library)(nil)
+
// Provides access to the list of permitted packages from updatable boot jars.
type PermittedPackagesForUpdatableBootJars interface {
PermittedPackagesForUpdatableBootJars() []string
@@ -2131,18 +2173,6 @@
j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
j.Stem()+".jar", j.outputFile, extraInstallDeps...)
}
-
- // If this is a component library (stubs, etc.) for a java_sdk_library then
- // add the name of that java_sdk_library to the exported sdk libs to make sure
- // that, if necessary, a <uses-library> element for that java_sdk_library is
- // added to the Android manifest.
- j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
- j.DexJarBuildPath(), j.DexJarInstallPath())
-
- // A non-SDK library may provide a <uses-library> (the name may be different from the module name).
- if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
- j.classLoaderContexts.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
- }
}
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2170,9 +2200,22 @@
// Function to retrieve the appropriate output jar (implementation or header) from
// the library.
- jarToExportGetter func(j *Library) android.Path
+ jarToExportGetter func(ctx android.SdkMemberContext, j *Library) android.Path
+
+ // Function to compute the snapshot relative path to which the named library's
+ // jar should be copied.
+ snapshotPathGetter func(osPrefix, name string) string
+
+ // True if only the jar should be copied to the snapshot, false if the jar plus any additional
+ // files like aidl files should also be copied.
+ onlyCopyJarToSnapshot bool
}
+const (
+ onlyCopyJarToSnapshot = true
+ copyEverythingToSnapshot = false
+)
+
func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
mctx.AddVariationDependencies(nil, dependencyTag, names...)
}
@@ -2200,21 +2243,32 @@
func (p *librarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
j := variant.(*Library)
- p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(j)
+ p.JarToExport = ctx.MemberType().(*librarySdkMemberType).jarToExportGetter(ctx, j)
+
p.AidlIncludeDirs = j.AidlIncludeDirs()
}
func (p *librarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
builder := ctx.SnapshotBuilder()
+ memberType := ctx.MemberType().(*librarySdkMemberType)
+
exportedJar := p.JarToExport
if exportedJar != nil {
- snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(p.OsPrefix(), ctx.Name())
+ // Delegate the creation of the snapshot relative path to the member type.
+ snapshotRelativeJavaLibPath := memberType.snapshotPathGetter(p.OsPrefix(), ctx.Name())
+
+ // Copy the exported jar to the snapshot.
builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
propertySet.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
}
+ // Do not copy anything else to the snapshot.
+ if memberType.onlyCopyJarToSnapshot {
+ return
+ }
+
aidlIncludeDirs := p.AidlIncludeDirs
if len(aidlIncludeDirs) != 0 {
sdkModuleContext := ctx.SdkModuleContext()
@@ -2235,7 +2289,7 @@
PropertyName: "java_header_libs",
SupportsSdk: true,
},
- func(j *Library) android.Path {
+ func(_ android.SdkMemberContext, j *Library) android.Path {
headerJars := j.HeaderJars()
if len(headerJars) != 1 {
panic(fmt.Errorf("there must be only one header jar from %q", j.Name()))
@@ -2243,6 +2297,8 @@
return headerJars[0]
},
+ sdkSnapshotFilePathForJar,
+ copyEverythingToSnapshot,
}
// java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well.
@@ -2717,6 +2773,7 @@
hiddenAPI
dexer
+ dexpreopter
properties ImportProperties
@@ -2774,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...)
@@ -2802,9 +2863,9 @@
j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags
+ var deapexerModule android.Module
ctx.VisitDirectDeps(func(module android.Module) {
- otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
switch dep := module.(type) {
@@ -2812,8 +2873,6 @@
switch tag {
case libTag, staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars()...)
- // sdk lib names from dependencies are re-exported
- j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
}
@@ -2821,52 +2880,78 @@
switch tag {
case libTag:
flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
- // names of sdk libs that are directly depended are exported
- j.classLoaderContexts.AddContext(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
}
}
+
+ addCLCFromDep(ctx, module, j.classLoaderContexts)
+
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = module
+ }
})
- var installFile android.Path
if Bool(j.properties.Installable) {
- installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+ ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
jarName, outputFile)
}
- // If this is a component library (impl, stubs, etc.) for a java_sdk_library then
- // add the name of that java_sdk_library to the exported sdk libs to make sure
- // that, if necessary, a <uses-library> element for that java_sdk_library is
- // added to the Android manifest.
- j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
- outputFile, installFile)
-
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
- 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
}
}
@@ -2932,10 +3017,14 @@
return nil, nil
}
+var _ android.ApexModule = (*Import)(nil)
+
+// Implements android.ApexModule
func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return j.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// Do not check for prebuilts against the min_sdk_version of enclosing APEX
@@ -2968,6 +3057,12 @@
var _ android.PrebuiltInterface = (*Import)(nil)
+func (j *Import) IsInstallable() bool {
+ return Bool(j.properties.Installable)
+}
+
+var _ dexpreopterInterface = (*Import)(nil)
+
// java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
//
// By default, a java_import has a single variant that expects a `.jar` file containing `.class` files that were
@@ -3113,7 +3208,7 @@
j.dexJarFile = dexOutputFile
- dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+ j.dexpreopt(ctx, dexOutputFile)
j.maybeStrippedDexJarFile = dexOutputFile
@@ -3127,6 +3222,9 @@
return j.dexJarFile
}
+var _ android.ApexModule = (*DexImport)(nil)
+
+// Implements android.ApexModule
func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// we don't check prebuilt modules for sdk_version
@@ -3245,3 +3343,64 @@
var BoolDefault = proptools.BoolDefault
var String = proptools.String
var inList = android.InList
+
+// TODO(b/132357300) Generalize SdkLibrarComponentDependency to non-SDK libraries and merge with
+// this interface.
+type ProvidesUsesLib interface {
+ ProvidesUsesLib() *string
+}
+
+func (j *Module) ProvidesUsesLib() *string {
+ return j.usesLibraryProperties.Provides_uses_lib
+}
+
+// Add class loader context (CLC) of a given dependency to the current CLC.
+func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
+ clcMap dexpreopt.ClassLoaderContextMap) {
+
+ dep, ok := depModule.(UsesLibraryDependency)
+ if !ok {
+ return
+ }
+
+ // Find out if the dependency is either an SDK library or an ordinary library that is disguised
+ // as an SDK library by the means of `provides_uses_lib` property. If yes, the library is itself
+ // a <uses-library> and should be added as a node in the CLC tree, and its CLC should be added
+ // as subtree of that node. Otherwise the library is not a <uses_library> and should not be
+ // added to CLC, but the transitive <uses-library> dependencies from its CLC should be added to
+ // the current CLC.
+ var implicitSdkLib *string
+ comp, isComp := depModule.(SdkLibraryComponentDependency)
+ if isComp {
+ implicitSdkLib = comp.OptionalImplicitSdkLibrary()
+ // OptionalImplicitSdkLibrary() may be nil so need to fall through to ProvidesUsesLib().
+ }
+ if implicitSdkLib == nil {
+ if ulib, ok := depModule.(ProvidesUsesLib); ok {
+ implicitSdkLib = ulib.ProvidesUsesLib()
+ }
+ }
+
+ depTag := ctx.OtherModuleDependencyTag(depModule)
+ if depTag == libTag || depTag == usesLibTag {
+ // Ok, propagate <uses-library> through non-static library dependencies.
+ } else if depTag == staticLibTag {
+ // Propagate <uses-library> through static library dependencies, unless it is a component
+ // library (such as stubs). Component libraries have a dependency on their SDK library,
+ // which should not be pulled just because of a static component library.
+ if implicitSdkLib != nil {
+ return
+ }
+ } else {
+ // Don't propagate <uses-library> for other dependency tags.
+ return
+ }
+
+ if implicitSdkLib != nil {
+ clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+ dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+ } else {
+ depName := ctx.OtherModuleName(depModule)
+ clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+ }
+}
diff --git a/java/java_test.go b/java/java_test.go
index f7cf03f..7b89848 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -61,34 +61,19 @@
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)
- RegisterAARBuildComponents(ctx)
- RegisterGenRuleBuildComponents(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)
- RegisterSdkLibraryBuildComponents(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(android.RegisterComponentsMutator)
- RegisterPrebuiltApisBuildComponents(ctx)
-
ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory))
@@ -865,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",
@@ -956,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,
}))
}
@@ -1813,7 +1784,7 @@
func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) {
ctx, _ := testJava(t, `
java_sdk_library {
- name: "sdk_lib",
+ name: "sdklib",
srcs: ["a.java"],
impl_only_libs: ["foo"],
stub_only_libs: ["bar"],
@@ -1830,13 +1801,13 @@
}
`)
- for _, implName := range []string{"sdk_lib", "sdk_lib.impl"} {
+ for _, implName := range []string{"sdklib", "sdklib.impl"} {
implJavacCp := ctx.ModuleForTests(implName, "android_common").Rule("javac").Args["classpath"]
if !strings.Contains(implJavacCp, "/foo.jar") || strings.Contains(implJavacCp, "/bar.jar") {
t.Errorf("%v javac classpath %v does not contain foo and not bar", implName, implJavacCp)
}
}
- stubName := apiScopePublic.stubsLibraryModuleName("sdk_lib")
+ stubName := apiScopePublic.stubsLibraryModuleName("sdklib")
stubsJavacCp := ctx.ModuleForTests(stubName, "android_common").Rule("javac").Args["classpath"]
if strings.Contains(stubsJavacCp, "/foo.jar") || !strings.Contains(stubsJavacCp, "/bar.jar") {
t.Errorf("stubs javac classpath %v does not contain bar and not foo", stubsJavacCp)
@@ -2422,6 +2393,22 @@
}
}
+func TestAidlFlagsArePassedToTheAidlCompiler(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["aidl/foo/IFoo.aidl"],
+ aidl: { flags: ["-Werror"], },
+ }
+ `)
+
+ aidlCommand := ctx.ModuleForTests("foo", "android_common").Rule("aidl").RuleParams.Command
+ expectedAidlFlag := "-Werror"
+ if !strings.Contains(aidlCommand, expectedAidlFlag) {
+ t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlag)
+ }
+}
+
func TestDataNativeBinaries(t *testing.T) {
ctx, config := testJava(t, `
java_test_host {
diff --git a/java/proto.go b/java/proto.go
index cc9abbe..dc5519f 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -49,7 +49,7 @@
// into a srcjar.
rule.Command().
BuiltTool("soong_zip").
- Flag("-jar").
+ Flag("-srcjar").
Flag("-write_if_changed").
FlagWithOutput("-o ", srcJarFile).
FlagWithArg("-C ", outDir.String()).
diff --git a/java/robolectric.go b/java/robolectric.go
index 419efda..c821e5b 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -260,7 +260,7 @@
entries := &entriesList[0]
entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
if s := r.robolectricProperties.Test_options.Shards; s != nil && *s > 1 {
numShards := int(*s)
shardSize := (len(r.tests) + numShards - 1) / numShards
diff --git a/java/rro.go b/java/rro.go
new file mode 100644
index 0000000..98cd379
--- /dev/null
+++ b/java/rro.go
@@ -0,0 +1,214 @@
+// 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 java
+
+// This file contains the module implementations for runtime_resource_overlay and
+// override_runtime_resource_overlay.
+
+import "android/soong/android"
+
+func init() {
+ RegisterRuntimeResourceOverlayBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterRuntimeResourceOverlayBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
+ ctx.RegisterModuleType("override_runtime_resource_overlay", OverrideRuntimeResourceOverlayModuleFactory)
+}
+
+type RuntimeResourceOverlay struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.OverridableModuleBase
+ aapt
+
+ properties RuntimeResourceOverlayProperties
+ overridableProperties OverridableRuntimeResourceOverlayProperties
+
+ certificate Certificate
+
+ outputFile android.Path
+ installDir android.InstallPath
+}
+
+type RuntimeResourceOverlayProperties struct {
+ // the name of a certificate in the default certificate directory or an android_app_certificate
+ // module name in the form ":module".
+ Certificate *string
+
+ // Name of the signing certificate lineage file.
+ Lineage *string
+
+ // optional theme name. If specified, the overlay package will be applied
+ // only when the ro.boot.vendor.overlay.theme system property is set to the same value.
+ Theme *string
+
+ // if not blank, set to the version of the sdk to compile against.
+ // Defaults to compiling against the current platform.
+ Sdk_version *string
+
+ // if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
+ // Defaults to sdk_version if not set.
+ Min_sdk_version *string
+
+ // list of android_library modules whose resources are extracted and linked against statically
+ Static_libs []string
+
+ // list of android_app modules whose resources are extracted and linked against
+ Resource_libs []string
+
+ // Names of modules to be overridden. Listed modules can only be other overlays
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden overlays, but if both
+ // overlays would be installed by default (in PRODUCT_PACKAGES) the other overlay will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+}
+
+// RuntimeResourceOverlayModule interface is used by the apex package to gather information from
+// a RuntimeResourceOverlay module.
+type RuntimeResourceOverlayModule interface {
+ android.Module
+ OutputFile() android.Path
+ Certificate() Certificate
+ Theme() string
+}
+
+func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
+ sdkDep := decodeSdkDep(ctx, sdkContext(r))
+ if sdkDep.hasFrameworkLibs() {
+ r.aapt.deps(ctx, sdkDep)
+ }
+
+ cert := android.SrcIsModule(String(r.properties.Certificate))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ }
+
+ ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
+ ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
+}
+
+func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Compile and link resources
+ r.aapt.hasNoCode = true
+ // Do not remove resources without default values nor dedupe resource configurations with the same value
+ aaptLinkFlags := []string{"--no-resource-deduping", "--no-resource-removal"}
+ // Allow the override of "package name" and "overlay target package name"
+ manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
+ if overridden || r.overridableProperties.Package_name != nil {
+ // The product override variable has a priority over the package_name property.
+ if !overridden {
+ manifestPackageName = *r.overridableProperties.Package_name
+ }
+ aaptLinkFlags = append(aaptLinkFlags, generateAaptRenamePackageFlags(manifestPackageName, false)...)
+ }
+ if r.overridableProperties.Target_package_name != nil {
+ aaptLinkFlags = append(aaptLinkFlags,
+ "--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
+ }
+ r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
+
+ // Sign the built package
+ _, certificates := collectAppDeps(ctx, r, false, false)
+ certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+ signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
+ var lineageFile android.Path
+ if lineage := String(r.properties.Lineage); lineage != "" {
+ lineageFile = android.PathForModuleSrc(ctx, lineage)
+ }
+ SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil, lineageFile)
+ r.certificate = certificates[0]
+
+ r.outputFile = signed
+ r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+ ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
+}
+
+func (r *RuntimeResourceOverlay) sdkVersion() sdkSpec {
+ return sdkSpecFrom(String(r.properties.Sdk_version))
+}
+
+func (r *RuntimeResourceOverlay) systemModules() string {
+ return ""
+}
+
+func (r *RuntimeResourceOverlay) minSdkVersion() sdkSpec {
+ if r.properties.Min_sdk_version != nil {
+ return sdkSpecFrom(*r.properties.Min_sdk_version)
+ }
+ return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) targetSdkVersion() sdkSpec {
+ return r.sdkVersion()
+}
+
+func (r *RuntimeResourceOverlay) Certificate() Certificate {
+ return r.certificate
+}
+
+func (r *RuntimeResourceOverlay) OutputFile() android.Path {
+ return r.outputFile
+}
+
+func (r *RuntimeResourceOverlay) Theme() string {
+ return String(r.properties.Theme)
+}
+
+// runtime_resource_overlay generates a resource-only apk file that can overlay application and
+// system resources at run time.
+func RuntimeResourceOverlayFactory() android.Module {
+ module := &RuntimeResourceOverlay{}
+ module.AddProperties(
+ &module.properties,
+ &module.aaptProperties,
+ &module.overridableProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitOverridableModule(module, &module.properties.Overrides)
+ return module
+}
+
+// runtime_resource_overlay properties that can be overridden by override_runtime_resource_overlay
+type OverridableRuntimeResourceOverlayProperties struct {
+ // the package name of this app. The package name in the manifest file is used if one was not given.
+ Package_name *string
+
+ // the target package name of this overlay app. The target package name in the manifest file is used if one was not given.
+ Target_package_name *string
+}
+
+type OverrideRuntimeResourceOverlay struct {
+ android.ModuleBase
+ android.OverrideModuleBase
+}
+
+func (i *OverrideRuntimeResourceOverlay) GenerateAndroidBuildActions(_ android.ModuleContext) {
+ // All the overrides happen in the base module.
+ // TODO(jungjw): Check the base module type.
+}
+
+// override_runtime_resource_overlay is used to create a module based on another
+// runtime_resource_overlay module by overriding some of its properties.
+func OverrideRuntimeResourceOverlayModuleFactory() android.Module {
+ m := &OverrideRuntimeResourceOverlay{}
+ m.AddProperties(&OverridableRuntimeResourceOverlayProperties{})
+
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ android.InitOverrideModule(m)
+ return m
+}
diff --git a/java/rro_test.go b/java/rro_test.go
new file mode 100644
index 0000000..345f2ee
--- /dev/null
+++ b/java/rro_test.go
@@ -0,0 +1,373 @@
+// 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 java
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestRuntimeResourceOverlay(t *testing.T) {
+ fs := map[string][]byte{
+ "baz/res/res/values/strings.xml": nil,
+ "bar/res/res/values/strings.xml": nil,
+ }
+ bp := `
+ runtime_resource_overlay {
+ name: "foo",
+ certificate: "platform",
+ lineage: "lineage.bin",
+ product_specific: true,
+ static_libs: ["bar"],
+ resource_libs: ["baz"],
+ aaptflags: ["--keep-raw-values"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_themed",
+ certificate: "platform",
+ product_specific: true,
+ theme: "faza",
+ overrides: ["foo"],
+ }
+
+ android_library {
+ name: "bar",
+ resource_dirs: ["bar/res"],
+ }
+
+ android_app {
+ name: "baz",
+ sdk_version: "current",
+ resource_dirs: ["baz/res"],
+ }
+ `
+ config := testAppConfig(nil, bp, fs)
+ ctx := testContext(config)
+ run(t, ctx, config)
+
+ m := ctx.ModuleForTests("foo", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags := m.Output("package-res.apk").Args["flags"]
+ expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+ absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
+ if len(absentFlags) > 0 {
+ t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+ }
+
+ // Check overlay.list output for static_libs dependency.
+ overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
+ staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+ if !inList(staticLibPackage, overlayList) {
+ t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
+ }
+
+ // Check AAPT2 link flags for resource_libs dependency.
+ resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+ if !strings.Contains(aapt2Flags, resourceLibFlag) {
+ t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
+ }
+
+ // Check cert signing flag.
+ signedApk := m.Output("signed/foo.apk")
+ lineageFlag := signedApk.Args["flags"]
+ expectedLineageFlag := "--lineage lineage.bin"
+ if expectedLineageFlag != lineageFlag {
+ t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
+ }
+ signingFlag := signedApk.Args["certificates"]
+ expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
+ if expected != signingFlag {
+ t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+ }
+ androidMkEntries := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
+ expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
+ }
+
+ // Check device location.
+ path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/product/overlay"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+
+ // A themed module has a different device location
+ m = ctx.ModuleForTests("foo_themed", "android_common")
+ androidMkEntries = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0]
+ path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+
+ overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
+ expectedOverrides := []string{"foo"}
+ if !reflect.DeepEqual(overrides, expectedOverrides) {
+ t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
+ }
+}
+
+func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
+ ctx, config := testJava(t, `
+ java_defaults {
+ name: "rro_defaults",
+ theme: "default_theme",
+ product_specific: true,
+ aaptflags: ["--keep-raw-values"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_with_defaults",
+ defaults: ["rro_defaults"],
+ }
+
+ runtime_resource_overlay {
+ name: "foo_barebones",
+ }
+ `)
+
+ //
+ // RRO module with defaults
+ //
+ m := ctx.ModuleForTests("foo_with_defaults", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
+ expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
+ absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
+ if len(absentFlags) > 0 {
+ t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
+ }
+
+ // Check device location.
+ path := android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath)
+ }
+
+ //
+ // RRO module without defaults
+ //
+ m = ctx.ModuleForTests("foo_barebones", "android_common")
+
+ // Check AAPT2 link flags.
+ aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
+ unexpectedFlags := "--keep-raw-values"
+ if inList(unexpectedFlags, aapt2Flags) {
+ t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
+ }
+
+ // Check device location.
+ path = android.AndroidMkEntriesForTest(t, config, "", m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+ expectedPath = []string{"/tmp/target/product/test_device/system/overlay"}
+ if !reflect.DeepEqual(path, expectedPath) {
+ t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath)
+ }
+}
+
+func TestOverrideRuntimeResourceOverlay(t *testing.T) {
+ ctx, _ := testJava(t, `
+ runtime_resource_overlay {
+ name: "foo_overlay",
+ certificate: "platform",
+ product_specific: true,
+ sdk_version: "current",
+ }
+
+ override_runtime_resource_overlay {
+ name: "bar_overlay",
+ base: "foo_overlay",
+ package_name: "com.android.bar.overlay",
+ target_package_name: "com.android.bar",
+ }
+ `)
+
+ expectedVariants := []struct {
+ moduleName string
+ variantName string
+ apkPath string
+ overrides []string
+ targetVariant string
+ packageFlag string
+ targetPackageFlag string
+ }{
+ {
+ variantName: "android_common",
+ apkPath: "/target/product/test_device/product/overlay/foo_overlay.apk",
+ overrides: nil,
+ targetVariant: "android_common",
+ packageFlag: "",
+ targetPackageFlag: "",
+ },
+ {
+ variantName: "android_common_bar_overlay",
+ apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk",
+ overrides: []string{"foo_overlay"},
+ targetVariant: "android_common_bar",
+ packageFlag: "com.android.bar.overlay",
+ targetPackageFlag: "com.android.bar",
+ },
+ }
+ for _, expected := range expectedVariants {
+ variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
+
+ // Check the final apk name
+ outputs := variant.AllOutputs()
+ expectedApkPath := buildDir + expected.apkPath
+ found := false
+ for _, o := range outputs {
+ if o == expectedApkPath {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
+ }
+
+ // Check if the overrides field values are correctly aggregated.
+ mod := variant.Module().(*RuntimeResourceOverlay)
+ if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
+ t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
+ expected.overrides, mod.properties.Overrides)
+ }
+
+ // Check aapt2 flags.
+ res := variant.Output("package-res.apk")
+ aapt2Flags := res.Args["flags"]
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
+ checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
+ }
+}
+
+func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
+ testCases := []struct {
+ name string
+ enforceRROTargets []string
+ enforceRROExemptTargets []string
+ rroDirs map[string][]string
+ }{
+ {
+ name: "no RRO",
+ enforceRROTargets: nil,
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": nil,
+ "bar": nil,
+ },
+ },
+ {
+ name: "enforce RRO on all",
+ enforceRROTargets: []string{"*"},
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": {"product/vendor/blah/overlay/lib2/res"},
+ },
+ },
+ {
+ name: "enforce RRO on foo",
+ enforceRROTargets: []string{"foo"},
+ enforceRROExemptTargets: nil,
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": {"product/vendor/blah/overlay/lib2/res"},
+ },
+ },
+ {
+ name: "enforce RRO on foo, bar exempted",
+ enforceRROTargets: []string{"foo"},
+ enforceRROExemptTargets: []string{"bar"},
+ rroDirs: map[string][]string{
+ "foo": {"product/vendor/blah/overlay/lib2/res"},
+ "bar": nil,
+ },
+ },
+ }
+
+ productResourceOverlays := []string{
+ "product/vendor/blah/overlay",
+ }
+
+ fs := map[string][]byte{
+ "lib2/res/values/strings.xml": nil,
+ "product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
+ }
+
+ bp := `
+ android_app {
+ name: "foo",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib"],
+ }
+
+ android_app {
+ name: "bar",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib"],
+ }
+
+ android_library {
+ name: "lib",
+ sdk_version: "current",
+ resource_dirs: [],
+ static_libs: ["lib2"],
+ }
+
+ android_library {
+ name: "lib2",
+ sdk_version: "current",
+ resource_dirs: ["lib2/res"],
+ }
+ `
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ config := testAppConfig(nil, bp, fs)
+ config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
+ if testCase.enforceRROTargets != nil {
+ config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
+ }
+ if testCase.enforceRROExemptTargets != nil {
+ config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
+ }
+
+ ctx := testContext(config)
+ run(t, ctx, config)
+
+ modules := []string{"foo", "bar"}
+ for _, moduleName := range modules {
+ module := ctx.ModuleForTests(moduleName, "android_common")
+ mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+ actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
+ if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
+ t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
+ moduleName, testCase.rroDirs[moduleName], actualRRODirs)
+ }
+ }
+ })
+ }
+}
diff --git a/java/sdk.go b/java/sdk.go
index 32a4b5a..a68abfb 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -371,8 +371,8 @@
jar := filepath.Join(dir, "android.jar")
// There's no aidl for other SDKs yet.
// TODO(77525052): Add aidl files for other SDKs too.
- public_dir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
- aidl := filepath.Join(public_dir, "framework.aidl")
+ publicDir := filepath.Join("prebuilts", "sdk", sdkVersion.version.String(), "public")
+ aidl := filepath.Join(publicDir, "framework.aidl")
jarPath := android.ExistentPathForSource(ctx, jar)
aidlPath := android.ExistentPathForSource(ctx, aidl)
lambdaStubsPath := android.PathForSource(ctx, config.SdkLambdaStubsPath)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 4e33d74..638740f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -28,6 +28,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/dexpreopt"
)
const (
@@ -453,6 +454,10 @@
// * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest
Dist_stem *string
+ // A compatibility mode that allows historical API-tracking files to not exist.
+ // Do not use.
+ Unsafe_ignore_missing_latest_api bool
+
// indicates whether system and test apis should be generated.
Generate_system_and_test_apis bool `blueprint:"mutated"`
@@ -1043,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)
@@ -1266,10 +1295,10 @@
Merge_annotations_dirs []string
Merge_inclusion_annotations_dirs []string
Generate_stubs *bool
+ Previous_api *string
Check_api struct {
- Current ApiToCheck
- Last_released ApiToCheck
- Ignore_missing_latest_api *bool
+ Current ApiToCheck
+ Last_released ApiToCheck
Api_lint struct {
Enabled *bool
@@ -1353,13 +1382,13 @@
props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
- if !apiScope.unstable {
+ if !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api) {
// check against the latest released API
latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+ props.Previous_api = latestApiFilegroupName
props.Check_api.Last_released.Api_file = latestApiFilegroupName
props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
module.latestRemovedApiFilegroupName(apiScope))
- props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
// Enable api lint.
@@ -1533,7 +1562,7 @@
hasSystemAndTestApis := sdkDep.hasStandardLibs()
module.sdkLibraryProperties.Generate_system_and_test_apis = hasSystemAndTestApis
- missing_current_api := false
+ missingCurrentApi := false
generatedScopes := module.getGeneratedApiScopes(mctx)
@@ -1544,12 +1573,12 @@
p := android.ExistentPathForSource(mctx, path)
if !p.Valid() {
mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
- missing_current_api = true
+ missingCurrentApi = true
}
}
}
- if missing_current_api {
+ if missingCurrentApi {
script := "build/soong/scripts/gen-java-current-api-files.sh"
p := android.ExistentPathForSource(mctx, script)
@@ -1925,6 +1954,9 @@
}
}
+var _ android.ApexModule = (*SdkLibraryImport)(nil)
+
+// Implements android.ApexModule
func (module *SdkLibraryImport) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
depTag := mctx.OtherModuleDependencyTag(dep)
if depTag == xmlPermissionsFileTag {
@@ -1936,6 +1968,7 @@
return false
}
+// Implements android.ApexModule
func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// we don't check prebuilt modules for sdk_version
@@ -2016,7 +2049,7 @@
return module.sdkJars(ctx, sdkVersion, false)
}
-// to satisfy SdkLibraryDependency interface
+// to satisfy UsesLibraryDependency interface
func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
if module.implLibraryModule == nil {
return nil
@@ -2025,7 +2058,7 @@
}
}
-// to satisfy SdkLibraryDependency interface
+// to satisfy UsesLibraryDependency interface
func (module *SdkLibraryImport) DexJarInstallPath() android.Path {
if module.implLibraryModule == nil {
return nil
@@ -2034,6 +2067,11 @@
}
}
+// to satisfy UsesLibraryDependency interface
+func (module *SdkLibraryImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return nil
+}
+
// to satisfy apex.javaDependency interface
func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path {
if module.implLibraryModule == nil {
@@ -2141,6 +2179,9 @@
// do nothing
}
+var _ android.ApexModule = (*sdkLibraryXml)(nil)
+
+// Implements android.ApexModule
func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
diff --git a/java/system_modules.go b/java/system_modules.go
index 7394fd5..5cc546d 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -192,6 +192,7 @@
fmt.Fprintln(w, name+":", "$("+makevar+")")
fmt.Fprintln(w, ".PHONY:", name)
+ // TODO(b/151177513): Licenses: Doesn't go through base_rules. May have to generate meta_lic and meta_module here.
},
}
}
diff --git a/java/testing.go b/java/testing.go
index ab13121..0b1e2eb 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -39,59 +39,8 @@
"api/test-current.txt": nil,
"api/test-removed.txt": nil,
- "prebuilts/sdk/14/public/android.jar": nil,
- "prebuilts/sdk/14/public/framework.aidl": nil,
- "prebuilts/sdk/14/system/android.jar": nil,
- "prebuilts/sdk/17/public/android.jar": nil,
- "prebuilts/sdk/17/public/framework.aidl": nil,
- "prebuilts/sdk/17/system/android.jar": nil,
- "prebuilts/sdk/28/public/android.jar": nil,
- "prebuilts/sdk/28/public/framework.aidl": nil,
- "prebuilts/sdk/28/system/android.jar": nil,
- "prebuilts/sdk/29/public/android.jar": nil,
- "prebuilts/sdk/29/public/framework.aidl": nil,
- "prebuilts/sdk/29/system/android.jar": nil,
- "prebuilts/sdk/29/system/foo.jar": nil,
- "prebuilts/sdk/30/public/android.jar": nil,
- "prebuilts/sdk/30/public/framework.aidl": nil,
- "prebuilts/sdk/30/system/android.jar": nil,
- "prebuilts/sdk/30/system/foo.jar": nil,
- "prebuilts/sdk/30/module-lib/android.jar": nil,
- "prebuilts/sdk/30/module-lib/foo.jar": nil,
- "prebuilts/sdk/30/public/core-for-system-modules.jar": nil,
- "prebuilts/sdk/current/core/android.jar": nil,
- "prebuilts/sdk/current/public/android.jar": nil,
- "prebuilts/sdk/current/public/framework.aidl": nil,
- "prebuilts/sdk/current/public/core.jar": nil,
- "prebuilts/sdk/current/public/core-for-system-modules.jar": nil,
- "prebuilts/sdk/current/system/android.jar": nil,
- "prebuilts/sdk/current/test/android.jar": nil,
- "prebuilts/sdk/28/public/api/foo.txt": nil,
- "prebuilts/sdk/28/system/api/foo.txt": nil,
- "prebuilts/sdk/28/test/api/foo.txt": nil,
- "prebuilts/sdk/28/public/api/foo-removed.txt": nil,
- "prebuilts/sdk/28/system/api/foo-removed.txt": nil,
- "prebuilts/sdk/28/test/api/foo-removed.txt": nil,
- "prebuilts/sdk/28/public/api/bar.txt": nil,
- "prebuilts/sdk/28/system/api/bar.txt": nil,
- "prebuilts/sdk/28/test/api/bar.txt": nil,
- "prebuilts/sdk/28/public/api/bar-removed.txt": nil,
- "prebuilts/sdk/28/system/api/bar-removed.txt": nil,
- "prebuilts/sdk/28/test/api/bar-removed.txt": nil,
- "prebuilts/sdk/30/public/api/foo.txt": nil,
- "prebuilts/sdk/30/system/api/foo.txt": nil,
- "prebuilts/sdk/30/test/api/foo.txt": nil,
- "prebuilts/sdk/30/public/api/foo-removed.txt": nil,
- "prebuilts/sdk/30/system/api/foo-removed.txt": nil,
- "prebuilts/sdk/30/test/api/foo-removed.txt": nil,
- "prebuilts/sdk/30/public/api/bar.txt": nil,
- "prebuilts/sdk/30/system/api/bar.txt": nil,
- "prebuilts/sdk/30/test/api/bar.txt": nil,
- "prebuilts/sdk/30/public/api/bar-removed.txt": nil,
- "prebuilts/sdk/30/system/api/bar-removed.txt": nil,
- "prebuilts/sdk/30/test/api/bar-removed.txt": nil,
- "prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
- "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"], imports_sdk_version: "none", imports_compile_dex:true,}`),
+ "prebuilts/sdk/tools/core-lambda-stubs.jar": nil,
+ "prebuilts/sdk/Android.bp": []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"], imports_sdk_version: "none", imports_compile_dex:true,}`),
"bin.py": nil,
python.StubTemplateHost: []byte(`PYTHON_BINARY = '%interpreter%'
@@ -104,6 +53,16 @@
"api/system-server-removed.txt": nil,
}
+ levels := []string{"14", "28", "29", "30", "current"}
+ libs := []string{
+ "android", "foo", "bar", "sdklib", "barney", "betty", "foo-shared_library",
+ "foo-no_shared_library", "core-for-system-modules", "quuz", "qux", "fred",
+ "runtime-library",
+ }
+ for k, v := range prebuiltApisFilesForLibs(levels, libs) {
+ mockFS[k] = v
+ }
+
cc.GatherRequiredFilesForTest(mockFS)
for k, v := range fs {
@@ -121,6 +80,44 @@
return config
}
+func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
+ fs := make(map[string][]byte)
+ for _, level := range apiLevels {
+ for _, lib := range sdkLibs {
+ for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} {
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
+ fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+ }
+ }
+ fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil
+ }
+ 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
@@ -207,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
new file mode 100644
index 0000000..8032dc5
--- /dev/null
+++ b/licenses/Android.bp
@@ -0,0 +1,1087 @@
+//
+// Copyright (C) 2020 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 {
+ default_visibility: ["//visibility:public"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+license {
+ name: "Android-Apache-2.0",
+ license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+ copyright_notice: "Copyright (C) The Android Open Source Project",
+ license_text: ["LICENSE"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-1.2",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/AFL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AFL-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/AFL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-only",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-1.0-or-later",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-only",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-AGPL-3.0-or-later",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/AGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Apache-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Apache-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-Perl",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-Perl.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-1.0-cl8",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-1.0-cl8.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Artistic-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Artistic-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-1-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-1-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-FreeBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-FreeBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-NetBSD",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-NetBSD.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-2-Clause-Patent",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-2-Clause-Patent.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Attribution",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Attribution.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Clear",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Clear.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-LBNL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-LBNL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-License-2014",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-License-2014.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-No-Nuclear-Warranty",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-No-Nuclear-Warranty.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-3-Clause-Open-MPI",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-3-Clause-Open-MPI.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-4-Clause-UC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-4-Clause-UC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Protection",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Protection.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSD-Source-Code",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSD-Source-Code.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-BSL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/BSL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Beerware",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/Beerware.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-2.5",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-3.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-4.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/CC-BY-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-ND-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-2.5",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-3.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-NC-SA-4.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CC-BY-NC-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-ND-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-ND-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-2.5",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-2.5.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-4.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/CC-BY-SA-4.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC-BY-SA-ND",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CC0-1.0",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/CC0-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPAL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/CPAL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-CPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/CPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/EPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.1",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-EUPL-1.2",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/EUPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-FTL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/FTL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-1.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-1.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-bison-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-bison-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-classpath-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-2.0-with-font-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-2.0-with-font-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-GCC-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-3.0-with-autoconf-exception",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/GPL-3.0-with-autoconf-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-GPL-with-classpath-exception",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-HPND",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/HPND.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ICU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ICU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ISC",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ISC.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-JSON",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/JSON.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-2.1-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-2.1-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0+",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0+.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-only",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-only.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPL-3.0-or-later",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPL-3.0-or-later.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LGPLLR",
+ conditions: ["restricted"],
+ url: "https://spdx.org/licenses/LGPLLR.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-LPL-1.02",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/LPL-1.02.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-CMU",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-CMU.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-advertising",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-advertising.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-enna",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-enna.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MIT-feh",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MIT-feh.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MITNFA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MITNFA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-1.1",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MPL-2.0-no-copyleft-exception",
+ conditions: ["reciprocal"],
+ url: "https://spdx.org/licenses/MPL-2.0-no-copyleft-exception.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-MS-PL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/MS-PL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-NCSA",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/NCSA.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.0-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OFL-1.1-no-RFN",
+ conditions: ["by_exception_only"],
+ url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-OpenSSL",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/OpenSSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-PSF-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/PSF-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SISSL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SISSL-1.2",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SISSL-1.2.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SPL-1.0",
+ conditions: ["by_exception_only", "reciprocal"],
+ url: "https://spdx.org/licenses/SPL-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-SSPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/SSPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-UPL-1.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/UPL-1.-.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2015",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2015.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unicode-DFS-2016",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Unicode-DFS-2016.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Unlicense",
+ conditions: ["unencumbered"],
+ url: "https://spdx.org/licenses/Unlicense.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-19980720",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-19980720.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-W3C-20150513",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/W3C-20150513.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-WTFPL",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/WTFPL.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Watcom-1.0",
+ conditions: ["by_exception_only", "not_allowed"],
+ url: "https://spdx.org/licenses/Watcom-1.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Xnet",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Xnet.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-1.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-1.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-ZPL-2.1",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/ZPL-2.1.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zend-2.0",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zend-2.0.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-Zlib",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/Zlib.html",
+}
+
+license_kind {
+ name: "SPDX-license-identifier-libtiff",
+ conditions: ["notice"],
+ url: "https://spdx.org/licenses/libtiff.html",
+}
+
+
+// Legacy license kinds -- do not add new references -- use an spdx kind instead.
+license_kind {
+ name: "legacy_unknown",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_unencumbered",
+ conditions: ["unencumbered"],
+}
+
+license_kind {
+ name: "legacy_permissive",
+ conditions: ["permissive"],
+}
+
+license_kind {
+ name: "legacy_notice",
+ conditions: ["notice"],
+}
+
+license_kind {
+ name: "legacy_reciprocal",
+ conditions: ["reciprocal"],
+}
+
+license_kind {
+ name: "legacy_restricted",
+ conditions: ["restricted"],
+}
+
+license_kind {
+ name: "legacy_by_exception_only",
+ conditions: ["by_exception_only"],
+}
+
+license_kind {
+ name: "legacy_not_allowed",
+ conditions: ["by_exception_only", "not_allowed"],
+}
+
+license_kind {
+ name: "legacy_proprietary",
+ conditions: ["by_exception_only", "not_allowed", "proprietary"],
+}
diff --git a/licenses/LICENSE b/licenses/LICENSE
new file mode 100644
index 0000000..dae0406
--- /dev/null
+++ b/licenses/LICENSE
@@ -0,0 +1,214 @@
+
+ Copyright (c) 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp
index c04887e..4d97128 100644
--- a/linkerconfig/proto/Android.bp
+++ b/linkerconfig/proto/Android.bp
@@ -7,6 +7,10 @@
type: "lite",
},
srcs: ["linker_config.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
}
python_library_host {
diff --git a/phony/phony.go b/phony/phony.go
index cb60b9f..a31d402 100644
--- a/phony/phony.go
+++ b/phony/phony.go
@@ -52,6 +52,7 @@
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+ data.Entries.WriteLicenseVariables(w)
if p.Host() {
fmt.Fprintln(w, "LOCAL_IS_HOST_MODULE := true")
}
diff --git a/python/binary.go b/python/binary.go
index 1d2400e..416a7ee 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -81,7 +81,7 @@
func PythonBinaryHostFactory() android.Module {
module, _ := NewBinary(android.HostSupported)
- return module.Init()
+ return module.init()
}
func (binary *binaryDecorator) autorun() bool {
diff --git a/python/library.go b/python/library.go
index 0c8d613..b724d2b 100644
--- a/python/library.go
+++ b/python/library.go
@@ -28,11 +28,11 @@
func PythonLibraryHostFactory() android.Module {
module := newModule(android.HostSupported, android.MultilibFirst)
- return module.Init()
+ return module.init()
}
func PythonLibraryFactory() android.Module {
module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
- return module.Init()
+ return module.init()
}
diff --git a/python/python.go b/python/python.go
index 7e376c6..b3e3d13 100644
--- a/python/python.go
+++ b/python/python.go
@@ -20,7 +20,6 @@
"fmt"
"path/filepath"
"regexp"
- "sort"
"strings"
"github.com/google/blueprint"
@@ -33,33 +32,34 @@
android.PreDepsMutators(RegisterPythonPreDepsMutators)
}
+// Exported to support other packages using Python modules in tests.
func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("python_version", versionSplitMutator()).Parallel()
}
-// the version properties that apply to python libraries and binaries.
+// the version-specific properties that apply to python modules.
type VersionProperties struct {
- // true, if the module is required to be built with this version.
+ // whether the module is required to be built with this version.
+ // Defaults to true for Python 3, and false otherwise.
Enabled *bool `android:"arch_variant"`
- // non-empty list of .py files under this strict Python version.
- // srcs may reference the outputs of other modules that produce source files like genrule
- // or filegroup using the syntax ":module".
+ // list of source files specific to this Python version.
+ // Using the syntax ":module", srcs may reference the outputs of other modules that produce source files,
+ // e.g. genrule or filegroup.
Srcs []string `android:"path,arch_variant"`
- // list of source files that should not be used to build the Python module.
- // This is most useful in the arch/multilib variants to remove non-common files
+ // list of source files that should not be used to build the Python module for this version.
+ // This is most useful to remove files that are not common to all Python versions.
Exclude_srcs []string `android:"path,arch_variant"`
- // list of the Python libraries under this Python version.
+ // list of the Python libraries used only for this Python version.
Libs []string `android:"arch_variant"`
- // true, if the binary is required to be built with embedded launcher.
- // TODO(nanzhang): Remove this flag when embedded Python3 is supported later.
- Embedded_launcher *bool `android:"arch_variant"`
+ // whether the binary is required to be built with embedded launcher for this version, defaults to false.
+ Embedded_launcher *bool `android:"arch_variant"` // TODO(b/174041232): Remove this property
}
-// properties that apply to python libraries and binaries.
+// properties that apply to all python modules
type BaseProperties struct {
// the package path prefix within the output artifact at which to place the source/data
// files of the current module.
@@ -93,10 +93,12 @@
Libs []string `android:"arch_variant"`
Version struct {
- // all the "srcs" or Python dependencies that are to be used only for Python2.
+ // Python2-specific properties, including whether Python2 is supported for this module
+ // and version-specific sources, exclusions and dependencies.
Py2 VersionProperties `android:"arch_variant"`
- // all the "srcs" or Python dependencies that are to be used only for Python3.
+ // Python3-specific properties, including whether Python3 is supported for this module
+ // and version-specific sources, exclusions and dependencies.
Py3 VersionProperties `android:"arch_variant"`
} `android:"arch_variant"`
@@ -105,14 +107,16 @@
// runtime.
Actual_version string `blueprint:"mutated"`
- // true, if the module is required to be built with actual_version.
+ // whether the module is required to be built with actual_version.
+ // this is set by the python version mutator based on version-specific properties
Enabled *bool `blueprint:"mutated"`
- // true, if the binary is required to be built with embedded launcher.
- // TODO(nanzhang): Remove this flag when embedded Python3 is supported later.
+ // whether the binary is required to be built with embedded launcher for this actual_version.
+ // this is set by the python version mutator based on version-specific properties
Embedded_launcher *bool `blueprint:"mutated"`
}
+// Used to store files of current module after expanding dependencies
type pathMapping struct {
dest string
src android.Path
@@ -129,11 +133,14 @@
hod android.HostOrDeviceSupported
multilib android.Multilib
- // the bootstrapper is used to bootstrap .par executable.
- // bootstrapper might be nil (Python library module).
+ // interface used to bootstrap .par executable when embedded_launcher is true
+ // this should be set by Python modules which are runnable, e.g. binaries and tests
+ // bootstrapper might be nil (e.g. Python library module).
bootstrapper bootstrapper
- // the installer might be nil.
+ // interface that implements functions required for installation
+ // this should be set by Python modules which are runnable, e.g. binaries and tests
+ // installer might be nil (e.g. Python library module).
installer installer
// the Python files of current module after expanding source dependencies.
@@ -153,9 +160,11 @@
// (.intermediate) module output path as installation source.
installSource android.OptionalPath
+ // Map to ensure sub-part of the AndroidMk for this module is only added once
subAndroidMkOnce map[subAndroidMkProvider]bool
}
+// newModule generates new Python base module
func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -163,6 +172,7 @@
}
}
+// bootstrapper interface should be implemented for runnable modules, e.g. binary and test
type bootstrapper interface {
bootstrapperProps() []interface{}
bootstrap(ctx android.ModuleContext, ActualVersion string, embeddedLauncher bool,
@@ -172,36 +182,45 @@
autorun() bool
}
+// installer interface should be implemented for installable modules, e.g. binary and test
type installer interface {
install(ctx android.ModuleContext, path android.Path)
setAndroidMkSharedLibs(sharedLibs []string)
}
-type PythonDependency interface {
- GetSrcsPathMappings() []pathMapping
- GetDataPathMappings() []pathMapping
- GetSrcsZip() android.Path
+// interface implemented by Python modules to provide source and data mappings and zip to python
+// modules that depend on it
+type pythonDependency interface {
+ getSrcsPathMappings() []pathMapping
+ getDataPathMappings() []pathMapping
+ getSrcsZip() android.Path
}
-func (p *Module) GetSrcsPathMappings() []pathMapping {
+// getSrcsPathMappings gets this module's path mapping of src source path : runfiles destination
+func (p *Module) getSrcsPathMappings() []pathMapping {
return p.srcsPathMappings
}
-func (p *Module) GetDataPathMappings() []pathMapping {
+// getSrcsPathMappings gets this module's path mapping of data source path : runfiles destination
+func (p *Module) getDataPathMappings() []pathMapping {
return p.dataPathMappings
}
-func (p *Module) GetSrcsZip() android.Path {
+// getSrcsZip returns the filepath where the current module's source/data files are zipped.
+func (p *Module) getSrcsZip() android.Path {
return p.srcsZip
}
-var _ PythonDependency = (*Module)(nil)
+var _ pythonDependency = (*Module)(nil)
var _ android.AndroidMkEntriesProvider = (*Module)(nil)
-func (p *Module) Init() android.Module {
-
+func (p *Module) init(additionalProps ...interface{}) android.Module {
p.AddProperties(&p.properties, &p.protoProperties)
+
+ // Add additional properties for bootstrapping/installation
+ // This is currently tied to the bootstrapper interface;
+ // however, these are a combination of properties for the installation and bootstrapping of a module
if p.bootstrapper != nil {
p.AddProperties(p.bootstrapper.bootstrapperProps()...)
}
@@ -212,13 +231,19 @@
return p
}
+// Python-specific tag to transfer information on the purpose of a dependency.
+// This is used when adding a dependency on a module, which can later be accessed when visiting
+// dependencies.
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
}
+// Python-specific tag that indicates that installed files of this module should depend on installed
+// files of the dependency
type installDependencyTag struct {
blueprint.BaseDependencyTag
+ // embedding this struct provides the installation dependency requirement
android.InstallAlwaysNeededDependencyTag
name string
}
@@ -228,7 +253,7 @@
javaDataTag = dependencyTag{name: "javaData"}
launcherTag = dependencyTag{name: "launcher"}
launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
- pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
pyExt = ".py"
protoExt = ".proto"
pyVersion2 = "PY2"
@@ -237,35 +262,37 @@
mainFileName = "__main__.py"
entryPointFile = "entry_point.txt"
parFileExt = ".zip"
- internal = "internal"
+ internalPath = "internal"
)
-// create version variants for modules.
+// versionSplitMutator creates version variants for modules and appends the version-specific
+// properties for a given variant to the properties in the variant module
func versionSplitMutator() func(android.BottomUpMutatorContext) {
return func(mctx android.BottomUpMutatorContext) {
if base, ok := mctx.Module().(*Module); ok {
versionNames := []string{}
+ // collect version specific properties, so that we can merge version-specific properties
+ // into the module's overall properties
versionProps := []VersionProperties{}
// PY3 is first so that we alias the PY3 variant rather than PY2 if both
// are available
- if !(base.properties.Version.Py3.Enabled != nil &&
- *(base.properties.Version.Py3.Enabled) == false) {
+ if proptools.BoolDefault(base.properties.Version.Py3.Enabled, true) {
versionNames = append(versionNames, pyVersion3)
versionProps = append(versionProps, base.properties.Version.Py3)
}
- if base.properties.Version.Py2.Enabled != nil &&
- *(base.properties.Version.Py2.Enabled) == true {
+ if proptools.BoolDefault(base.properties.Version.Py2.Enabled, false) {
versionNames = append(versionNames, pyVersion2)
versionProps = append(versionProps, base.properties.Version.Py2)
}
modules := mctx.CreateLocalVariations(versionNames...)
+ // Alias module to the first variant
if len(versionNames) > 0 {
mctx.AliasVariation(versionNames[0])
}
for i, v := range versionNames {
// set the actual version for Python module.
modules[i].(*Module).properties.Actual_version = v
- // append versioned properties for the Python module
+ // append versioned properties for the Python module to the overall properties
err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
if err != nil {
panic(err)
@@ -275,14 +302,19 @@
}
}
+// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
+// fulfilling HostToolProvider interface.
func (p *Module) HostToolPath() android.OptionalPath {
if p.installer == nil {
// python_library is just meta module, and doesn't have any installer.
return android.OptionalPath{}
}
+ // TODO: This should only be set when building host binaries -- tests built for device would be
+ // setting this incorrectly.
return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
}
+// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
func (p *Module) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -296,12 +328,12 @@
}
func (p *Module) isEmbeddedLauncherEnabled() bool {
- return Bool(p.properties.Embedded_launcher)
+ return p.installer != nil && Bool(p.properties.Embedded_launcher)
}
-func hasSrcExt(srcs []string, ext string) bool {
- for _, src := range srcs {
- if filepath.Ext(src) == ext {
+func anyHasExt(paths []string, ext string) bool {
+ for _, p := range paths {
+ if filepath.Ext(p) == ext {
return true
}
}
@@ -309,10 +341,14 @@
return false
}
-func (p *Module) hasSrcExt(ctx android.BottomUpMutatorContext, ext string) bool {
- return hasSrcExt(p.properties.Srcs, ext)
+func (p *Module) anySrcHasExt(ctx android.BottomUpMutatorContext, ext string) bool {
+ return anyHasExt(p.properties.Srcs, ext)
}
+// DepsMutator mutates dependencies for this module:
+// * handles proto dependencies,
+// * if required, specifies launcher and adds launcher dependencies,
+// * applies python version mutations to Python dependencies
func (p *Module) DepsMutator(ctx android.BottomUpMutatorContext) {
android.ProtoDeps(ctx, &p.protoProperties)
@@ -320,66 +356,61 @@
{"python_version", p.properties.Actual_version},
}
- if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
+ // If sources contain a proto file, add dependency on libprotobuf-python
+ if p.anySrcHasExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
ctx.AddVariationDependencies(versionVariation, pythonLibTag, "libprotobuf-python")
}
+
+ // Add python library dependencies for this python version variation
ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
- switch p.properties.Actual_version {
- case pyVersion2:
+ // If this module will be installed and has an embedded launcher, we need to add dependencies for:
+ // * standard library
+ // * launcher
+ // * shared dependencies of the launcher
+ if p.installer != nil && p.isEmbeddedLauncherEnabled() {
+ var stdLib string
+ var launcherModule string
+ // Add launcher shared lib dependencies. Ideally, these should be
+ // derived from the `shared_libs` property of the launcher. However, we
+ // cannot read the property at this stage and it will be too late to add
+ // dependencies later.
+ launcherSharedLibDeps := []string{
+ "libsqlite",
+ }
+ // Add launcher-specific dependencies for bionic
+ if ctx.Target().Os.Bionic() {
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
+ }
- if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() {
- ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py2-stdlib")
+ switch p.properties.Actual_version {
+ case pyVersion2:
+ stdLib = "py2-stdlib"
- launcherModule := "py2-launcher"
+ launcherModule = "py2-launcher"
if p.bootstrapper.autorun() {
launcherModule = "py2-launcher-autorun"
}
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")
- // Add py2-launcher shared lib dependencies. Ideally, these should be
- // derived from the `shared_libs` property of "py2-launcher". However, we
- // cannot read the property at this stage and it will be too late to add
- // dependencies later.
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libc++")
+ case pyVersion3:
+ stdLib = "py3-stdlib"
- if ctx.Target().Os.Bionic() {
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
- "libc", "libdl", "libm")
- }
- }
-
- case pyVersion3:
-
- if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled() {
- ctx.AddVariationDependencies(versionVariation, pythonLibTag, "py3-stdlib")
-
- launcherModule := "py3-launcher"
+ launcherModule = "py3-launcher"
if p.bootstrapper.autorun() {
launcherModule = "py3-launcher-autorun"
}
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
-
- // Add py3-launcher shared lib dependencies. Ideally, these should be
- // derived from the `shared_libs` property of "py3-launcher". However, we
- // cannot read the property at this stage and it will be too late to add
- // dependencies later.
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, "libsqlite")
if ctx.Device() {
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
- "liblog")
+ launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
}
-
- if ctx.Target().Os.Bionic() {
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
- "libc", "libdl", "libm")
- }
+ default:
+ panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
+ p.properties.Actual_version, ctx.ModuleName()))
}
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
+ ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
+ ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
}
// Emulate the data property for java_data but with the arch variation overridden to "common"
@@ -389,19 +420,25 @@
}
func (p *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.GeneratePythonBuildActions(ctx)
+ p.generatePythonBuildActions(ctx)
- // Only Python binaries and test has non-empty bootstrapper.
+ // Only Python binary and test modules have non-empty bootstrapper.
if p.bootstrapper != nil {
- p.walkTransitiveDeps(ctx)
- embeddedLauncher := false
- embeddedLauncher = p.isEmbeddedLauncherEnabled()
+ // if the module is being installed, we need to collect all transitive dependencies to embed in
+ // the final par
+ p.collectPathsFromTransitiveDeps(ctx)
+ // bootstrap the module, including resolving main file, getting launcher path, and
+ // registering actions to build the par file
+ // bootstrap returns the binary output path
p.installSource = p.bootstrapper.bootstrap(ctx, p.properties.Actual_version,
- embeddedLauncher, p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
+ p.isEmbeddedLauncherEnabled(), p.srcsPathMappings, p.srcsZip, p.depsSrcsZips)
}
+ // Only Python binary and test modules have non-empty installer.
if p.installer != nil {
var sharedLibs []string
+ // if embedded launcher is enabled, we need to collect the shared library depenendencies of the
+ // launcher
ctx.VisitDirectDeps(func(dep android.Module) {
if ctx.OtherModuleDependencyTag(dep) == launcherSharedLibTag {
sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
@@ -409,18 +446,16 @@
})
p.installer.setAndroidMkSharedLibs(sharedLibs)
+ // Install the par file from installSource
if p.installSource.Valid() {
p.installer.install(ctx, p.installSource.Path())
}
}
-
}
-func (p *Module) GeneratePythonBuildActions(ctx android.ModuleContext) {
- // expand python files from "srcs" property.
- srcs := p.properties.Srcs
- exclude_srcs := p.properties.Exclude_srcs
- expandedSrcs := android.PathsForModuleSrcExcludes(ctx, srcs, exclude_srcs)
+// generatePythonBuildActions performs build actions common to all Python modules
+func (p *Module) generatePythonBuildActions(ctx android.ModuleContext) {
+ expandedSrcs := android.PathsForModuleSrcExcludes(ctx, p.properties.Srcs, p.properties.Exclude_srcs)
requiresSrcs := true
if p.bootstrapper != nil && !p.bootstrapper.autorun() {
requiresSrcs = false
@@ -437,9 +472,10 @@
expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
}
- // sanitize pkg_path.
+ // Validate pkg_path property
pkgPath := String(p.properties.Pkg_path)
if pkgPath != "" {
+ // TODO: export validation from android/paths.go handling to replace this duplicated functionality
pkgPath = filepath.Clean(String(p.properties.Pkg_path))
if pkgPath == ".." || strings.HasPrefix(pkgPath, "../") ||
strings.HasPrefix(pkgPath, "/") {
@@ -448,22 +484,35 @@
String(p.properties.Pkg_path))
return
}
- if p.properties.Is_internal != nil && *p.properties.Is_internal {
- pkgPath = filepath.Join(internal, pkgPath)
- }
- } else {
- if p.properties.Is_internal != nil && *p.properties.Is_internal {
- pkgPath = internal
- }
+ }
+ // If property Is_internal is set, prepend pkgPath with internalPath
+ if proptools.BoolDefault(p.properties.Is_internal, false) {
+ pkgPath = filepath.Join(internalPath, pkgPath)
}
+ // generate src:destination path mappings for this module
p.genModulePathMappings(ctx, pkgPath, expandedSrcs, expandedData)
+ // generate the zipfile of all source and data files
p.srcsZip = p.createSrcsZip(ctx, pkgPath)
}
-// generate current module unique pathMappings: <dest: runfiles_path, src: source_path>
-// for python/data files.
+func isValidPythonPath(path string) error {
+ identifiers := strings.Split(strings.TrimSuffix(path, filepath.Ext(path)), "/")
+ for _, token := range identifiers {
+ if !pathComponentRegexp.MatchString(token) {
+ return fmt.Errorf("the path %q contains invalid subpath %q. "+
+ "Subpaths must be at least one character long. "+
+ "The first character must an underscore or letter. "+
+ "Following characters may be any of: letter, digit, underscore, hyphen.",
+ path, token)
+ }
+ }
+ return nil
+}
+
+// For this module, generate unique pathMappings: <dest: runfiles_path, src: source_path>
+// for python/data files expanded from properties.
func (p *Module) genModulePathMappings(ctx android.ModuleContext, pkgPath string,
expandedSrcs, expandedData android.Paths) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
@@ -477,17 +526,11 @@
continue
}
runfilesPath := filepath.Join(pkgPath, s.Rel())
- identifiers := strings.Split(strings.TrimSuffix(runfilesPath,
- filepath.Ext(runfilesPath)), "/")
- for _, token := range identifiers {
- if !pyIdentifierRegexp.MatchString(token) {
- ctx.PropertyErrorf("srcs", "the path %q contains invalid token %q.",
- runfilesPath, token)
- }
+ if err := isValidPythonPath(runfilesPath); err != nil {
+ ctx.PropertyErrorf("srcs", err.Error())
}
- if fillInMap(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) {
- p.srcsPathMappings = append(p.srcsPathMappings,
- pathMapping{dest: runfilesPath, src: s})
+ if !checkForDuplicateOutputPath(ctx, destToPySrcs, runfilesPath, s.String(), p.Name(), p.Name()) {
+ p.srcsPathMappings = append(p.srcsPathMappings, pathMapping{dest: runfilesPath, src: s})
}
}
@@ -497,22 +540,23 @@
continue
}
runfilesPath := filepath.Join(pkgPath, d.Rel())
- if fillInMap(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) {
+ if !checkForDuplicateOutputPath(ctx, destToPyData, runfilesPath, d.String(), p.Name(), p.Name()) {
p.dataPathMappings = append(p.dataPathMappings,
pathMapping{dest: runfilesPath, src: d})
}
}
}
-// register build actions to zip current module's sources.
+// createSrcsZip registers build actions to zip current module's sources and data.
func (p *Module) createSrcsZip(ctx android.ModuleContext, pkgPath string) android.Path {
relativeRootMap := make(map[string]android.Paths)
pathMappings := append(p.srcsPathMappings, p.dataPathMappings...)
var protoSrcs android.Paths
- // "srcs" or "data" properties may have filegroup so it might happen that
- // the relative root for each source path is different.
+ // "srcs" or "data" properties may contain filegroup so it might happen that
+ // the root directory for each source path is different.
for _, path := range pathMappings {
+ // handle proto sources separately
if path.src.Ext() == protoExt {
protoSrcs = append(protoSrcs, path.src)
} else {
@@ -537,24 +581,21 @@
}
if len(relativeRootMap) > 0 {
- var keys []string
-
// in order to keep stable order of soong_zip params, we sort the keys here.
- for k := range relativeRootMap {
- keys = append(keys, k)
- }
- sort.Strings(keys)
+ roots := android.SortedStringKeys(relativeRootMap)
parArgs := []string{}
if pkgPath != "" {
+ // use package path as path prefix
parArgs = append(parArgs, `-P `+pkgPath)
}
- implicits := android.Paths{}
- for _, k := range keys {
- parArgs = append(parArgs, `-C `+k)
- for _, path := range relativeRootMap[k] {
+ paths := android.Paths{}
+ for _, root := range roots {
+ // specify relative root of file in following -f arguments
+ parArgs = append(parArgs, `-C `+root)
+ for _, path := range relativeRootMap[root] {
parArgs = append(parArgs, `-f `+path.String())
- implicits = append(implicits, path)
+ paths = append(paths, path)
}
}
@@ -563,13 +604,15 @@
Rule: zip,
Description: "python library archive",
Output: origSrcsZip,
- Implicits: implicits,
+ // as zip rule does not use $in, there is no real need to distinguish between Inputs and Implicits
+ Implicits: paths,
Args: map[string]string{
"args": strings.Join(parArgs, " "),
},
})
zips = append(zips, origSrcsZip)
}
+ // we may have multiple zips due to separate handling of proto source files
if len(zips) == 1 {
return zips[0]
} else {
@@ -584,25 +627,27 @@
}
}
+// isPythonLibModule returns whether the given module is a Python library Module or not
+// This is distinguished by the fact that Python libraries are not installable, while other Python
+// modules are.
func isPythonLibModule(module blueprint.Module) bool {
if m, ok := module.(*Module); ok {
- // Python library has no bootstrapper or installer.
- if m.bootstrapper != nil || m.installer != nil {
- return false
+ // Python library has no bootstrapper or installer
+ if m.bootstrapper == nil && m.installer == nil {
+ return true
}
- return true
}
return false
}
-// check Python source/data files duplicates for whole runfiles tree since Python binary/test
-// need collect and zip all srcs of whole transitive dependencies to a final par file.
-func (p *Module) walkTransitiveDeps(ctx android.ModuleContext) {
+// collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
+// for module and its transitive dependencies and collects list of data/source file
+// zips for transitive dependencies.
+func (p *Module) collectPathsFromTransitiveDeps(ctx android.ModuleContext) {
// fetch <runfiles_path, source_path> pairs from "src" and "data" properties to
// check duplicates.
destToPySrcs := make(map[string]string)
destToPyData := make(map[string]string)
-
for _, path := range p.srcsPathMappings {
destToPySrcs[path.dest] = path.src.String()
}
@@ -614,6 +659,7 @@
// visit all its dependencies in depth first.
ctx.WalkDeps(func(child, parent android.Module) bool {
+ // we only collect dependencies tagged as python library deps
if ctx.OtherModuleDependencyTag(child) != pythonLibTag {
return false
}
@@ -623,44 +669,46 @@
seen[child] = true
// Python modules only can depend on Python libraries.
if !isPythonLibModule(child) {
- panic(fmt.Errorf(
+ ctx.PropertyErrorf("libs",
"the dependency %q of module %q is not Python library!",
- ctx.ModuleName(), ctx.OtherModuleName(child)))
+ ctx.ModuleName(), ctx.OtherModuleName(child))
}
- if dep, ok := child.(PythonDependency); ok {
- srcs := dep.GetSrcsPathMappings()
+ // collect source and data paths, checking that there are no duplicate output file conflicts
+ if dep, ok := child.(pythonDependency); ok {
+ srcs := dep.getSrcsPathMappings()
for _, path := range srcs {
- if !fillInMap(ctx, destToPySrcs,
- path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child)) {
- continue
- }
- }
- data := dep.GetDataPathMappings()
- for _, path := range data {
- fillInMap(ctx, destToPyData,
+ checkForDuplicateOutputPath(ctx, destToPySrcs,
path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
}
- p.depsSrcsZips = append(p.depsSrcsZips, dep.GetSrcsZip())
+ data := dep.getDataPathMappings()
+ for _, path := range data {
+ checkForDuplicateOutputPath(ctx, destToPyData,
+ path.dest, path.src.String(), ctx.ModuleName(), ctx.OtherModuleName(child))
+ }
+ p.depsSrcsZips = append(p.depsSrcsZips, dep.getSrcsZip())
}
return true
})
}
-func fillInMap(ctx android.ModuleContext, m map[string]string,
- key, value, curModule, otherModule string) bool {
- if oldValue, found := m[key]; found {
+// chckForDuplicateOutputPath checks whether outputPath has already been included in map m, which
+// would result in two files being placed in the same location.
+// If there is a duplicate path, an error is thrown and true is returned
+// Otherwise, outputPath: srcPath is added to m and returns false
+func checkForDuplicateOutputPath(ctx android.ModuleContext, m map[string]string, outputPath, srcPath, curModule, otherModule string) bool {
+ if oldSrcPath, found := m[outputPath]; found {
ctx.ModuleErrorf("found two files to be placed at the same location within zip %q."+
" First file: in module %s at path %q."+
" Second file: in module %s at path %q.",
- key, curModule, oldValue, otherModule, value)
- return false
- } else {
- m[key] = value
+ outputPath, curModule, oldSrcPath, otherModule, srcPath)
+ return true
}
+ m[outputPath] = srcPath
- return true
+ return false
}
+// InstallInData returns true as Python is not supported in the system partition
func (p *Module) InstallInData() bool {
return true
}
diff --git a/python/python_test.go b/python/python_test.go
index 64bc4f6..5c4efa7 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -44,7 +44,7 @@
pkgPathErrTemplate = moduleVariantErrTemplate +
"pkg_path: %q must be a relative path contained in par file."
badIdentifierErrTemplate = moduleVariantErrTemplate +
- "srcs: the path %q contains invalid token %q."
+ "srcs: the path %q contains invalid subpath %q."
dupRunfileErrTemplate = moduleVariantErrTemplate +
"found two files to be placed at the same location within zip %q." +
" First file: in module %s at path %q." +
@@ -370,7 +370,7 @@
} else {
sort.Strings(expErrs)
for i, v := range actErrStrs {
- if v != expErrs[i] {
+ if !strings.Contains(v, expErrs[i]) {
testErrs = append(testErrs, errors.New(v))
}
}
diff --git a/python/test.go b/python/test.go
index 4df71c1..b7cd475 100644
--- a/python/test.go
+++ b/python/test.go
@@ -108,12 +108,12 @@
func PythonTestHostFactory() android.Module {
module := NewTest(android.HostSupportedNoCross)
- return module.Init()
+ return module.init()
}
func PythonTestFactory() android.Module {
module := NewTest(android.HostAndDeviceSupported)
module.multilib = android.MultilibBoth
- return module.Init()
+ return module.init()
}
diff --git a/rust/Android.bp b/rust/Android.bp
index 8618207..df731db 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -15,6 +15,7 @@
"clippy.go",
"compiler.go",
"coverage.go",
+ "image.go",
"library.go",
"prebuilt.go",
"proc_macro.go",
@@ -33,6 +34,7 @@
"clippy_test.go",
"compiler_test.go",
"coverage_test.go",
+ "image_test.go",
"library_test.go",
"project_json_test.go",
"protobuf_test.go",
diff --git a/rust/androidmk.go b/rust/androidmk.go
index c181d67..e9da6fa 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -58,6 +58,7 @@
entries.AddStrings("LOCAL_PROC_MACRO_LIBRARIES", mod.Properties.AndroidMkProcMacroLibs...)
entries.AddStrings("LOCAL_SHARED_LIBRARIES", mod.Properties.AndroidMkSharedLibs...)
entries.AddStrings("LOCAL_STATIC_LIBRARIES", mod.Properties.AndroidMkStaticLibs...)
+ entries.AddStrings("LOCAL_SOONG_LINK_TYPE", mod.makeLinkType)
},
},
}
diff --git a/rust/binary.go b/rust/binary.go
index af39d38..ca07d07 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -24,11 +24,6 @@
}
type BinaryCompilerProperties struct {
- // Change the rustlibs linkage to select rlib linkage by default for device targets.
- // Also link libstd as an rlib as well on device targets.
- // Note: This is the default behavior for host targets.
- Prefer_rlib *bool `android:"arch_variant"`
-
// Builds this binary as a static binary. Implies prefer_rlib true.
//
// Static executables currently only support for bionic targets. Non-bionic targets will not produce a fully static
@@ -115,7 +110,7 @@
}
func (binary *binaryDecorator) preferRlib() bool {
- return Bool(binary.Properties.Prefer_rlib) || Bool(binary.Properties.Static_executable)
+ return Bool(binary.baseCompiler.Properties.Prefer_rlib) || Bool(binary.Properties.Static_executable)
}
func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
@@ -156,8 +151,7 @@
// Binaries default to dylib dependencies for device, rlib for host.
if binary.preferRlib() {
return rlibAutoDep
- }
- if ctx.Device() {
+ } else if ctx.Device() {
return dylibAutoDep
} else {
return rlibAutoDep
@@ -170,3 +164,7 @@
}
return binary.baseCompiler.stdLinkage(ctx)
}
+
+func (binary *binaryDecorator) isDependencyRoot() bool {
+ return true
+}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 35a807b..56d660e 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -31,12 +31,19 @@
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
bindgenClangVersion = "clang-r399163b"
+ _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
+ return override
+ }
+ return bindgenClangVersion
+ })
+
//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
_ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
_ = pctx.SourcePathVariable("bindgenClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
+ "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
_ = pctx.SourcePathVariable("bindgenLibClang",
- "${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/lib64/")
+ "${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/")
//TODO(ivanlozano) Switch this to RuleBuilder
bindgen = pctx.AndroidStaticRule("bindgen",
diff --git a/rust/builder.go b/rust/builder.go
index 6079e30..baeab69 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -137,13 +137,13 @@
var implicitOutputs android.WritablePaths
output.outputFile = outputFile
- crate_name := ctx.RustModule().CrateName()
+ crateName := ctx.RustModule().CrateName()
targetTriple := ctx.toolchain().RustTriple()
// libstd requires a specific environment variable to be set. This is
// not officially documented and may be removed in the future. See
// https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
- if crate_name == "std" {
+ if crateName == "std" {
envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
}
@@ -153,8 +153,8 @@
rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
rustcFlags = append(rustcFlags, flags.RustFlags...)
rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
- if crate_name != "" {
- rustcFlags = append(rustcFlags, "--crate-name="+crate_name)
+ if crateName != "" {
+ rustcFlags = append(rustcFlags, "--crate-name="+crateName)
}
if targetTriple != "" {
rustcFlags = append(rustcFlags, "--target="+targetTriple)
@@ -188,7 +188,8 @@
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() {
implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
diff --git a/rust/compiler.go b/rust/compiler.go
index 8d2f09c..bcea6cc 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -122,6 +122,17 @@
// whether to suppress inclusion of standard crates - defaults to false
No_stdlibs *bool
+
+ // Change the rustlibs linkage to select rlib linkage by default for device targets.
+ // Also link libstd as an rlib as well on device targets.
+ // Note: This is the default behavior for host targets.
+ //
+ // This is primarily meant for rust_binary and rust_ffi modules where the default
+ // linkage of libstd might need to be overridden in some use cases. This should
+ // generally be avoided with other module types since it may cause collisions at
+ // linkage if all dependencies of the root binary module do not link against libstd\
+ // the same way.
+ Prefer_rlib *bool `android:"arch_variant"`
}
type baseCompiler struct {
@@ -154,9 +165,15 @@
panic("baseCompiler does not implement coverageOutputZipPath()")
}
+func (compiler *baseCompiler) preferRlib() bool {
+ return Bool(compiler.Properties.Prefer_rlib)
+}
+
func (compiler *baseCompiler) stdLinkage(ctx *depsContext) RustLinkage {
// For devices, we always link stdlibs in as dylibs by default.
- if ctx.Device() {
+ if compiler.preferRlib() {
+ return RlibLinkage
+ } else if ctx.Device() {
return DylibLinkage
} else {
return RlibLinkage
@@ -197,9 +214,9 @@
flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags())
if ctx.Host() && !ctx.Windows() {
- rpath_prefix := `\$$ORIGIN/`
+ rpathPrefix := `\$$ORIGIN/`
if ctx.Darwin() {
- rpath_prefix = "@loader_path/"
+ rpathPrefix = "@loader_path/"
}
var rpath string
@@ -208,8 +225,8 @@
} else {
rpath = "lib"
}
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
- flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+"../"+rpath)
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+rpath)
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpathPrefix+"../"+rpath)
}
return flags
@@ -219,6 +236,10 @@
panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
}
+func (compiler *baseCompiler) isDependencyRoot() bool {
+ return false
+}
+
func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index e0cc4ce..1f0109f 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -13,6 +13,7 @@
"toolchain.go",
"allowed_list.go",
"x86_darwin_host.go",
+ "x86_linux_bionic_host.go",
"x86_linux_host.go",
"x86_device.go",
"x86_64_device.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index e6643f5..21df024 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -9,10 +9,12 @@
"device/google/cuttlefish",
"external/adhd",
"external/crosvm",
+ "external/libchromeos-rs",
"external/minijail",
"external/rust",
"external/vm_tools/p9",
"frameworks/native/libs/binder/rust",
+ "packages/modules/DnsResolver",
"packages/modules/Virtualization",
"prebuilts/rust",
"system/bt",
diff --git a/rust/config/global.go b/rust/config/global.go
index 22d9567..08ec877 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.48.0"
+ RustDefaultVersion = "1.49.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2018"
Stdlibs = []string{
@@ -44,6 +44,7 @@
GlobalRustFlags = []string{
"--remap-path-prefix $$(pwd)=",
"-C codegen-units=1",
+ "-C debuginfo=2",
"-C opt-level=3",
"-C relocation-model=pic",
}
diff --git a/rust/config/x86_linux_bionic_host.go b/rust/config/x86_linux_bionic_host.go
new file mode 100644
index 0000000..b1a2c17
--- /dev/null
+++ b/rust/config/x86_linux_bionic_host.go
@@ -0,0 +1,73 @@
+// Copyright 2020 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 config
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+var (
+ LinuxBionicRustFlags = []string{}
+ LinuxBionicRustLinkFlags = []string{
+ "-B${cc_config.ClangBin}",
+ "-fuse-ld=lld",
+ "-Wl,--undefined-version",
+ "-nostdlib",
+ }
+)
+
+func init() {
+ registerToolchainFactory(android.LinuxBionic, android.X86_64, linuxBionicX8664ToolchainFactory)
+
+ pctx.StaticVariable("LinuxBionicToolchainRustFlags", strings.Join(LinuxBionicRustFlags, " "))
+ pctx.StaticVariable("LinuxBionicToolchainLinkFlags", strings.Join(LinuxBionicRustLinkFlags, " "))
+}
+
+type toolchainLinuxBionicX8664 struct {
+ toolchain64Bit
+}
+
+func (toolchainLinuxBionicX8664) Supported() bool {
+ return true
+}
+
+func (toolchainLinuxBionicX8664) Bionic() bool {
+ return true
+}
+
+func (t *toolchainLinuxBionicX8664) Name() string {
+ return "x86_64"
+}
+
+func (t *toolchainLinuxBionicX8664) RustTriple() string {
+ return "x86_64-linux-android"
+}
+
+func (t *toolchainLinuxBionicX8664) ToolchainLinkFlags() string {
+ // Prepend the lld flags from cc_config so we stay in sync with cc
+ return "${cc_config.LinuxBionicLldflags} ${config.LinuxBionicToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxBionicX8664) ToolchainRustFlags() string {
+ return "${config.LinuxBionicToolchainRustFlags}"
+}
+
+func linuxBionicX8664ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxBionicX8664Singleton
+}
+
+var toolchainLinuxBionicX8664Singleton Toolchain = &toolchainLinuxBionicX8664{}
diff --git a/rust/image.go b/rust/image.go
new file mode 100644
index 0000000..5e55e22
--- /dev/null
+++ b/rust/image.go
@@ -0,0 +1,153 @@
+// Copyright 2020 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 rust
+
+import (
+ "strings"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+var _ android.ImageInterface = (*Module)(nil)
+
+func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return mod.Properties.CoreVariantNeeded
+}
+
+func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+ return mod.InRamdisk()
+}
+
+func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
+ return mod.InRecovery()
+}
+
+func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
+ return mod.Properties.ExtraVariants
+}
+
+func (ctx *moduleContext) ProductSpecific() bool {
+ return false
+}
+
+func (mod *Module) InRecovery() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInRamdisk() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInRecovery() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInVendorRamdisk() bool {
+ return false
+}
+
+// Returns true when this module is configured to have core and vendor variants.
+func (mod *Module) HasVendorVariant() bool {
+ return mod.IsVndk() || Bool(mod.VendorProperties.Vendor_available)
+}
+
+func (c *Module) VendorAvailable() bool {
+ return Bool(c.VendorProperties.Vendor_available)
+}
+
+func (c *Module) InProduct() bool {
+ return false
+}
+
+func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+ m := module.(*Module)
+ if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+ m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
+ m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+
+ // Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
+ // Hide other vendor variants to avoid collision.
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
+ m.Properties.HideFromMake = true
+ m.HideFromMake()
+ }
+ }
+}
+
+func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+ vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+ platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+
+ // Rust does not support installing to the product image yet.
+ if Bool(mod.VendorProperties.Product_available) {
+ mctx.PropertyErrorf("product_available",
+ "Rust modules do not yet support being available to the product image")
+ } else if mctx.ProductSpecific() {
+ mctx.PropertyErrorf("product_specific",
+ "Rust modules do not yet support installing to the product image.")
+ } else if Bool(mod.VendorProperties.Double_loadable) {
+ mctx.PropertyErrorf("double_loadable",
+ "Rust modules do not yet support double loading")
+ }
+
+ coreVariantNeeded := true
+ var vendorVariants []string
+
+ if Bool(mod.VendorProperties.Vendor_available) {
+ if vendorSpecific {
+ mctx.PropertyErrorf("vendor_available",
+ "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+ }
+
+ if lib, ok := mod.compiler.(libraryInterface); ok {
+ // Explicitly disallow rust_ffi variants which produce shared libraries from setting vendor_available.
+ // Vendor variants do not produce an error for dylibs, rlibs with dylib-std linkage are disabled in the respective library
+ // mutators until support is added.
+ //
+ // We can't check shared() here because image mutator is called before the library mutator, so we need to
+ // check buildShared()
+ if lib.buildShared() {
+ mctx.PropertyErrorf("vendor_available",
+ "vendor_available can only be set for rust_ffi_static modules.")
+ } else if Bool(mod.VendorProperties.Vendor_available) == true {
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ }
+ }
+ }
+
+ if vendorSpecific {
+ if lib, ok := mod.compiler.(libraryInterface); !ok || (ok && !lib.static()) {
+ mctx.ModuleErrorf("Rust vendor specific modules are currently only supported for rust_ffi_static modules.")
+ } else {
+ coreVariantNeeded = false
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ }
+ }
+
+ mod.Properties.CoreVariantNeeded = coreVariantNeeded
+ for _, variant := range android.FirstUniqueStrings(vendorVariants) {
+ mod.Properties.ExtraVariants = append(mod.Properties.ExtraVariants, cc.VendorVariationPrefix+variant)
+ }
+
+}
diff --git a/rust/image_test.go b/rust/image_test.go
new file mode 100644
index 0000000..025b0fd
--- /dev/null
+++ b/rust/image_test.go
@@ -0,0 +1,73 @@
+// Copyright 2020 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 rust
+
+import (
+ "testing"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+// Test that cc_binaries can link against rust_ffi_static libraries.
+func TestVendorLinkage(t *testing.T) {
+ ctx := testRust(t, `
+ cc_binary {
+ name: "fizz_vendor",
+ static_libs: ["libfoo_vendor"],
+ soc_specific: true,
+ }
+ rust_ffi_static {
+ name: "libfoo_vendor",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor_available: true,
+ }
+ `)
+
+ vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_arm64_armv8-a").Module().(*cc.Module)
+
+ if !android.InList("libfoo_vendor", vendorBinary.Properties.AndroidMkStaticLibs) {
+ t.Errorf("vendorBinary should have a dependency on libfoo_vendor")
+ }
+}
+
+// Test that shared libraries cannot be made vendor available until proper support is added.
+func TestForbiddenVendorLinkage(t *testing.T) {
+ testRustError(t, "vendor_available can only be set for rust_ffi_static modules", `
+ rust_ffi_shared {
+ name: "libfoo_vendor",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor_available: true,
+ }
+ `)
+ testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
+ rust_ffi {
+ name: "libfoo_vendor",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor: true,
+ }
+ `)
+ testRustError(t, "Rust vendor specific modules are currently only supported for rust_ffi_static modules.", `
+ rust_library {
+ name: "libfoo_vendor",
+ crate_name: "foo",
+ srcs: ["foo.rs"],
+ vendor: true,
+ }
+ `)
+}
diff --git a/rust/library.go b/rust/library.go
index 971588d..4ac52b4 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -159,14 +159,6 @@
return library.MutatedProperties.VariantIsStatic
}
-func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
- // libraries should only request the RlibLinkage when building a static FFI or when variant is StaticStd
- if library.static() || library.MutatedProperties.VariantIsStaticStd {
- return RlibLinkage
- }
- return DefaultLinkage
-}
-
func (library *libraryDecorator) source() bool {
return library.MutatedProperties.VariantIsSource
}
@@ -228,7 +220,9 @@
}
func (library *libraryDecorator) autoDep(ctx BaseModuleContext) autoDep {
- if library.rlib() || library.static() {
+ if library.preferRlib() {
+ return rlibAutoDep
+ } else if library.rlib() || library.static() {
return rlibAutoDep
} else if library.dylib() || library.shared() {
return dylibAutoDep
@@ -237,6 +231,15 @@
}
}
+func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
+ if library.static() || library.MutatedProperties.VariantIsStaticStd {
+ return RlibLinkage
+ } else if library.baseCompiler.preferRlib() {
+ return RlibLinkage
+ }
+ return DefaultLinkage
+}
+
var _ compiler = (*libraryDecorator)(nil)
var _ libraryInterface = (*libraryDecorator)(nil)
var _ exportedFlagsProducer = (*libraryDecorator)(nil)
@@ -430,6 +433,7 @@
if library.sourceProvider != nil {
// Assume the first source from the source provider is the library entry point.
srcPath = library.sourceProvider.Srcs()[0]
+ deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
} else {
srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
}
@@ -601,6 +605,11 @@
v.(*Module).compiler.(libraryInterface).setRlib()
case dylibVariation:
v.(*Module).compiler.(libraryInterface).setDylib()
+ if v.(*Module).ModuleBase.ImageVariation().Variation != android.CoreVariation {
+ // TODO(b/165791368)
+ // Disable dylib non-core variations until we support these.
+ v.(*Module).Disable()
+ }
case "source":
v.(*Module).compiler.(libraryInterface).setSource()
// The source variant does not produce any library.
@@ -637,6 +646,12 @@
dylib := modules[1].(*Module)
rlib.compiler.(libraryInterface).setRlibStd()
dylib.compiler.(libraryInterface).setDylibStd()
+ if dylib.ModuleBase.ImageVariation().Variation != android.CoreVariation {
+ // TODO(b/165791368)
+ // Disable rlibs that link against dylib-std on non-core variations until non-core dylib
+ // variants are properly supported.
+ dylib.Disable()
+ }
rlib.Properties.SubName += RlibStdlibSuffix
dylib.Properties.SubName += DylibStdlibSuffix
}
diff --git a/rust/library_test.go b/rust/library_test.go
index fec3992..54cd2a5 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -251,6 +251,13 @@
srcs: ["foo.rs"],
crate_name: "bar",
rustlibs: ["libfoo"],
+ }
+ rust_ffi {
+ name: "libbar.prefer_rlib",
+ srcs: ["foo.rs"],
+ crate_name: "bar",
+ rustlibs: ["libfoo"],
+ prefer_rlib: true,
}`)
libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
@@ -260,6 +267,9 @@
libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
+ // prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
+ libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
+
if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) {
t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib")
}
@@ -279,4 +289,8 @@
if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
}
+ if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
+ t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
+ }
+
}
diff --git a/rust/project_json.go b/rust/project_json.go
index 8d9e50c..32ce6f4 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -45,10 +45,11 @@
}
type rustProjectCrate struct {
- RootModule string `json:"root_module"`
- Edition string `json:"edition,omitempty"`
- Deps []rustProjectDep `json:"deps"`
- Cfgs []string `json:"cfgs"`
+ DisplayName string `json:"display_name"`
+ RootModule string `json:"root_module"`
+ Edition string `json:"edition,omitempty"`
+ Deps []rustProjectDep `json:"deps"`
+ Cfgs []string `json:"cfgs"`
}
type rustProjectJson struct {
@@ -58,13 +59,13 @@
// crateInfo is used during the processing to keep track of the known crates.
type crateInfo struct {
- ID int
- Deps map[string]int
+ Idx int // Index of the crate in rustProjectJson.Crates slice.
+ Deps map[string]int // The keys are the module names and not the crate names.
}
type projectGeneratorSingleton struct {
project rustProjectJson
- knownCrates map[string]crateInfo
+ knownCrates map[string]crateInfo // Keys are module names.
}
func rustProjectGeneratorSingleton() android.Singleton {
@@ -75,94 +76,190 @@
android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
}
-// librarySource finds the main source file (.rs) for a crate.
-func librarySource(ctx android.SingletonContext, rModule *Module, rustLib *libraryDecorator) (string, bool) {
- srcs := rustLib.baseCompiler.Properties.Srcs
+// sourceProviderVariantSource returns the path to the source file if this
+// module variant should be used as a priority.
+//
+// SourceProvider modules may have multiple variants considered as source
+// (e.g., x86_64 and armv8). For a module available on device, use the source
+// generated for the target. For a host-only module, use the source generated
+// for the host.
+func sourceProviderVariantSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
+ rustLib, ok := rModule.compiler.(*libraryDecorator)
+ if !ok {
+ return "", false
+ }
+ if rustLib.source() {
+ switch rModule.hod {
+ case android.HostSupported, android.HostSupportedNoCross:
+ if rModule.Target().String() == ctx.Config().BuildOSTarget.String() {
+ src := rustLib.sourceProvider.Srcs()[0]
+ return src.String(), true
+ }
+ default:
+ if rModule.Target().String() == ctx.Config().AndroidFirstDeviceTarget.String() {
+ src := rustLib.sourceProvider.Srcs()[0]
+ return src.String(), true
+ }
+ }
+ }
+ return "", false
+}
+
+// sourceProviderSource finds the main source file of a source-provider crate.
+func sourceProviderSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
+ rustLib, ok := rModule.compiler.(*libraryDecorator)
+ if !ok {
+ return "", false
+ }
+ if rustLib.source() {
+ // This is a source-variant, check if we are the right variant
+ // depending on the module configuration.
+ if src, ok := sourceProviderVariantSource(ctx, rModule); ok {
+ return src, true
+ }
+ }
+ foundSource := false
+ sourceSrc := ""
+ // Find the variant with the source and return its.
+ ctx.VisitAllModuleVariants(rModule, func(variant android.Module) {
+ if foundSource {
+ return
+ }
+ // All variants of a source provider library are libraries.
+ rVariant, _ := variant.(*Module)
+ variantLib, _ := rVariant.compiler.(*libraryDecorator)
+ if variantLib.source() {
+ sourceSrc, ok = sourceProviderVariantSource(ctx, rVariant)
+ if ok {
+ foundSource = true
+ }
+ }
+ })
+ if !foundSource {
+ fmt.Errorf("No valid source for source provider found: %v\n", rModule)
+ }
+ return sourceSrc, foundSource
+}
+
+// crateSource finds the main source file (.rs) for a crate.
+func crateSource(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (string, bool) {
+ // Basic libraries, executables and tests.
+ srcs := comp.Properties.Srcs
if len(srcs) != 0 {
return path.Join(ctx.ModuleDir(rModule), srcs[0]), true
}
- if !rustLib.source() {
- return "", false
+ // SourceProvider libraries.
+ if rModule.sourceProvider != nil {
+ return sourceProviderSource(ctx, rModule)
}
- // It is a SourceProvider module. If this module is host only, uses the variation for the host.
- // Otherwise, use the variation for the primary target.
- switch rModule.hod {
- case android.HostSupported:
- case android.HostSupportedNoCross:
- if rModule.Target().String() != ctx.Config().BuildOSTarget.String() {
- return "", false
- }
- default:
- if rModule.Target().String() != ctx.Config().AndroidFirstDeviceTarget.String() {
- return "", false
- }
- }
- src := rustLib.sourceProvider.Srcs()[0]
- return src.String(), true
+ return "", false
}
+// mergeDependencies visits all the dependencies for module and updates crate and deps
+// with any new dependency.
func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext,
- module android.Module, crate *rustProjectCrate, deps map[string]int) {
+ module *Module, crate *rustProjectCrate, deps map[string]int) {
ctx.VisitDirectDeps(module, func(child android.Module) {
- childId, childCrateName, ok := singleton.appendLibraryAndDeps(ctx, child)
- if !ok {
- return
- }
// Skip intra-module dependencies (i.e., generated-source library depending on the source variant).
if module.Name() == child.Name() {
return
}
- if _, ok = deps[ctx.ModuleName(child)]; ok {
+ // Skip unsupported modules.
+ rChild, compChild, ok := isModuleSupported(ctx, child)
+ if !ok {
return
}
- crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName})
- deps[ctx.ModuleName(child)] = childId
+ // For unknown dependency, add it first.
+ var childId int
+ cInfo, known := singleton.knownCrates[rChild.Name()]
+ if !known {
+ childId, ok = singleton.addCrate(ctx, rChild, compChild)
+ if !ok {
+ return
+ }
+ } else {
+ childId = cInfo.Idx
+ }
+ // Is this dependency known already?
+ if _, ok = deps[child.Name()]; ok {
+ return
+ }
+ crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: rChild.CrateName()})
+ deps[child.Name()] = childId
})
}
-// appendLibraryAndDeps creates a rustProjectCrate for the module argument and appends it to singleton.project.
-// It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the
-// current module is already in singleton.knownCrates, its dependencies are merged. Returns a tuple (id, crate_name, ok).
-func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.SingletonContext, module android.Module) (int, string, bool) {
+// isModuleSupported returns the RustModule and baseCompiler if the module
+// should be considered for inclusion in rust-project.json.
+func isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, *baseCompiler, bool) {
rModule, ok := module.(*Module)
if !ok {
- return 0, "", false
+ return nil, nil, false
}
if rModule.compiler == nil {
- return 0, "", false
+ return nil, nil, false
}
- rustLib, ok := rModule.compiler.(*libraryDecorator)
+ var comp *baseCompiler
+ switch c := rModule.compiler.(type) {
+ case *libraryDecorator:
+ comp = c.baseCompiler
+ case *binaryDecorator:
+ comp = c.baseCompiler
+ case *testDecorator:
+ comp = c.binaryDecorator.baseCompiler
+ default:
+ return nil, nil, false
+ }
+ return rModule, comp, true
+}
+
+// addCrate adds a crate to singleton.project.Crates ensuring that required
+// dependencies are also added. It returns the index of the new crate in
+// singleton.project.Crates
+func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (int, bool) {
+ rootModule, ok := crateSource(ctx, rModule, comp)
if !ok {
- return 0, "", false
+ fmt.Errorf("Unable to find source for valid module: %v", rModule)
+ return 0, false
}
- moduleName := ctx.ModuleName(module)
- crateName := rModule.CrateName()
- if cInfo, ok := singleton.knownCrates[moduleName]; ok {
- // We have seen this crate already; merge any new dependencies.
- crate := singleton.project.Crates[cInfo.ID]
- singleton.mergeDependencies(ctx, module, &crate, cInfo.Deps)
- singleton.project.Crates[cInfo.ID] = crate
- return cInfo.ID, crateName, true
+
+ crate := rustProjectCrate{
+ DisplayName: rModule.Name(),
+ RootModule: rootModule,
+ Edition: comp.edition(),
+ Deps: make([]rustProjectDep, 0),
+ Cfgs: make([]string, 0),
}
- crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)}
- rootModule, ok := librarySource(ctx, rModule, rustLib)
- if !ok {
- return 0, "", false
- }
- crate.RootModule = rootModule
- crate.Edition = rustLib.baseCompiler.edition()
deps := make(map[string]int)
- singleton.mergeDependencies(ctx, module, &crate, deps)
+ singleton.mergeDependencies(ctx, rModule, &crate, deps)
- id := len(singleton.project.Crates)
- singleton.knownCrates[moduleName] = crateInfo{ID: id, Deps: deps}
+ idx := len(singleton.project.Crates)
+ singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps}
singleton.project.Crates = append(singleton.project.Crates, crate)
// rust-analyzer requires that all crates belong to at least one root:
// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule))
- return id, crateName, true
+ return idx, true
+}
+
+// appendCrateAndDependencies creates a rustProjectCrate for the module argument and appends it to singleton.project.
+// It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the
+// current module is already in singleton.knownCrates, its dependencies are merged.
+func (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) {
+ rModule, comp, ok := isModuleSupported(ctx, module)
+ if !ok {
+ return
+ }
+ // If we have seen this crate already; merge any new dependencies.
+ if cInfo, ok := singleton.knownCrates[module.Name()]; ok {
+ crate := singleton.project.Crates[cInfo.Idx]
+ singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps)
+ singleton.project.Crates[cInfo.Idx] = crate
+ return
+ }
+ singleton.addCrate(ctx, rModule, comp)
}
func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -172,7 +269,7 @@
singleton.knownCrates = make(map[string]crateInfo)
ctx.VisitAllModules(func(module android.Module) {
- singleton.appendLibraryAndDeps(ctx, module)
+ singleton.appendCrateAndDependencies(ctx, module)
})
path := android.PathForOutput(ctx, rustProjectJsonFileName)
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 16699c1..ba66215 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -67,6 +67,37 @@
return crates
}
+// validateCrate ensures that a crate can be parsed as a map.
+func validateCrate(t *testing.T, crate interface{}) map[string]interface{} {
+ c, ok := crate.(map[string]interface{})
+ if !ok {
+ t.Fatalf("Unexpected type for crate: %v", c)
+ }
+ return c
+}
+
+// validateDependencies parses the dependencies for a crate. It returns a list
+// of the dependencies name.
+func validateDependencies(t *testing.T, crate map[string]interface{}) []string {
+ var dependencies []string
+ deps, ok := crate["deps"].([]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for deps: %v", crate["deps"])
+ }
+ for _, dep := range deps {
+ d, ok := dep.(map[string]interface{})
+ if !ok {
+ t.Errorf("Unexpected format for dependency: %v", dep)
+ }
+ name, ok := d["name"].(string)
+ if !ok {
+ t.Errorf("Dependency is missing the name key: %v", d)
+ }
+ dependencies = append(dependencies, name)
+ }
+ return dependencies
+}
+
func TestProjectJsonDep(t *testing.T) {
bp := `
rust_library {
@@ -85,13 +116,36 @@
validateJsonCrates(t, jsonContent)
}
+func TestProjectJsonBinary(t *testing.T) {
+ bp := `
+ rust_binary {
+ name: "libz",
+ srcs: ["z/src/lib.rs"],
+ crate_name: "z"
+ }
+ `
+ jsonContent := testProjectJson(t, bp)
+ crates := validateJsonCrates(t, jsonContent)
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ rootModule, ok := crate["root_module"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+ }
+ if rootModule == "z/src/lib.rs" {
+ return
+ }
+ }
+ t.Errorf("Entry for binary %q not found: %s", "a", jsonContent)
+}
+
func TestProjectJsonBindGen(t *testing.T) {
bp := `
rust_library {
- name: "liba",
- srcs: ["src/lib.rs"],
+ name: "libd",
+ srcs: ["d/src/lib.rs"],
rlibs: ["libbindings1"],
- crate_name: "a"
+ crate_name: "d"
}
rust_bindgen {
name: "libbindings1",
@@ -101,10 +155,10 @@
wrapper_src: "src/any.h",
}
rust_library_host {
- name: "libb",
- srcs: ["src/lib.rs"],
+ name: "libe",
+ srcs: ["e/src/lib.rs"],
rustlibs: ["libbindings2"],
- crate_name: "b"
+ crate_name: "e"
}
rust_bindgen_host {
name: "libbindings2",
@@ -116,10 +170,7 @@
jsonContent := testProjectJson(t, bp)
crates := validateJsonCrates(t, jsonContent)
for _, c := range crates {
- crate, ok := c.(map[string]interface{})
- if !ok {
- t.Fatalf("Unexpected type for crate: %v", c)
- }
+ crate := validateCrate(t, c)
rootModule, ok := crate["root_module"].(string)
if !ok {
t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
@@ -133,20 +184,25 @@
}
// Check that libbindings1 does not depend on itself.
if strings.Contains(rootModule, "libbindings1") {
- deps, ok := crate["deps"].([]interface{})
- if !ok {
- t.Errorf("Unexpected format for deps: %v", crate["deps"])
- }
- for _, dep := range deps {
- d, ok := dep.(map[string]interface{})
- if !ok {
- t.Errorf("Unexpected format for dep: %v", dep)
- }
- if d["name"] == "bindings1" {
+ for _, depName := range validateDependencies(t, crate) {
+ if depName == "bindings1" {
t.Errorf("libbindings1 depends on itself")
}
}
}
+ // Check that liba depends on libbindings1
+ if strings.Contains(rootModule, "d/src/lib.rs") {
+ found := false
+ for _, depName := range validateDependencies(t, crate) {
+ if depName == "bindings1" {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("liba does not depend on libbindings1: %v", crate)
+ }
+ }
}
}
@@ -171,20 +227,18 @@
`
jsonContent := testProjectJson(t, bp)
crates := validateJsonCrates(t, jsonContent)
- for _, crate := range crates {
- c := crate.(map[string]interface{})
- if c["root_module"] == "b/src/lib.rs" {
- deps, ok := c["deps"].([]interface{})
- if !ok {
- t.Errorf("Unexpected format for deps: %v", c["deps"])
- }
+ for _, c := range crates {
+ crate := validateCrate(t, c)
+ rootModule, ok := crate["root_module"].(string)
+ if !ok {
+ t.Fatalf("Unexpected type for root_module: %v", crate["root_module"])
+ }
+ // Make sure that b has 2 different dependencies.
+ if rootModule == "b/src/lib.rs" {
aCount := 0
- for _, dep := range deps {
- d, ok := dep.(map[string]interface{})
- if !ok {
- t.Errorf("Unexpected format for dep: %v", dep)
- }
- if d["name"] == "a" {
+ deps := validateDependencies(t, crate)
+ for _, depName := range deps {
+ if depName == "a" {
aCount++
}
}
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 0e79089..b91fea8 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -31,24 +31,22 @@
type PluginType int
-const (
- Protobuf PluginType = iota
- Grpc
-)
-
func init() {
android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
- android.RegisterModuleType("rust_grpcio", RustGrpcioFactory)
- android.RegisterModuleType("rust_grpcio_host", RustGrpcioHostFactory)
}
var _ SourceProvider = (*protobufDecorator)(nil)
type ProtobufProperties struct {
- // List of realtive paths to proto files that will be used to generate the source
+ // List of relative paths to proto files that will be used to generate the source.
+ // Either this or grpc_protos must be defined.
Protos []string `android:"path,arch_variant"`
+ // List of relative paths to GRPC-containing proto files that will be used to generate the source.
+ // Either this or protos must be defined.
+ Grpc_protos []string `android:"path,arch_variant"`
+
// List of additional flags to pass to aprotoc
Proto_flags []string `android:"arch_variant"`
@@ -60,29 +58,54 @@
*BaseSourceProvider
Properties ProtobufProperties
- plugin PluginType
+ protoNames []string
+ grpcNames []string
+
+ grpcProtoFlags android.ProtoFlags
+ protoFlags android.ProtoFlags
}
func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
var protoFlags android.ProtoFlags
- var pluginPaths android.Paths
- var protoNames []string
+ var grpcProtoFlags android.ProtoFlags
+ var commonProtoFlags []string
- protoFlags.OutTypeFlag = "--rust_out"
outDir := android.PathForModuleOut(ctx)
-
- pluginPaths, protoFlags = proto.setupPlugin(ctx, protoFlags, outDir)
-
- protoFlags.Flags = append(protoFlags.Flags, defaultProtobufFlags...)
- protoFlags.Flags = append(protoFlags.Flags, proto.Properties.Proto_flags...)
-
- protoFlags.Deps = append(protoFlags.Deps, pluginPaths...)
-
protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
+ grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos)
+ protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
+
+ commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...)
+ commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...)
+ commonProtoFlags = append(commonProtoFlags, "--plugin=protoc-gen-rust="+protoPluginPath.String())
+
+ if len(protoFiles) > 0 {
+ protoFlags.OutTypeFlag = "--rust_out"
+ protoFlags.Flags = append(protoFlags.Flags, commonProtoFlags...)
+
+ protoFlags.Deps = append(protoFlags.Deps, protoPluginPath)
+ }
+
+ if len(grpcFiles) > 0 {
+ grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
+
+ grpcProtoFlags.OutTypeFlag = "--rust_out"
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--grpc_out="+outDir.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, commonProtoFlags...)
+
+ grpcProtoFlags.Deps = append(grpcProtoFlags.Deps, grpcPath, protoPluginPath)
+ }
+
+ if len(protoFiles) == 0 && len(grpcFiles) == 0 {
+ ctx.PropertyErrorf("protos",
+ "at least one protobuf must be defined in either protos or grpc_protos.")
+ }
// Add exported dependency include paths
for _, include := range deps.depIncludePaths {
protoFlags.Flags = append(protoFlags.Flags, "-I"+include.String())
+ grpcProtoFlags.Flags = append(grpcProtoFlags.Flags, "-I"+include.String())
}
stem := proto.BaseSourceProvider.getStem(ctx)
@@ -91,53 +114,76 @@
stemFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
// stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
- outputs := android.WritablePaths{stemFile}
+ var outputs android.WritablePaths
rule := android.NewRuleBuilder(pctx, ctx)
+
for _, protoFile := range protoFiles {
- protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
- protoNames = append(protoNames, protoName)
-
- protoOut := android.PathForModuleOut(ctx, protoName+".rs")
- ruleOutputs := android.WritablePaths{android.WritablePath(protoOut)}
-
- if proto.plugin == Grpc {
- grpcOut := android.PathForModuleOut(ctx, protoName+grpcSuffix+".rs")
- ruleOutputs = append(ruleOutputs, android.WritablePath(grpcOut))
+ // Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
+ if android.InList(protoFile.String(), grpcFiles.Strings()) {
+ ctx.PropertyErrorf("protos",
+ "A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
+ protoFile.String())
}
+ protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
+ proto.protoNames = append(proto.protoNames, protoName)
+
+ protoOut := android.PathForModuleOut(ctx, protoName+".rs")
depFile := android.PathForModuleOut(ctx, protoName+".d")
+ ruleOutputs := android.WritablePaths{protoOut, depFile}
+
android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
outputs = append(outputs, ruleOutputs...)
}
- rule.Command().
- Implicits(outputs.Paths()).
- Text("printf '" + proto.genModFileContents(ctx, protoNames) + "' >").
- Output(stemFile)
+ for _, grpcFile := range grpcFiles {
+ grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
+ proto.grpcNames = append(proto.grpcNames, grpcName)
+
+ // GRPC protos produce two files, a proto.rs and a proto_grpc.rs
+ protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
+ grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
+ depFile := android.PathForModuleOut(ctx, grpcName+".d")
+
+ ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
+
+ android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
+ outputs = append(outputs, ruleOutputs...)
+ }
+
+ // Check that all proto base filenames are unique as outputs are written to the same directory.
+ baseFilenames := append(proto.protoNames, proto.grpcNames...)
+ if len(baseFilenames) != len(android.FirstUniqueStrings(baseFilenames)) {
+ ctx.PropertyErrorf("protos", "proto filenames must be unique across 'protos' and 'grpc_protos' "+
+ "to be used in the same rust_protobuf module. For example, foo.proto and src/foo.proto will conflict.")
+ }
+
+ android.WriteFileRule(ctx, stemFile, proto.genModFileContents())
rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
- proto.BaseSourceProvider.OutputFiles = outputs.Paths()
+ // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
+ proto.BaseSourceProvider.OutputFiles = append(android.Paths{stemFile}, outputs.Paths()...)
// mod_stem.rs is the entry-point for our library modules, so this is what we return.
return stemFile
}
-func (proto *protobufDecorator) genModFileContents(ctx ModuleContext, protoNames []string) string {
+func (proto *protobufDecorator) genModFileContents() string {
lines := []string{
"// @Soong generated Source",
}
- for _, protoName := range protoNames {
+ for _, protoName := range proto.protoNames {
lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
-
- if proto.plugin == Grpc {
- lines = append(lines, fmt.Sprintf("pub mod %s%s;", protoName, grpcSuffix))
- }
}
- if proto.plugin == Grpc {
+ for _, grpcName := range proto.grpcNames {
+ lines = append(lines, fmt.Sprintf("pub mod %s;", grpcName))
+ lines = append(lines, fmt.Sprintf("pub mod %s%s;", grpcName, grpcSuffix))
+ }
+ if len(proto.grpcNames) > 0 {
lines = append(
lines,
"pub mod empty {",
@@ -145,28 +191,7 @@
"}")
}
- return strings.Join(lines, "\\n")
-}
-
-func (proto *protobufDecorator) setupPlugin(ctx ModuleContext, protoFlags android.ProtoFlags, outDir android.ModuleOutPath) (android.Paths, android.ProtoFlags) {
- pluginPaths := []android.Path{}
-
- if proto.plugin == Protobuf {
- pluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
- pluginPaths = append(pluginPaths, pluginPath)
- protoFlags.Flags = append(protoFlags.Flags, "--plugin="+pluginPath.String())
- } else if proto.plugin == Grpc {
- grpcPath := ctx.Config().HostToolPath(ctx, "grpc_rust_plugin")
- protobufPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
- pluginPaths = append(pluginPaths, grpcPath, protobufPath)
- protoFlags.Flags = append(protoFlags.Flags, "--grpc_out="+outDir.String())
- protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-grpc="+grpcPath.String())
- protoFlags.Flags = append(protoFlags.Flags, "--plugin=protoc-gen-rust="+protobufPath.String())
- } else {
- ctx.ModuleErrorf("Unknown protobuf plugin type requested")
- }
-
- return pluginPaths, protoFlags
+ return strings.Join(lines, "\n")
}
func (proto *protobufDecorator) SourceProviderProps() []interface{} {
@@ -178,7 +203,7 @@
deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
- if proto.plugin == Grpc {
+ if len(proto.Properties.Grpc_protos) > 0 {
deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
}
@@ -201,34 +226,10 @@
return module.Init()
}
-func RustGrpcioFactory() android.Module {
- module, _ := NewRustGrpcio(android.HostAndDeviceSupported)
- return module.Init()
-}
-
-// A host-only variant of rust_protobuf. Refer to rust_protobuf for more details.
-func RustGrpcioHostFactory() android.Module {
- module, _ := NewRustGrpcio(android.HostSupported)
- return module.Init()
-}
-
func NewRustProtobuf(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
protobuf := &protobufDecorator{
BaseSourceProvider: NewSourceProvider(),
Properties: ProtobufProperties{},
- plugin: Protobuf,
- }
-
- module := NewSourceProviderModule(hod, protobuf, false)
-
- return module, protobuf
-}
-
-func NewRustGrpcio(hod android.HostOrDeviceSupported) (*Module, *protobufDecorator) {
- protobuf := &protobufDecorator{
- BaseSourceProvider: NewSourceProvider(),
- Properties: ProtobufProperties{},
- plugin: Grpc,
}
module := NewSourceProviderModule(hod, protobuf, false)
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 608a4e8..1ac66f3 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -69,31 +69,19 @@
}
}
-func TestRustGrpcio(t *testing.T) {
+func TestRustGrpc(t *testing.T) {
ctx := testRust(t, `
- rust_grpcio {
+ rust_protobuf {
name: "librust_grpcio",
- protos: ["buf.proto", "proto.proto"],
+ protos: ["buf.proto"],
+ grpc_protos: ["foo.proto", "proto.proto"],
crate_name: "rust_grpcio",
source_stem: "buf",
- shared_libs: ["libfoo_shared"],
- static_libs: ["libfoo_static"],
- }
- cc_library_shared {
- name: "libfoo_shared",
- export_include_dirs: ["shared_include"],
- }
- cc_library_static {
- name: "libfoo_static",
- export_include_dirs: ["static_include"],
}
`)
// Check that libprotobuf is added as a dependency.
librust_grpcio_module := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_dylib").Module().(*Module)
- if !android.InList("libprotobuf", librust_grpcio_module.Properties.AndroidMkDylibs) {
- t.Errorf("libprotobuf dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
- }
// Check that libgrpcio is added as a dependency.
if !android.InList("libgrpcio", librust_grpcio_module.Properties.AndroidMkDylibs) {
@@ -106,20 +94,12 @@
}
// Make sure the correct plugin is being used.
- librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("buf_grpc.rs")
+ librust_grpcio_out := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").Output("foo_grpc.rs")
cmd := librust_grpcio_out.RuleParams.Command
if w := "protoc-gen-grpc"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
}
- // Check exported include directories
- if w := "-Ishared_include"; !strings.Contains(cmd, w) {
- t.Errorf("expected %q in %q", w, cmd)
- }
- if w := "-Istatic_include"; !strings.Contains(cmd, w) {
- t.Errorf("expected %q in %q", w, cmd)
- }
-
// Check that we're including the exported directory from libprotobuf-cpp-full
if w := "-Ilibprotobuf-cpp-full-includes"; !strings.Contains(cmd, w) {
t.Errorf("expected %q in %q", w, cmd)
@@ -132,3 +112,25 @@
librust_grpcio_outputs)
}
}
+
+func TestRustProtoErrors(t *testing.T) {
+ testRustError(t, "A proto can only be added once to either grpc_protos or protos.*", `
+ rust_protobuf {
+ name: "librust_grpcio",
+ protos: ["buf.proto"],
+ grpc_protos: ["buf.proto"],
+ crate_name: "rust_grpcio",
+ source_stem: "buf",
+ }
+ `)
+
+ testRustError(t, "proto filenames must be unique across 'protos' and 'grpc_protos'.*", `
+ rust_protobuf {
+ name: "librust_grpcio",
+ protos: ["buf.proto"],
+ grpc_protos: ["proto/buf.proto"],
+ crate_name: "rust_grpcio",
+ source_stem: "buf",
+ }
+ `)
+}
diff --git a/rust/rust.go b/rust/rust.go
index 38caad3..83add87 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -65,7 +65,16 @@
AndroidMkSharedLibs []string
AndroidMkStaticLibs []string
- SubName string `blueprint:"mutated"`
+ ImageVariationPrefix string `blueprint:"mutated"`
+ VndkVersion string `blueprint:"mutated"`
+ SubName string `blueprint:"mutated"`
+
+ // Set by imageMutator
+ CoreVariantNeeded bool `blueprint:"mutated"`
+ ExtraVariants []string `blueprint:"mutated"`
+
+ // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
+ Min_sdk_version *string
PreventInstall bool
HideFromMake bool
@@ -76,11 +85,15 @@
android.DefaultableModuleBase
android.ApexModuleBase
+ VendorProperties cc.VendorProperties
+
Properties BaseProperties
hod android.HostOrDeviceSupported
multilib android.Multilib
+ makeLinkType string
+
compiler compiler
coverage *coverage
clippy *clippy
@@ -93,6 +106,42 @@
hideApexVariantFromMake bool
}
+func (mod *Module) Header() bool {
+ //TODO: If Rust libraries provide header variants, this needs to be updated.
+ return false
+}
+
+func (mod *Module) SetPreventInstall() {
+ mod.Properties.PreventInstall = true
+}
+
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (mod *Module) InVendor() bool {
+ return mod.Properties.ImageVariationPrefix == cc.VendorVariationPrefix
+}
+
+func (mod *Module) SetHideFromMake() {
+ mod.Properties.HideFromMake = true
+}
+
+func (mod *Module) SanitizePropDefined() bool {
+ return false
+}
+
+func (mod *Module) IsDependencyRoot() bool {
+ if mod.compiler != nil {
+ return mod.compiler.isDependencyRoot()
+ }
+ panic("IsDependencyRoot called on a non-compiler Rust module")
+}
+
+func (mod *Module) IsPrebuilt() bool {
+ if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ return true
+ }
+ return false
+}
+
func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -109,33 +158,6 @@
}
}
-var _ android.ImageInterface = (*Module)(nil)
-
-func (mod *Module) ImageMutatorBegin(ctx android.BaseModuleContext) {}
-
-func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
- return true
-}
-
-func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
- return mod.InRamdisk()
-}
-
-func (mod *Module) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool {
- return mod.InVendorRamdisk()
-}
-
-func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
- return mod.InRecovery()
-}
-
-func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
- return nil
-}
-
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-}
-
func (mod *Module) SelectedStl() string {
return ""
}
@@ -176,24 +198,18 @@
panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))
}
-func (mod *Module) OnlyInRamdisk() bool {
- return false
-}
-
-func (mod *Module) OnlyInVendorRamdisk() bool {
- return false
-}
-
-func (mod *Module) OnlyInRecovery() bool {
- return false
-}
-
func (mod *Module) UseSdk() bool {
return false
}
+// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64.
+// "product" and "vendor" variant modules return true for this function.
+// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true",
+// "soc_specific: true" and more vendor installed modules are included here.
+// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or
+// "product_specific: true" modules are included here.
func (mod *Module) UseVndk() bool {
- return false
+ return mod.Properties.VndkVersion != ""
}
func (mod *Module) MustUseVendorVariant() bool {
@@ -201,10 +217,23 @@
}
func (mod *Module) IsVndk() bool {
+ // TODO(b/165791368)
return false
}
-func (mod *Module) HasVendorVariant() bool {
+func (mod *Module) IsVndkExt() bool {
+ return false
+}
+
+func (c *Module) IsVndkPrivate() bool {
+ return false
+}
+
+func (c *Module) IsLlndk() bool {
+ return false
+}
+
+func (c *Module) IsLlndkPublic() bool {
return false
}
@@ -238,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
@@ -260,7 +290,8 @@
CrtEnd android.OptionalPath
// Paths to generated source files
- SrcDeps android.Paths
+ SrcDeps android.Paths
+ srcProviderFiles android.Paths
}
type RustLibraries []RustLibrary
@@ -287,6 +318,7 @@
SetDisabled()
stdLinkage(ctx *depsContext) RustLinkage
+ isDependencyRoot() bool
}
type exportedFlagsProducer interface {
@@ -376,6 +408,7 @@
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
+ &cc.VendorProperties{},
&BindgenProperties{},
&BaseCompilerProperties{},
&BinaryCompilerProperties{},
@@ -472,11 +505,6 @@
return mod.outputFile
}
-func (mod *Module) InRecovery() bool {
- // For now, Rust has no notion of the recovery image
- return false
-}
-
func (mod *Module) CoverageFiles() android.Paths {
if mod.compiler != nil {
if !mod.compiler.nativeCoverage() {
@@ -492,10 +520,21 @@
panic(fmt.Errorf("CoverageFiles called on non-library module: %q", mod.BaseModuleName()))
}
+func (mod *Module) installable(apexInfo android.ApexInfo) bool {
+ // The apex variant is not installable because it is included in the APEX and won't appear
+ // in the system partition as a standalone file.
+ if !apexInfo.IsForPlatform() {
+ return false
+ }
+
+ return mod.outputFile.Valid() && !mod.Properties.PreventInstall
+}
+
var _ cc.LinkableInterface = (*Module)(nil)
func (mod *Module) Init() android.Module {
mod.AddProperties(&mod.Properties)
+ mod.AddProperties(&mod.VendorProperties)
if mod.compiler != nil {
mod.AddProperties(mod.compiler.compilerProps()...)
@@ -615,6 +654,12 @@
}
toolchain := mod.toolchain(ctx)
+ mod.makeLinkType = cc.GetMakeLinkType(actx, mod)
+
+ // Differentiate static libraries that are vendor available
+ if mod.UseVndk() {
+ mod.Properties.SubName += ".vendor"
+ }
if !toolchain.Supported() {
// This toolchain's unsupported, there's nothing to do for this mod.
@@ -656,7 +701,9 @@
outputFile := mod.compiler.compile(ctx, flags, deps)
mod.outputFile = android.OptionalPathForPath(outputFile)
- if mod.outputFile.Valid() && !mod.Properties.PreventInstall {
+
+ apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if mod.installable(apexInfo) {
mod.compiler.install(ctx)
}
}
@@ -689,15 +736,15 @@
type dependencyTag struct {
blueprint.BaseDependencyTag
- name string
- library bool
- proc_macro bool
+ name string
+ library bool
+ procMacro bool
}
// InstallDepNeeded returns true for rlibs, dylibs, and proc macros so that they or their transitive
// dependencies (especially C/C++ shared libs) are installed as dependencies of a rust binary.
func (d dependencyTag) InstallDepNeeded() bool {
- return d.library || d.proc_macro
+ return d.library || d.procMacro
}
var _ android.InstallNeededDependencyTag = dependencyTag{}
@@ -706,7 +753,7 @@
customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
rlibDepTag = dependencyTag{name: "rlibTag", library: true}
dylibDepTag = dependencyTag{name: "dylib", library: true}
- procMacroDepTag = dependencyTag{name: "procMacro", proc_macro: true}
+ procMacroDepTag = dependencyTag{name: "procMacro", procMacro: true}
testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
sourceDepTag = dependencyTag{name: "source"}
)
@@ -906,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
@@ -924,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)
@@ -956,10 +1011,6 @@
deps := mod.deps(ctx)
var commonDepVariations []blueprint.Variation
- if !mod.Host() {
- commonDepVariations = append(commonDepVariations,
- blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
- }
stdLinkage := "dylib-std"
if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1076,10 +1127,36 @@
var _ android.ApexModule = (*Module)(nil)
+func (mod *Module) minSdkVersion() string {
+ return String(mod.Properties.Min_sdk_version)
+}
+
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+ minSdkVersion := mod.minSdkVersion()
+ if minSdkVersion == "apex_inherit" {
+ return nil
+ }
+ if minSdkVersion == "" {
+ return fmt.Errorf("min_sdk_version is not specificed")
+ }
+
+ // Not using nativeApiLevelFromUser because the context here is not
+ // necessarily a native context.
+ ver, err := android.ApiLevelFromUser(ctx, minSdkVersion)
+ if err != nil {
+ return err
+ }
+
+ if ver.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", ver)
+ }
return nil
}
+// Implements android.ApexModule
func (mod *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
depTag := ctx.OtherModuleDependencyTag(dep)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 4edc6cd..48c8d74 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -106,14 +106,16 @@
// useMockedFs setup a default mocked filesystem for the test environment.
func (tctx *testRustCtx) useMockedFs() {
tctx.fs = map[string][]byte{
- "foo.rs": nil,
- "foo.c": nil,
- "src/bar.rs": nil,
- "src/any.h": nil,
- "proto.proto": nil,
- "buf.proto": nil,
- "liby.so": nil,
- "libz.so": nil,
+ "foo.rs": nil,
+ "foo.c": nil,
+ "src/bar.rs": nil,
+ "src/any.h": nil,
+ "proto.proto": nil,
+ "proto/buf.proto": nil,
+ "buf.proto": nil,
+ "foo.proto": nil,
+ "liby.so": nil,
+ "libz.so": nil,
}
}
diff --git a/rust/testing.go b/rust/testing.go
index a8496d9..07f557a 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -78,6 +78,7 @@
nocrt: true,
system_shared_libs: [],
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+ min_sdk_version: "29",
}
cc_library {
name: "libprotobuf-cpp-full",
@@ -92,9 +93,11 @@
srcs: ["foo.rs"],
no_stdlibs: true,
host_supported: true,
+ vendor_available: true,
native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+ min_sdk_version: "29",
}
rust_library {
name: "libtest",
@@ -102,9 +105,11 @@
srcs: ["foo.rs"],
no_stdlibs: true,
host_supported: true,
+ vendor_available: true,
native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+ min_sdk_version: "29",
}
rust_library {
name: "libprotobuf",
@@ -148,8 +153,6 @@
ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory)
ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory)
ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory)
- ctx.RegisterModuleType("rust_grpcio", RustGrpcioFactory)
- ctx.RegisterModuleType("rust_grpcio_host", RustGrpcioHostFactory)
ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory)
ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 0c868ea..4b08ac3 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -54,6 +54,11 @@
PLATFORM_VERSION_ALL_CODENAMES="${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}"
PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
+# Get the list of missing <uses-library> modules and convert it to a JSON array
+# (quote module names, add comma separator and wrap in brackets).
+MISSING_USES_LIBRARIES="$(my_get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
+MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
+
# Logic from build/make/core/goma.mk
if [ "${USE_GOMA}" = true ]; then
if [ -n "${GOMA_DIR}" ]; then
@@ -77,10 +82,14 @@
# CrossHost: linux_bionic
# CrossHostArch: x86_64
# - Enable Bionic on host as ART needs prebuilts for it.
+# VendorVars.art_mdoule.source_build
+# - TODO(b/172480615): Change default to false when platform uses ART Module
+# prebuilts by default.
cat > ${SOONG_VARS}.new << EOF
{
"BuildNumberFile": "build_number.txt",
+ "Platform_version_name": "${PLATFORM_VERSION}",
"Platform_sdk_version": ${PLATFORM_SDK_VERSION},
"Platform_sdk_codename": "${PLATFORM_VERSION}",
"Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
@@ -94,7 +103,15 @@
"Allow_missing_dependencies": ${SOONG_ALLOW_MISSING_DEPENDENCIES:-false},
"Unbundled_build": ${TARGET_BUILD_UNBUNDLED:-false},
- "UseGoma": ${USE_GOMA}
+ "UseGoma": ${USE_GOMA},
+
+ "VendorVars": {
+ "art_module": {
+ "source_build": "${ENABLE_ART_SOURCE_BUILD:-true}"
+ }
+ },
+
+ "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index b8dd7aa..ea62af4 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -16,15 +16,17 @@
MODULES_SDK_AND_EXPORTS=(
art-module-sdk
art-module-test-exports
+ conscrypt-module-host-exports
conscrypt-module-sdk
conscrypt-module-test-exports
- conscrypt-module-host-exports
- runtime-module-sdk
- runtime-module-host-exports
- i18n-module-test-exports
+ i18n-module-host-exports
i18n-module-sdk
+ i18n-module-test-exports
platform-mainline-sdk
platform-mainline-test-exports
+ runtime-module-host-exports
+ runtime-module-sdk
+ tzdata-module-test-exports
)
# List of libraries installed on the platform that are needed for ART chroot
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index b6ed659..c27f098 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -30,6 +30,11 @@
PLATFORM_VERSION_ALL_CODENAMES=${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}
PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
+# Get the list of missing <uses-library> modules and convert it to a JSON array
+# (quote module names, add comma separator and wrap in brackets).
+MISSING_USES_LIBRARIES="$(get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
+MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
+
SOONG_OUT=${OUT_DIR}/soong
SOONG_NDK_OUT=${OUT_DIR}/soong/ndk
rm -rf ${SOONG_OUT}
@@ -49,7 +54,15 @@
"Safestack": false,
"Ndk_abis": true,
- "Exclude_draft_ndk_apis": true
+ "Exclude_draft_ndk_apis": true,
+
+ "VendorVars": {
+ "art_module": {
+ "source_build": "true"
+ }
+ },
+
+ "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
m --skip-make ${SOONG_OUT}/ndk.timestamp
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index c59732b..55d0fd1 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -121,7 +121,7 @@
# is empty. Set it to something low so that it will be overriden by the
# main manifest, but high enough that it doesn't cause implicit
# permissions grants.
- target_attr.value = '15'
+ target_attr.value = '16'
else:
target_attr.value = target_sdk_version
element.setAttributeNode(target_attr)
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index d6e7f26..3a0a25d 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -173,7 +173,7 @@
"""Tests inserting targetSdkVersion when minSdkVersion exists."""
manifest_input = self.manifest_tmpl % self.uses_sdk(min='27')
- expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
self.assertEqual(output, expected)
@@ -189,7 +189,7 @@
"""Tests inserting targetSdkVersion when minSdkVersion does not exist."""
manifest_input = self.manifest_tmpl % ''
- expected = self.manifest_tmpl % self.uses_sdk(min='28', target='15')
+ expected = self.manifest_tmpl % self.uses_sdk(min='28', target='16')
output = self.raise_min_sdk_version_test(manifest_input, '28', '29', True)
self.assertEqual(output, expected)
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/java_sdk_test.go b/sdk/java_sdk_test.go
index b44f66e..488afd8 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -44,6 +44,14 @@
"api/system-server-removed.txt": nil,
"build/soong/scripts/gen-java-current-api-files.sh": nil,
"docs/known_doctags": nil,
+ "100/public/api/myjavalib.txt": nil,
+ "100/public/api/myjavalib-removed.txt": nil,
+ "100/system/api/myjavalib.txt": nil,
+ "100/system/api/myjavalib-removed.txt": nil,
+ "100/module-lib/api/myjavalib.txt": nil,
+ "100/module-lib/api/myjavalib-removed.txt": nil,
+ "100/system-server/api/myjavalib.txt": nil,
+ "100/system-server/api/myjavalib-removed.txt": nil,
}
// for java_sdk_library tests
@@ -84,6 +92,10 @@
name: "framework",
sdk_version: "none",
}
+prebuilt_apis {
+ name: "sdk",
+ api_dirs: ["100"],
+}
` + bp
return testSdkWithFs(t, bp, fs)
@@ -460,6 +472,61 @@
)
}
+func TestSnapshotWithJavaBootLibrary(t *testing.T) {
+ result := testSdkWithJava(t, `
+ module_exports {
+ name: "myexports",
+ java_boot_libs: ["myjavalib"],
+ }
+
+ java_library {
+ name: "myjavalib",
+ srcs: ["Test.java"],
+ java_resources: ["resource.txt"],
+ // The aidl files should not be copied to the snapshot because a java_boot_libs member is not
+ // intended to be used for compiling Java, only for accessing the dex implementation jar.
+ aidl: {
+ export_include_dirs: ["aidl"],
+ },
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ }
+ `)
+
+ result.CheckSnapshot("myexports", "",
+ checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+ name: "myexports_myjavalib@current",
+ sdk_member_name: "myjavalib",
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+}
+
+java_import {
+ name: "myjavalib",
+ prefer: false,
+ visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
+ jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+ name: "myexports@current",
+ visibility: ["//visibility:public"],
+ java_boot_libs: ["myexports_myjavalib@current"],
+}
+
+`),
+ checkAllCopyRules(`
+.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
+`),
+ )
+}
+
func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
result := testSdkWithJava(t, `
module_exports {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 50b0886..f3d0750 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -311,7 +311,7 @@
DistFiles: android.MakeDefaultDistFiles(s.snapshotFile.Path()),
Include: "$(BUILD_PHONY_PACKAGE)",
ExtraFooters: []android.AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
// Allow the sdk to be built by simply passing its name on the command line.
fmt.Fprintln(w, ".PHONY:", s.Name())
fmt.Fprintln(w, s.Name()+":", s.snapshotFile.String())
diff --git a/sdk/testing.go b/sdk/testing.go
index 91aa879..1ac873b 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -107,11 +107,7 @@
ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
// from java package
- java.RegisterJavaBuildComponents(ctx)
- java.RegisterAppBuildComponents(ctx)
- java.RegisterSdkLibraryBuildComponents(ctx)
- java.RegisterStubsBuildComponents(ctx)
- java.RegisterSystemModulesBuildComponents(ctx)
+ java.RegisterRequiredBuildComponentsForTest(ctx)
// from cc package
cc.RegisterRequiredBuildComponentsForTest(ctx)
diff --git a/sdk/update.go b/sdk/update.go
index 377aaae..b5bc9f4 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -653,6 +653,9 @@
filesToZip android.Paths
zipsToMerge android.Paths
+ // The path to an empty file.
+ emptyFile android.WritablePath
+
prebuiltModules map[string]*bpModule
prebuiltOrder []*bpModule
@@ -703,6 +706,19 @@
s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
}
+func (s *snapshotBuilder) EmptyFile() android.Path {
+ if s.emptyFile == nil {
+ ctx := s.ctx
+ s.emptyFile = android.PathForModuleOut(ctx, "empty")
+ s.ctx.Build(pctx, android.BuildParams{
+ Rule: android.Touch,
+ Output: s.emptyFile,
+ })
+ }
+
+ return s.emptyFile
+}
+
func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
name := member.Name()
if s.prebuiltModules[name] != nil {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index f86e1fd..18749b5 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -205,14 +205,14 @@
func (s *ShBinary) generateAndroidBuildActions(ctx android.ModuleContext) {
s.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(s.properties.Src))
filename := proptools.String(s.properties.Filename)
- filename_from_src := proptools.Bool(s.properties.Filename_from_src)
+ filenameFromSrc := proptools.Bool(s.properties.Filename_from_src)
if filename == "" {
- if filename_from_src {
+ if filenameFromSrc {
filename = s.sourceFilePath.Base()
} else {
filename = ctx.ModuleName()
}
- } else if filename_from_src {
+ } else if filenameFromSrc {
ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
return
}
@@ -345,15 +345,8 @@
depTag := ctx.OtherModuleDependencyTag(dep)
switch depTag {
case shTestDataBinsTag, shTestDataDeviceBinsTag:
- if cc, isCc := dep.(*cc.Module); isCc {
- s.addToDataModules(ctx, cc.OutputFile().Path().Base(), cc.OutputFile().Path())
- return
- }
- property := "data_bins"
- if depTag == shTestDataDeviceBinsTag {
- property = "data_device_bins"
- }
- ctx.PropertyErrorf(property, "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
+ path := android.OutputFileForModule(ctx, dep, "")
+ s.addToDataModules(ctx, path.Base(), path)
case shTestDataLibsTag, shTestDataDeviceLibsTag:
if cc, isCc := dep.(*cc.Module); isCc {
// Copy to an intermediate output directory to append "lib[64]" to the path,
diff --git a/shared/Android.bp b/shared/Android.bp
index 07dfe11..2a4f56f 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -4,4 +4,7 @@
srcs: [
"paths.go",
],
+ deps: [
+ "soong-bazel",
+ ],
}
diff --git a/shared/paths.go b/shared/paths.go
index 24ba057..1b9ff60 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -18,23 +18,27 @@
import (
"path/filepath"
+
+ "android/soong/bazel"
)
+// A SharedPaths represents a list of paths that are shared between
+// soong_ui and soong.
+type SharedPaths interface {
+ // BazelMetricsDir returns the path where a set of bazel profile
+ // files are stored for later processed by the metrics pipeline.
+ BazelMetricsDir() string
+}
+
// Given the out directory, returns the root of the temp directory (to be cleared at the start of each execution of Soong)
func TempDirForOutDir(outDir string) (tempPath string) {
return filepath.Join(outDir, ".temp")
}
-// BazelMetricsDir returns the path where a set of bazel profile
-// files are stored for later processed by the metrics pipeline.
-func BazelMetricsDir(outDir string) string {
- return filepath.Join(outDir, "bazel_metrics")
-}
-
// BazelMetricsFilename returns the bazel profile filename based
// on the action name. This is to help to store a set of bazel
// profiles since bazel may execute multiple times during a single
// build.
-func BazelMetricsFilename(outDir, actionName string) string {
- return filepath.Join(BazelMetricsDir(outDir), actionName+"_bazel_profile.gz")
+func BazelMetricsFilename(s SharedPaths, actionName bazel.RunName) string {
+ return filepath.Join(s.BazelMetricsDir(), actionName.String()+"_bazel_profile.gz")
}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 6a53414..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")
@@ -307,6 +330,7 @@
// Actual implementation libraries are created on LoadHookMutator
fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
fmt.Fprintf(w, "LOCAL_MODULE := %s\n", m.Name())
+ data.Entries.WriteLicenseVariables(w)
fmt.Fprintf(w, "LOCAL_MODULE_CLASS := FAKE\n")
fmt.Fprintf(w, "LOCAL_MODULE_TAGS := optional\n")
fmt.Fprintf(w, "include $(BUILD_SYSTEM)/base_rules.mk\n\n")
@@ -322,6 +346,9 @@
}}
}
+var _ android.ApexModule = (*syspropLibrary)(nil)
+
+// Implements android.ApexModule
func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
@@ -389,31 +416,6 @@
ctx.PropertyErrorf("srcs", "sysprop_library must specify srcs")
}
- missing_api := 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)
- missing_api = true
- }
- }
-
- if missing_api {
- 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/third_party/zip/android_test.go b/third_party/zip/android_test.go
index 5154a17..9932c1b 100644
--- a/third_party/zip/android_test.go
+++ b/third_party/zip/android_test.go
@@ -74,3 +74,69 @@
}
}
}
+
+func TestCopyFromZip64(t *testing.T) {
+ if testing.Short() {
+ t.Skip("slow test; skipping")
+ }
+
+ const size = uint32max + 1
+ fromZipBytes := &bytes.Buffer{}
+ fromZip := NewWriter(fromZipBytes)
+ w, err := fromZip.CreateHeaderAndroid(&FileHeader{
+ Name: "large",
+ Method: Store,
+ UncompressedSize64: size,
+ CompressedSize64: size,
+ })
+ if err != nil {
+ t.Fatalf("Create: %v", err)
+ }
+ _, err = w.Write(make([]byte, size))
+ if err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+ err = fromZip.Close()
+ if err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+ fromZip = nil
+
+ fromZipReader, err := NewReader(bytes.NewReader(fromZipBytes.Bytes()), int64(fromZipBytes.Len()))
+ if err != nil {
+ t.Fatalf("NewReader: %v", err)
+ }
+
+ toZipBytes := &bytes.Buffer{}
+ toZip := NewWriter(toZipBytes)
+ err = toZip.CopyFrom(fromZipReader.File[0], fromZipReader.File[0].Name)
+ if err != nil {
+ t.Fatalf("CopyFrom: %v", err)
+ }
+
+ err = toZip.Close()
+ if err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+
+ // Save some memory
+ fromZipReader = nil
+ fromZipBytes.Reset()
+
+ toZipReader, err := NewReader(bytes.NewReader(toZipBytes.Bytes()), int64(toZipBytes.Len()))
+ if err != nil {
+ t.Fatalf("NewReader: %v", err)
+ }
+
+ if len(toZipReader.File) != 1 {
+ t.Fatalf("Expected 1 file in toZip, got %d", len(toZipReader.File))
+ }
+
+ if g, w := toZipReader.File[0].CompressedSize64, uint64(size); g != w {
+ t.Errorf("Expected CompressedSize64 %d, got %d", w, g)
+ }
+
+ if g, w := toZipReader.File[0].UncompressedSize64, uint64(size); g != w {
+ t.Errorf("Expected UnompressedSize64 %d, got %d", w, g)
+ }
+}
diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go
index 4c5eb78..8dd986e 100644
--- a/third_party/zip/writer.go
+++ b/third_party/zip/writer.go
@@ -276,9 +276,6 @@
} else {
b.uint32(h.CRC32)
- if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
- panic("skipping writing the data descriptor for a 64-bit value is not yet supported")
- }
compressedSize := uint32(h.CompressedSize64)
if compressedSize == 0 {
compressedSize = h.CompressedSize
@@ -289,6 +286,21 @@
uncompressedSize = h.UncompressedSize
}
+ if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
+ // Sizes don't fit in a 32-bit field, put them in a zip64 extra instead.
+ compressedSize = uint32max
+ uncompressedSize = uint32max
+
+ // append a zip64 extra block to Extra
+ var buf [20]byte // 2x uint16 + 2x uint64
+ eb := writeBuf(buf[:])
+ eb.uint16(zip64ExtraId)
+ eb.uint16(16) // size = 2x uint64
+ eb.uint64(h.UncompressedSize64)
+ eb.uint64(h.CompressedSize64)
+ h.Extra = append(h.Extra, buf[:]...)
+ }
+
b.uint32(compressedSize)
b.uint32(uncompressedSize)
}
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
index 2d36f67..81ce939 100644
--- a/ui/build/bazel.go
+++ b/ui/build/bazel.go
@@ -15,15 +15,41 @@
package build
import (
+ "bytes"
+ "fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
+ "android/soong/bazel"
"android/soong/shared"
"android/soong/ui/metrics"
)
+func getBazelInfo(ctx Context, config Config, bazelExecutable string, bazelEnv map[string]string, query string) string {
+ infoCmd := Command(ctx, config, "bazel", bazelExecutable)
+
+ if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
+ infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...)
+ }
+
+ // Obtain the output directory path in the execution root.
+ infoCmd.Args = append(infoCmd.Args,
+ "info",
+ query,
+ )
+
+ for k, v := range bazelEnv {
+ infoCmd.Environment.Set(k, v)
+ }
+
+ infoCmd.Dir = filepath.Join(config.OutDir(), "..")
+
+ queryResult := strings.TrimSpace(string(infoCmd.OutputOrFatal()))
+ return queryResult
+}
+
// Main entry point to construct the Bazel build command line, environment
// variables and post-processing steps (e.g. converge output directories)
func runBazel(ctx Context, config Config) {
@@ -43,14 +69,21 @@
// Environment variables are the primary mechanism to pass information from
// soong_ui configuration or context to Bazel.
- //
+ bazelEnv := make(map[string]string)
+
// Use *_NINJA variables to pass the root-relative path of the combined,
// kati-generated, soong-generated, and packaging Ninja files to Bazel.
// Bazel reads these from the lunch() repository rule.
- config.environ.Set("COMBINED_NINJA", config.CombinedNinjaFile())
- config.environ.Set("KATI_NINJA", config.KatiBuildNinjaFile())
- config.environ.Set("PACKAGE_NINJA", config.KatiPackageNinjaFile())
- config.environ.Set("SOONG_NINJA", config.SoongNinjaFile())
+ bazelEnv["COMBINED_NINJA"] = config.CombinedNinjaFile()
+ bazelEnv["KATI_NINJA"] = config.KatiBuildNinjaFile()
+ bazelEnv["PACKAGE_NINJA"] = config.KatiPackageNinjaFile()
+ bazelEnv["SOONG_NINJA"] = config.SoongNinjaFile()
+
+ // NOTE: When Bazel is used, config.DistDir() is rigged to return a fake distdir under config.OutDir()
+ // This is to ensure that Bazel can actually write there. See config.go for more details.
+ bazelEnv["DIST_DIR"] = config.DistDir()
+
+ bazelEnv["SHELL"] = "/bin/bash"
// `tools/bazel` is the default entry point for executing Bazel in the AOSP
// source tree.
@@ -65,27 +98,47 @@
}
// Start constructing the `build` command.
- actionName := "build"
+ actionName := bazel.BazelNinjaExecRunName
cmd.Args = append(cmd.Args,
- actionName,
+ "build",
// Use output_groups to select the set of outputs to produce from a
// ninja_build target.
"--output_groups="+outputGroups,
// Generate a performance profile
- "--profile="+filepath.Join(shared.BazelMetricsFilename(config.OutDir(), actionName)),
+ "--profile="+filepath.Join(shared.BazelMetricsFilename(config, actionName)),
"--slim_profile=true",
)
- // Append custom build flags to the Bazel command. Changes to these flags
- // may invalidate Bazel's analysis cache.
- if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
- cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...)
- }
+ if config.UseRBE() {
+ for _, envVar := range []string{
+ // RBE client
+ "RBE_compare",
+ "RBE_exec_strategy",
+ "RBE_invocation_id",
+ "RBE_log_dir",
+ "RBE_platform",
+ "RBE_remote_accept_cache",
+ "RBE_remote_update_cache",
+ "RBE_server_address",
+ // TODO: remove old FLAG_ variables.
+ "FLAG_compare",
+ "FLAG_exec_root",
+ "FLAG_exec_strategy",
+ "FLAG_invocation_id",
+ "FLAG_log_dir",
+ "FLAG_platform",
+ "FLAG_remote_accept_cache",
+ "FLAG_remote_update_cache",
+ "FLAG_server_address",
+ } {
+ cmd.Args = append(cmd.Args,
+ "--action_env="+envVar)
+ }
- // Append the label of the default ninja_build target.
- cmd.Args = append(cmd.Args,
- "//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(),
- )
+ // We need to calculate --RBE_exec_root ourselves
+ ctx.Println("Getting Bazel execution_root...")
+ cmd.Args = append(cmd.Args, "--action_env=RBE_exec_root="+getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "execution_root"))
+ }
// Ensure that the PATH environment variable value used in the action
// environment is the restricted set computed from soong_ui, and not a
@@ -95,15 +148,36 @@
cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
}
- cmd.Environment.Set("DIST_DIR", config.DistDir())
- cmd.Environment.Set("SHELL", "/bin/bash")
+ // Append custom build flags to the Bazel command. Changes to these flags
+ // may invalidate Bazel's analysis cache.
+ // These should be appended as the final args, so that they take precedence.
+ if extraBuildArgs, ok := cmd.Environment.Get("BAZEL_BUILD_ARGS"); ok {
+ cmd.Args = append(cmd.Args, strings.Fields(extraBuildArgs)...)
+ }
- // Print the full command line for debugging purposes.
- ctx.Println(cmd.Cmd)
+ // Append the label of the default ninja_build target.
+ cmd.Args = append(cmd.Args,
+ "//:"+config.TargetProduct()+"-"+config.TargetBuildVariant(),
+ )
// Execute the command at the root of the directory.
cmd.Dir = filepath.Join(config.OutDir(), "..")
- ctx.Status.Status("Starting Bazel..")
+
+ for k, v := range bazelEnv {
+ cmd.Environment.Set(k, v)
+ }
+
+ // Make a human-readable version of the bazelEnv map
+ bazelEnvStringBuffer := new(bytes.Buffer)
+ for k, v := range bazelEnv {
+ fmt.Fprintf(bazelEnvStringBuffer, "%s=%s ", k, v)
+ }
+
+ // Print the implicit command line
+ ctx.Println("Bazel implicit command line: " + strings.Join(cmd.Environment.Environ(), " ") + " " + cmd.Cmd.String() + "\n")
+
+ // Print the explicit command line too
+ ctx.Println("Bazel explicit command line: " + bazelEnvStringBuffer.String() + cmd.Cmd.String() + "\n")
// Execute the build command.
cmd.RunAndStreamOrFatal()
@@ -113,61 +187,67 @@
// Ensure that the $OUT_DIR contains the expected set of files by symlinking
// the files from the execution root's output direction into $OUT_DIR.
- // Obtain the Bazel output directory for ninja_build.
- infoCmd := Command(ctx, config, "bazel", bazelExecutable)
-
- if extraStartupArgs, ok := infoCmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
- infoCmd.Args = append(infoCmd.Args, strings.Fields(extraStartupArgs)...)
- }
-
- // Obtain the output directory path in the execution root.
- infoCmd.Args = append(infoCmd.Args,
- "info",
- "output_path",
- )
-
- infoCmd.Environment.Set("DIST_DIR", config.DistDir())
- infoCmd.Environment.Set("SHELL", "/bin/bash")
- infoCmd.Dir = filepath.Join(config.OutDir(), "..")
- ctx.Status.Status("Getting Bazel Info..")
- outputBasePath := string(infoCmd.OutputOrFatal())
+ ctx.Println("Getting Bazel output_path...")
+ outputBasePath := getBazelInfo(ctx, config, bazelExecutable, bazelEnv, "output_path")
// TODO: Don't hardcode out/ as the bazel output directory. This is
// currently hardcoded as ninja_build.output_root.
bazelNinjaBuildOutputRoot := filepath.Join(outputBasePath, "..", "out")
- symlinkOutdir(ctx, config, bazelNinjaBuildOutputRoot, ".")
+ ctx.Println("Populating output directory...")
+ populateOutdir(ctx, config, bazelNinjaBuildOutputRoot, ".")
}
// For all files F recursively under rootPath/relativePath, creates symlinks
// such that OutDir/F resolves to rootPath/F via symlinks.
-func symlinkOutdir(ctx Context, config Config, rootPath string, relativePath string) {
+// NOTE: For distdir paths we rename files instead of creating symlinks, so that the distdir is independent.
+func populateOutdir(ctx Context, config Config, rootPath string, relativePath string) {
destDir := filepath.Join(rootPath, relativePath)
os.MkdirAll(destDir, 0755)
files, err := ioutil.ReadDir(destDir)
if err != nil {
ctx.Fatal(err)
}
+
for _, f := range files {
+ // The original Bazel file path
destPath := filepath.Join(destDir, f.Name())
+
+ // The desired Soong file path
srcPath := filepath.Join(config.OutDir(), relativePath, f.Name())
- if statResult, err := os.Stat(srcPath); err == nil {
- if statResult.Mode().IsDir() && f.IsDir() {
- // Directory under OutDir already exists, so recurse on its contents.
- symlinkOutdir(ctx, config, rootPath, filepath.Join(relativePath, f.Name()))
- } else if !statResult.Mode().IsDir() && !f.IsDir() {
- // File exists both in source and destination, and it's not a directory
- // in either location. Do nothing.
- // This can arise for files which are generated under OutDir outside of
- // soong_build, such as .bootstrap files.
+
+ destLstatResult, destLstatErr := os.Lstat(destPath)
+ if destLstatErr != nil {
+ ctx.Fatalf("Unable to Lstat dest %s: %s", destPath, destLstatErr)
+ }
+
+ srcLstatResult, srcLstatErr := os.Lstat(srcPath)
+
+ if srcLstatErr == nil {
+ if srcLstatResult.IsDir() && destLstatResult.IsDir() {
+ // src and dest are both existing dirs - recurse on the dest dir contents...
+ populateOutdir(ctx, config, rootPath, filepath.Join(relativePath, f.Name()))
} else {
- // File is a directory in one location but not the other. Raise an error.
- ctx.Fatalf("Could not link %s to %s due to conflict", srcPath, destPath)
+ // Ignore other pre-existing src files (could be pre-existing files, directories, symlinks, ...)
+ // This can arise for files which are generated under OutDir outside of soong_build, such as .bootstrap files.
+ // FIXME: This might cause a problem later e.g. if a symlink in the build graph changes...
}
- } else if os.IsNotExist(err) {
- // Create symlink srcPath -> fullDestPath.
- os.Symlink(destPath, srcPath)
} else {
- ctx.Fatalf("Unable to stat %s: %s", srcPath, err)
+ if !os.IsNotExist(srcLstatErr) {
+ ctx.Fatalf("Unable to Lstat src %s: %s", srcPath, srcLstatErr)
+ }
+
+ if strings.Contains(destDir, config.DistDir()) {
+ // We need to make a "real" file/dir instead of making a symlink (because the distdir can't have symlinks)
+ // Rename instead of copy in order to save disk space.
+ if err := os.Rename(destPath, srcPath); err != nil {
+ ctx.Fatalf("Unable to rename %s -> %s due to error %s", srcPath, destPath, err)
+ }
+ } else {
+ // src does not exist, so try to create a src -> dest symlink (i.e. a Soong path -> Bazel path symlink)
+ if err := os.Symlink(destPath, srcPath); err != nil {
+ ctx.Fatalf("Unable to create symlink %s -> %s due to error %s", srcPath, destPath, err)
+ }
+ }
}
}
}
diff --git a/ui/build/build.go b/ui/build/build.go
index e8f0fc4..926da31 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -302,7 +302,7 @@
}
subDir := filepath.Join(subDirs...)
- destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+ destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
@@ -321,7 +321,7 @@
}
subDir := filepath.Join(subDirs...)
- destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+ destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 06f6c63..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"),
@@ -124,7 +125,6 @@
productOut("ramdisk"),
productOut("debug_ramdisk"),
productOut("vendor-ramdisk"),
- productOut("vendor-ramdisk-debug.cpio.gz"),
productOut("vendor_debug_ramdisk"),
productOut("test_harness_ramdisk"),
productOut("recovery"),
diff --git a/ui/build/config.go b/ui/build/config.go
index c9911f3..15da1bc 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -56,6 +56,7 @@
katiSuffix string
targetDevice string
targetDeviceDir string
+ fullBuild bool
// Autodetected
totalRAM uint64
@@ -65,6 +66,12 @@
brokenNinjaEnvVars []string
pathReplaced bool
+
+ useBazel bool
+
+ // During Bazel execution, Bazel cannot write outside OUT_DIR.
+ // So if DIST_DIR is set to an external dir (outside of OUT_DIR), we need to rig it temporarily and then migrate files at the end of the build.
+ riggedDistDirForBazel string
}
const srcDirFileCheck = "build/soong/root.bp"
@@ -221,7 +228,7 @@
ctx.Fatalln("Directory names containing spaces are not supported")
}
- if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') {
+ if distDir := ret.RealDistDir(); strings.ContainsRune(distDir, ' ') {
ctx.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
ctx.Println()
ctx.Printf("%q\n", distDir)
@@ -275,16 +282,26 @@
}
}
- bpd := shared.BazelMetricsDir(ret.OutDir())
+ bpd := ret.BazelMetricsDir()
if err := os.RemoveAll(bpd); err != nil {
ctx.Fatalf("Unable to remove bazel profile directory %q: %v", bpd, err)
}
+
+ ret.useBazel = ret.environ.IsEnvTrue("USE_BAZEL")
+
if ret.UseBazel() {
if err := os.MkdirAll(bpd, 0777); err != nil {
ctx.Fatalf("Failed to create bazel profile directory %q: %v", bpd, err)
}
}
+ if ret.UseBazel() {
+ ret.riggedDistDirForBazel = filepath.Join(ret.OutDir(), "dist")
+ } else {
+ // Not rigged
+ ret.riggedDistDirForBazel = ret.distDir
+ }
+
c := Config{ret}
storeConfigMetrics(ctx, c)
return c
@@ -601,19 +618,19 @@
// For LANG and LC_*, only preserve the evaluated version of
// LC_MESSAGES
- user_lang := ""
+ userLang := ""
if lc_all, ok := c.environ.Get("LC_ALL"); ok {
- user_lang = lc_all
+ userLang = lc_all
} else if lc_messages, ok := c.environ.Get("LC_MESSAGES"); ok {
- user_lang = lc_messages
+ userLang = lc_messages
} else if lang, ok := c.environ.Get("LANG"); ok {
- user_lang = lang
+ userLang = lang
}
c.environ.UnsetWithPrefix("LC_")
- if user_lang != "" {
- c.environ.Set("LC_MESSAGES", user_lang)
+ if userLang != "" {
+ c.environ.Set("LC_MESSAGES", userLang)
}
// The for LANG, use C.UTF-8 if it exists (Debian currently, proposed
@@ -697,6 +714,14 @@
}
func (c *configImpl) DistDir() string {
+ if c.UseBazel() {
+ return c.riggedDistDirForBazel
+ } else {
+ return c.distDir
+ }
+}
+
+func (c *configImpl) RealDistDir() string {
return c.distDir
}
@@ -767,6 +792,14 @@
c.targetDevice = device
}
+func (c *configImpl) FullBuild() bool {
+ return c.fullBuild
+}
+
+func (c *configImpl) SetFullBuild(fullBuild bool) {
+ c.fullBuild = fullBuild
+}
+
func (c *configImpl) TargetBuildVariant() string {
if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
return v
@@ -863,13 +896,7 @@
}
func (c *configImpl) UseBazel() bool {
- if v, ok := c.environ.Get("USE_BAZEL"); ok {
- v = strings.TrimSpace(v)
- if v != "" && v != "false" {
- return true
- }
- }
- return false
+ return c.useBazel
}
func (c *configImpl) StartRBE() bool {
@@ -886,14 +913,14 @@
return true
}
-func (c *configImpl) logDir() string {
+func (c *configImpl) rbeLogDir() string {
for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} {
if v, ok := c.environ.Get(f); ok {
return v
}
}
if c.Dist() {
- return filepath.Join(c.DistDir(), "logs")
+ return c.LogsDir()
}
return c.OutDir()
}
@@ -904,7 +931,7 @@
return v
}
}
- return c.logDir()
+ return c.rbeLogDir()
}
func (c *configImpl) rbeLogPath() string {
@@ -913,7 +940,7 @@
return v
}
}
- return fmt.Sprintf("text://%v/reproxy_log.txt", c.logDir())
+ return fmt.Sprintf("text://%v/reproxy_log.txt", c.rbeLogDir())
}
func (c *configImpl) rbeExecRoot() string {
@@ -1121,3 +1148,21 @@
}
return ""
}
+
+// LogsDir returns the logs directory where build log and metrics
+// files are located. By default, the logs directory is the out
+// directory. If the argument dist is specified, the logs directory
+// is <dist_dir>/logs.
+func (c *configImpl) LogsDir() string {
+ if c.Dist() {
+ // Always write logs to the real dist dir, even if Bazel is using a rigged dist dir for other files
+ return filepath.Join(c.RealDistDir(), "logs")
+ }
+ return c.OutDir()
+}
+
+// BazelMetricsDir returns the <logs dir>/bazel_metrics directory
+// where the bazel profiles are located.
+func (c *configImpl) BazelMetricsDir() string {
+ return filepath.Join(c.LogsDir(), "bazel_metrics")
+}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 999af07..be6f00a 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -214,6 +214,9 @@
// So that later Kati runs can find BoardConfig.mk faster
"TARGET_DEVICE_DIR",
+ // To decide whether to skip the old installed cleanup step.
+ "FULL_BUILD",
+
// Whether --werror_overriding_commands will work
"BUILD_BROKEN_DUP_RULES",
@@ -254,7 +257,7 @@
"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
}, exportEnvVars...), BannerVars...)
- make_vars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
+ makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
if err != nil {
ctx.Fatalln("Error dumping make vars:", err)
}
@@ -262,24 +265,25 @@
env := config.Environment()
// Print the banner like make does
if !env.IsEnvTrue("ANDROID_QUIET_BUILD") {
- fmt.Fprintln(ctx.Writer, Banner(make_vars))
+ fmt.Fprintln(ctx.Writer, Banner(makeVars))
}
// Populate the environment
for _, name := range exportEnvVars {
- if make_vars[name] == "" {
+ if makeVars[name] == "" {
env.Unset(name)
} else {
- env.Set(name, make_vars[name])
+ env.Set(name, makeVars[name])
}
}
- config.SetKatiArgs(strings.Fields(make_vars["KATI_GOALS"]))
- config.SetNinjaArgs(strings.Fields(make_vars["NINJA_GOALS"]))
- config.SetTargetDevice(make_vars["TARGET_DEVICE"])
- config.SetTargetDeviceDir(make_vars["TARGET_DEVICE_DIR"])
+ config.SetKatiArgs(strings.Fields(makeVars["KATI_GOALS"]))
+ config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
+ config.SetTargetDevice(makeVars["TARGET_DEVICE"])
+ config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
+ config.SetFullBuild(makeVars["FULL_BUILD"] == "true")
- config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
- config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
- config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(make_vars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
+ config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
+ config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
+ config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
}
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 06ec646..668b20e 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -229,7 +229,11 @@
// Cleanup steps.
cleanCopyHeaders(ctx, config)
- cleanOldInstalledFiles(ctx, config)
+ // Skip the old installed file cleanup step for few non-full build goals as we don't create
+ // an installed file list for them.
+ if config.FullBuild() {
+ cleanOldInstalledFiles(ctx, config)
+ }
}
// Clean out obsolete header files on the disk that were *not copied* during the
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index ffd1ab9..7799766 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -139,15 +139,6 @@
// b/147197813 - used by art-check-debug-apex-gen
"EMMA_INSTRUMENT_FRAMEWORK",
- // Goma -- gomacc may not need all of these
- "GOMA_DIR",
- "GOMA_DISABLED",
- "GOMA_FAIL_FAST",
- "GOMA_FALLBACK",
- "GOMA_GCE_SERVICE_ACCOUNT",
- "GOMA_TMP_DIR",
- "GOMA_USE_LOCAL",
-
// RBE client
"RBE_compare",
"RBE_exec_root",
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 64f3d4c..d9c33f6 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -74,7 +74,7 @@
func getRBEVars(ctx Context, config Config) map[string]string {
vars := map[string]string{
"RBE_log_path": config.rbeLogPath(),
- "RBE_log_dir": config.logDir(),
+ "RBE_log_dir": config.rbeLogDir(),
"RBE_re_proxy": config.rbeReproxy(),
"RBE_exec_root": config.rbeExecRoot(),
"RBE_output_dir": config.rbeStatsOutputDir(),
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 08c2ee1..6a93672 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -156,6 +156,7 @@
cmd.Environment.Set("BAZEL_HOME", filepath.Join(config.BazelOutDir(), "bazelhome"))
cmd.Environment.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
cmd.Environment.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
+ cmd.Environment.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
cmd.Environment.Set("SOONG_SANDBOX_SOONG_BUILD", "true")
cmd.Sandbox = soongSandbox
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 3164680..a910c06 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -50,10 +50,10 @@
// Get a list of leaf nodes in the dependency graph from ninja
executable := config.PrebuiltBuildTool("ninja")
- common_args := []string{}
- common_args = append(common_args, config.NinjaArgs()...)
- common_args = append(common_args, "-f", config.CombinedNinjaFile())
- args := append(common_args, "-t", "targets", "rule")
+ commonArgs := []string{}
+ commonArgs = append(commonArgs, config.NinjaArgs()...)
+ commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile())
+ args := append(commonArgs, "-t", "targets", "rule")
cmd := Command(ctx, config, "ninja", executable, args...)
stdout, err := cmd.StdoutPipe()
@@ -68,6 +68,7 @@
miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
modulePathsDir := filepath.Join(outDir, ".module_paths")
variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
+
// dexpreopt.config is an input to the soong_docs action, which runs the
// soong_build primary builder. However, this file is created from $(shell)
// invocation at Kati parse time, so it's not an explicit output of any
@@ -75,6 +76,9 @@
// treated as an source file.
dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config")
+ // out/build_date.txt is considered a "source file"
+ buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt")
+
danglingRules := make(map[string]bool)
scanner := bufio.NewScanner(stdout)
@@ -88,7 +92,8 @@
strings.HasPrefix(line, miniBootstrapDir) ||
strings.HasPrefix(line, modulePathsDir) ||
line == variablesFilePath ||
- line == dexpreoptConfigFilePath {
+ line == dexpreoptConfigFilePath ||
+ line == buildDatetimeFilePath {
// Leaf node is in one of Soong's bootstrap directories, which do not have
// full build rules in the primary build.ninja file.
continue
@@ -109,28 +114,28 @@
title := "Dependencies in out found with no rule to create them:"
fmt.Fprintln(sb, title)
- report_lines := 1
+ reportLines := 1
for i, dep := range danglingRulesList {
- if report_lines > 20 {
+ if reportLines > 20 {
fmt.Fprintf(sb, " ... and %d more\n", len(danglingRulesList)-i)
break
}
// It's helpful to see the reverse dependencies. ninja -t query is the
// best tool we got for that. Its output starts with the dependency
// itself.
- query_cmd := Command(ctx, config, "ninja", executable,
- append(common_args, "-t", "query", dep)...)
- query_stdout, err := query_cmd.StdoutPipe()
+ queryCmd := Command(ctx, config, "ninja", executable,
+ append(commonArgs, "-t", "query", dep)...)
+ queryStdout, err := queryCmd.StdoutPipe()
if err != nil {
ctx.Fatal(err)
}
- query_cmd.StartOrFatal()
- scanner := bufio.NewScanner(query_stdout)
+ queryCmd.StartOrFatal()
+ scanner := bufio.NewScanner(queryStdout)
for scanner.Scan() {
- report_lines++
+ reportLines++
fmt.Fprintln(sb, " ", scanner.Text())
}
- query_cmd.WaitOrFatal()
+ queryCmd.WaitOrFatal()
}
ts.FinishAction(status.ActionResult{
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 4f30136..55ca800 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -40,13 +40,42 @@
tmpDir = ioutil.TempDir
)
+// pruneMetricsFiles iterates the list of paths, checking if a path exist.
+// If a path is a file, it is added to the return list. If the path is a
+// directory, a recursive call is made to add the children files of the
+// path.
+func pruneMetricsFiles(paths []string) []string {
+ var metricsFiles []string
+ for _, p := range paths {
+ fi, err := os.Stat(p)
+ // Some paths passed may not exist. For example, build errors protobuf
+ // file may not exist since the build was successful.
+ if err != nil {
+ continue
+ }
+
+ if fi.IsDir() {
+ if l, err := ioutil.ReadDir(p); err == nil {
+ files := make([]string, 0, len(l))
+ for _, fi := range l {
+ files = append(files, filepath.Join(p, fi.Name()))
+ }
+ metricsFiles = append(metricsFiles, pruneMetricsFiles(files)...)
+ }
+ } else {
+ metricsFiles = append(metricsFiles, p)
+ }
+ }
+ return metricsFiles
+}
+
// UploadMetrics uploads a set of metrics files to a server for analysis. An
// uploader full path is specified in ANDROID_ENABLE_METRICS_UPLOAD environment
// variable in order to upload the set of metrics files. The metrics files are
// first copied to a temporary directory and the uploader is then executed in
// the background to allow the user/system to continue working. Soong communicates
// to the uploader through the upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, files ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
@@ -56,15 +85,8 @@
return
}
- // Some files passed in to this function may not exist. For example,
- // build errors protobuf file may not exist since the build was successful.
- var metricsFiles []string
- for _, f := range files {
- if _, err := os.Stat(f); err == nil {
- metricsFiles = append(metricsFiles, f)
- }
- }
-
+ // Several of the files might be directories.
+ metricsFiles := pruneMetricsFiles(paths)
if len(metricsFiles) == 0 {
return
}
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 768b031..b740c11 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -19,6 +19,8 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
+ "sort"
"strconv"
"strings"
"testing"
@@ -27,6 +29,49 @@
"android/soong/ui/logger"
)
+func TestPruneMetricsFiles(t *testing.T) {
+ rootDir := t.TempDir()
+
+ dirs := []string{
+ filepath.Join(rootDir, "d1"),
+ filepath.Join(rootDir, "d1", "d2"),
+ filepath.Join(rootDir, "d1", "d2", "d3"),
+ }
+
+ files := []string{
+ filepath.Join(rootDir, "d1", "f1"),
+ filepath.Join(rootDir, "d1", "d2", "f1"),
+ filepath.Join(rootDir, "d1", "d2", "d3", "f1"),
+ }
+
+ for _, d := range dirs {
+ if err := os.MkdirAll(d, 0777); err != nil {
+ t.Fatalf("got %v, expecting nil error for making directory %q", err, d)
+ }
+ }
+
+ for _, f := range files {
+ if err := ioutil.WriteFile(f, []byte{}, 0777); err != nil {
+ t.Fatalf("got %v, expecting nil error on writing file %q", err, f)
+ }
+ }
+
+ want := []string{
+ filepath.Join(rootDir, "d1", "f1"),
+ filepath.Join(rootDir, "d1", "d2", "f1"),
+ filepath.Join(rootDir, "d1", "d2", "d3", "f1"),
+ }
+
+ got := pruneMetricsFiles([]string{rootDir})
+
+ sort.Strings(got)
+ sort.Strings(want)
+
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("got %q, want %q after pruning metrics files", got, want)
+ }
+}
+
func TestUploadMetrics(t *testing.T) {
ctx := testContext()
tests := []struct {
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index 6becfd1..87c1b84 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -14,6 +14,17 @@
package metrics
+// This file contains the functionality to represent a build event in respect
+// to the metric system. A build event corresponds to a block of scoped code
+// that contains a "Begin()" and immediately followed by "defer End()" trace.
+// When defined, the duration of the scoped code is measure along with other
+// performance measurements such as memory.
+//
+// As explained in the metrics package, the metrics system is a stacked based
+// system since the collected metrics is considered to be topline metrics.
+// The steps of the build system in the UI layer is sequential. Hence, the
+// functionality defined below follows the stack data structure operations.
+
import (
"os"
"syscall"
@@ -21,54 +32,97 @@
"android/soong/ui/metrics/metrics_proto"
"android/soong/ui/tracer"
+
"github.com/golang/protobuf/proto"
)
-// for testing purpose only
-var _now = now
-
-type event struct {
- desc string
- name string
-
- // the time that the event started to occur.
- start time.Time
-
- // The list of process resource information that was executed
- procResInfo []*soong_metrics_proto.ProcessResourceInfo
-}
-
-type EventTracer interface {
- Begin(name, desc string, thread tracer.Thread)
- End(thread tracer.Thread) soong_metrics_proto.PerfInfo
- AddProcResInfo(string, *os.ProcessState)
-}
-
-type eventTracerImpl struct {
- activeEvents []event
-}
-
-var _ EventTracer = &eventTracerImpl{}
-
-func now() time.Time {
+// _now wraps the time.Now() function. _now is declared for unit testing purpose.
+var _now = func() time.Time {
return time.Now()
}
-// AddProcResInfo adds information on an executed process such as max resident set memory
-// and the number of voluntary context switches.
-func (t *eventTracerImpl) AddProcResInfo(name string, state *os.ProcessState) {
- if len(t.activeEvents) < 1 {
+// event holds the performance metrics data of a single build event.
+type event struct {
+ // The event name (mostly used for grouping a set of events)
+ name string
+
+ // The description of the event (used to uniquely identify an event
+ // for metrics analysis).
+ desc string
+
+ // The time that the event started to occur.
+ start time.Time
+
+ // The list of process resource information that was executed.
+ procResInfo []*soong_metrics_proto.ProcessResourceInfo
+}
+
+// newEvent returns an event with start populated with the now time.
+func newEvent(name, desc string) *event {
+ return &event{
+ name: name,
+ desc: desc,
+ start: _now(),
+ }
+}
+
+func (e event) perfInfo() soong_metrics_proto.PerfInfo {
+ realTime := uint64(_now().Sub(e.start).Nanoseconds())
+ return soong_metrics_proto.PerfInfo{
+ Desc: proto.String(e.desc),
+ Name: proto.String(e.name),
+ StartTime: proto.Uint64(uint64(e.start.UnixNano())),
+ RealTime: proto.Uint64(realTime),
+ ProcessesResourceInfo: e.procResInfo,
+ }
+}
+
+// EventTracer is an array of events that provides functionality to trace a
+// block of code on time and performance. The End call expects the Begin is
+// invoked, otherwise panic is raised.
+type EventTracer []*event
+
+// empty returns true if there are no pending events.
+func (t *EventTracer) empty() bool {
+ return len(*t) == 0
+}
+
+// lastIndex returns the index of the last element of events.
+func (t *EventTracer) lastIndex() int {
+ return len(*t) - 1
+}
+
+// peek returns the active build event.
+func (t *EventTracer) peek() *event {
+ return (*t)[t.lastIndex()]
+}
+
+// push adds the active build event in the stack.
+func (t *EventTracer) push(e *event) {
+ *t = append(*t, e)
+}
+
+// pop removes the active event from the stack since the event has completed.
+// A panic is raised if there are no pending events.
+func (t *EventTracer) pop() *event {
+ if t.empty() {
+ panic("Internal error: No pending events")
+ }
+ e := (*t)[t.lastIndex()]
+ *t = (*t)[:t.lastIndex()]
+ return e
+}
+
+// AddProcResInfo adds information on an executed process such as max resident
+// set memory and the number of voluntary context switches.
+func (t *EventTracer) AddProcResInfo(name string, state *os.ProcessState) {
+ if t.empty() {
return
}
rusage := state.SysUsage().(*syscall.Rusage)
- // The implementation of the metrics system is a stacked based system. The steps of the
- // build system in the UI layer is sequential so the Begin function is invoked when a
- // function (or scoped code) is invoked. That is translated to a new event which is added
- // at the end of the activeEvents array. When the invoking function is completed, End is
- // invoked which is a pop operation from activeEvents.
- curEvent := &t.activeEvents[len(t.activeEvents)-1]
- curEvent.procResInfo = append(curEvent.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
+ e := t.peek()
+ e.procResInfo = append(e.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
Name: proto.String(name),
UserTimeMicros: proto.Uint64(uint64(rusage.Utime.Usec)),
SystemTimeMicros: proto.Uint64(uint64(rusage.Stime.Usec)),
@@ -82,23 +136,13 @@
})
}
-func (t *eventTracerImpl) Begin(name, desc string, _ tracer.Thread) {
- t.activeEvents = append(t.activeEvents, event{name: name, desc: desc, start: _now()})
+// Begin starts tracing the event.
+func (t *EventTracer) Begin(name, desc string, _ tracer.Thread) {
+ t.push(newEvent(name, desc))
}
-func (t *eventTracerImpl) End(tracer.Thread) soong_metrics_proto.PerfInfo {
- if len(t.activeEvents) < 1 {
- panic("Internal error: No pending events for endAt to end!")
- }
- lastEvent := t.activeEvents[len(t.activeEvents)-1]
- t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
- realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
-
- return soong_metrics_proto.PerfInfo{
- Desc: proto.String(lastEvent.desc),
- Name: proto.String(lastEvent.name),
- StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
- RealTime: proto.Uint64(realTime),
- ProcessesResourceInfo: lastEvent.procResInfo,
- }
+// End performs post calculations such as duration of the event, aggregates
+// the collected performance information into PerfInfo protobuf message.
+func (t *EventTracer) End(tracer.Thread) soong_metrics_proto.PerfInfo {
+ return t.pop().perfInfo()
}
diff --git a/ui/metrics/event_test.go b/ui/metrics/event_test.go
index 6fc0b50..043450b 100644
--- a/ui/metrics/event_test.go
+++ b/ui/metrics/event_test.go
@@ -28,14 +28,14 @@
_now = func() time.Time { return startTime.Add(dur) }
defer func() { _now = initialNow }()
- eventTracer := &eventTracerImpl{}
- eventTracer.activeEvents = append(eventTracer.activeEvents, event{
+ et := &EventTracer{}
+ et.push(&event{
desc: "test",
name: "test",
start: startTime,
})
- perf := eventTracer.End(tracer.Thread(0))
+ perf := et.End(tracer.Thread(0))
if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 7031042..efb572c 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -12,8 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Package metrics represents the metrics system for Android Platform Build Systems.
package metrics
+// This is the main heart of the metrics system for Android Platform Build Systems.
+// The starting of the soong_ui (cmd/soong_ui/main.go), the metrics system is
+// initialized by the invocation of New and is then stored in the context
+// (ui/build/context.go) to be used throughout the system. During the build
+// initialization phase, several functions in this file are invoked to store
+// information such as the environment, build configuration and build metadata.
+// There are several scoped code that has Begin() and defer End() functions
+// that captures the metrics and is them added as a perfInfo into the set
+// of the collected metrics. Finally, when soong_ui has finished the build,
+// the defer Dump function is invoked to store the collected metrics to the
+// raw protobuf file in the $OUT directory.
+//
+// There is one additional step that occurs after the raw protobuf file is written.
+// If the configuration environment variable ANDROID_ENABLE_METRICS_UPLOAD is
+// set with the path, the raw protobuf file is uploaded to the destination. See
+// ui/build/upload.go for more details. The filename of the raw protobuf file
+// and the list of files to be uploaded is defined in cmd/soong_ui/main.go.
+//
+// See ui/metrics/event.go for the explanation of what an event is and how
+// the metrics system is a stack based system.
+
import (
"io/ioutil"
"os"
@@ -23,33 +45,51 @@
"github.com/golang/protobuf/proto"
- soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ "android/soong/ui/metrics/metrics_proto"
)
const (
- PrimaryNinja = "ninja"
- RunKati = "kati"
+ // Below is a list of names passed in to the Begin tracing functions. These
+ // names are used to group a set of metrics.
+
+ // Setup and tear down of the build systems.
RunSetupTool = "setup"
RunShutdownTool = "shutdown"
- RunSoong = "soong"
- RunBazel = "bazel"
TestRun = "test"
- Total = "total"
+
+ // List of build system tools.
+ RunSoong = "soong"
+ PrimaryNinja = "ninja"
+ RunKati = "kati"
+ RunBazel = "bazel"
+
+ // Overall build from building the graph to building the target.
+ Total = "total"
)
+// Metrics is a struct that stores collected metrics during the course
+// of a build which later is dumped to a MetricsBase protobuf file.
+// See ui/metrics/metrics_proto/metrics.proto for further details
+// on what information is collected.
type Metrics struct {
- metrics soong_metrics_proto.MetricsBase
- EventTracer EventTracer
+ // The protobuf message that is later written to the file.
+ metrics soong_metrics_proto.MetricsBase
+
+ // A list of pending build events.
+ EventTracer *EventTracer
}
+// New returns a pointer of Metrics to store a set of metrics.
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
- EventTracer: &eventTracerImpl{},
+ EventTracer: &EventTracer{},
}
return m
}
+// SetTimeMetrics stores performance information from an executed block of
+// code.
func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
switch perf.GetName() {
case RunKati:
@@ -60,19 +100,26 @@
m.metrics.BazelRuns = append(m.metrics.BazelRuns, &perf)
case PrimaryNinja:
m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
+ case RunSetupTool:
+ m.metrics.SetupTools = append(m.metrics.SetupTools, &perf)
case Total:
m.metrics.Total = &perf
}
}
+// BuildConfig stores information about the build configuration.
func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) {
m.metrics.BuildConfig = b
}
+// SystemResourceInfo stores information related to the host system such
+// as total CPU and memory.
func (m *Metrics) SystemResourceInfo(b *soong_metrics_proto.SystemResourceInfo) {
m.metrics.SystemResourceInfo = b
}
+// SetMetadataMetrics sets information about the build such as the target
+// product, host architecture and out directory.
func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
for k, v := range metadata {
switch k {
@@ -92,15 +139,15 @@
m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_ENG.Enum()
}
case "TARGET_ARCH":
- m.metrics.TargetArch = m.getArch(v)
+ m.metrics.TargetArch = arch(v)
case "TARGET_ARCH_VARIANT":
m.metrics.TargetArchVariant = proto.String(v)
case "TARGET_CPU_VARIANT":
m.metrics.TargetCpuVariant = proto.String(v)
case "HOST_ARCH":
- m.metrics.HostArch = m.getArch(v)
+ m.metrics.HostArch = arch(v)
case "HOST_2ND_ARCH":
- m.metrics.Host_2NdArch = m.getArch(v)
+ m.metrics.Host_2NdArch = arch(v)
case "HOST_OS_EXTRA":
m.metrics.HostOsExtra = proto.String(v)
case "HOST_CROSS_OS":
@@ -115,8 +162,10 @@
}
}
-func (m *Metrics) getArch(arch string) *soong_metrics_proto.MetricsBase_Arch {
- switch arch {
+// arch returns the corresponding MetricsBase_Arch based on the string
+// parameter.
+func arch(a string) *soong_metrics_proto.MetricsBase_Arch {
+ switch a {
case "arm":
return soong_metrics_proto.MetricsBase_ARM.Enum()
case "arm64":
@@ -130,37 +179,51 @@
}
}
+// SetBuildDateTime sets the build date and time. The value written
+// to the protobuf file is in seconds.
func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
+// SetBuildCommand adds the build command specified by the user to the
+// list of collected metrics.
func (m *Metrics) SetBuildCommand(cmd []string) {
m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
}
-// exports the output to the file at outputPath
-func (m *Metrics) Dump(outputPath string) error {
+// Dump exports the collected metrics from the executed build to the file at
+// out path.
+func (m *Metrics) Dump(out string) error {
// ignore the error if the hostname could not be retrieved as it
// is not a critical metric to extract.
if hostname, err := os.Hostname(); err == nil {
m.metrics.Hostname = proto.String(hostname)
}
m.metrics.HostOs = proto.String(runtime.GOOS)
- return writeMessageToFile(&m.metrics, outputPath)
+
+ return save(&m.metrics, out)
}
+// SetSoongBuildMetrics sets the metrics collected from the soong_build
+// execution.
func (m *Metrics) SetSoongBuildMetrics(metrics *soong_metrics_proto.SoongBuildMetrics) {
m.metrics.SoongBuildMetrics = metrics
}
+// A CriticalUserJourneysMetrics is a struct that contains critical user journey
+// metrics. These critical user journeys are defined under cuj/cuj.go file.
type CriticalUserJourneysMetrics struct {
+ // A list of collected CUJ metrics.
cujs soong_metrics_proto.CriticalUserJourneysMetrics
}
+// NewCriticalUserJourneyMetrics returns a pointer of CriticalUserJourneyMetrics
+// to capture CUJs metrics.
func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics {
return &CriticalUserJourneysMetrics{}
}
+// Add adds a set of collected metrics from an executed critical user journey.
func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) {
c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{
Name: proto.String(name),
@@ -168,22 +231,25 @@
})
}
-func (c *CriticalUserJourneysMetrics) Dump(outputPath string) (err error) {
- return writeMessageToFile(&c.cujs, outputPath)
+// Dump saves the collected CUJs metrics to the raw protobuf file.
+func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) {
+ return save(&c.cujs, filename)
}
-func writeMessageToFile(pb proto.Message, outputPath string) (err error) {
+// save takes a protobuf message, marshals to an array of bytes
+// and is then saved to a file.
+func save(pb proto.Message, filename string) (err error) {
data, err := proto.Marshal(pb)
if err != nil {
return err
}
- tempPath := outputPath + ".tmp"
- err = ioutil.WriteFile(tempPath, []byte(data), 0644)
- if err != nil {
+
+ tempFilename := filename + ".tmp"
+ if err := ioutil.WriteFile(tempFilename, []byte(data), 0644 /* rw-r--r-- */); err != nil {
return err
}
- err = os.Rename(tempPath, outputPath)
- if err != nil {
+
+ if err := os.Rename(tempFilename, filename); err != nil {
return err
}
diff --git a/ui/metrics/proc/Android.bp b/ui/metrics/proc/Android.bp
new file mode 100644
index 0000000..32d8217
--- /dev/null
+++ b/ui/metrics/proc/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+
+bootstrap_go_package {
+ name: "soong-ui-metrics-proc",
+ pkgPath: "android/soong/ui/metrics/proc",
+ deps: [
+ "soong-finder-fs",
+ ],
+ srcs: [
+ "status.go",
+ ],
+ linux: {
+ srcs: [
+ "status_linux.go",
+ ],
+ testSrcs: [
+ "status_linux_test.go",
+ ],
+ },
+ darwin: {
+ srcs: [
+ "status_darwin.go",
+ ],
+ },
+}
diff --git a/ui/metrics/proc/status.go b/ui/metrics/proc/status.go
new file mode 100644
index 0000000..f8b734c
--- /dev/null
+++ b/ui/metrics/proc/status.go
@@ -0,0 +1,128 @@
+// package proc contains functionality to read proc status files.
+package proc
+
+import (
+ "strconv"
+ "strings"
+)
+
+// ProcStatus holds information regarding the memory usage of
+// an executing process. The memory sizes in each of the field
+// is in bytes.
+type ProcStatus struct {
+ // Process PID.
+ pid int
+
+ // Peak virtual memory size.
+ VmPeak uint64
+
+ // Virtual memory size.
+ VmSize uint64
+
+ // Locked Memory size.
+ VmLck uint64
+
+ // Pinned memory size.
+ VmPin uint64
+
+ // Peak resident set size.
+ VmHWM uint64
+
+ // Resident set size (sum of RssAnon, RssFile and RssShmem).
+ VmRss uint64
+
+ // Size of resident anonymous memory.
+ RssAnon uint64
+
+ // Size of resident shared memory.
+ RssShmem uint64
+
+ // Size of data segments.
+ VmData uint64
+
+ // Size of stack segments.
+ VmStk uint64
+
+ //Size of text segments.
+ VmExe uint64
+
+ //Shared library code size.
+ VmLib uint64
+
+ // Page table entries size.
+ VmPTE uint64
+
+ // Size of second-level page tables.
+ VmPMD uint64
+
+ // Swapped-out virtual memory size by anonymous private.
+ VmSwap uint64
+
+ // Size of hugetlb memory page size.
+ HugetlbPages uint64
+}
+
+// fillProcStatus takes the key and value, converts the value
+// to the proper size unit and is stored in the ProcStatus.
+func fillProcStatus(s *ProcStatus, key, value string) {
+ v := strToUint64(value)
+ switch key {
+ case "VmPeak":
+ s.VmPeak = v
+ case "VmSize":
+ s.VmSize = v
+ case "VmLck":
+ s.VmLck = v
+ case "VmPin":
+ s.VmPin = v
+ case "VmHWM":
+ s.VmHWM = v
+ case "VmRSS":
+ s.VmRss = v
+ case "RssAnon":
+ s.RssAnon = v
+ case "RssShmem":
+ s.RssShmem = v
+ case "VmData":
+ s.VmData = v
+ case "VmStk":
+ s.VmStk = v
+ case "VmExe":
+ s.VmExe = v
+ case "VmLib":
+ s.VmLib = v
+ case "VmPTE":
+ s.VmPTE = v
+ case "VmPMD":
+ s.VmPMD = v
+ case "VmSwap":
+ s.VmSwap = v
+ case "HugetlbPages":
+ s.HugetlbPages = v
+ }
+}
+
+// strToUint64 takes the string and converts to unsigned 64-bit integer.
+// If the string contains a memory unit such as kB and is converted to
+// bytes.
+func strToUint64(v string) uint64 {
+ // v could be "1024 kB" so scan for the empty space and
+ // split between the value and the unit.
+ var separatorIndex int
+ if separatorIndex = strings.IndexAny(v, " "); separatorIndex < 0 {
+ separatorIndex = len(v)
+ }
+ value, err := strconv.ParseUint(v[:separatorIndex], 10, 64)
+ if err != nil {
+ return 0
+ }
+
+ var scale uint64 = 1
+ switch strings.TrimSpace(v[separatorIndex:]) {
+ case "kB", "KB":
+ scale = 1024
+ case "mB", "MB":
+ scale = 1024 * 1024
+ }
+ return value * scale
+}
diff --git a/ui/metrics/proc/status_darwin.go b/ui/metrics/proc/status_darwin.go
new file mode 100644
index 0000000..5c788a5
--- /dev/null
+++ b/ui/metrics/proc/status_darwin.go
@@ -0,0 +1,11 @@
+package proc
+
+import (
+ "android/soong/finder/fs"
+)
+
+// NewProcStatus returns a zero filled value of ProcStatus as it
+// is not supported for darwin distribution based.
+func NewProcStatus(pid int, _ fs.FileSystem) (*ProcStatus, error) {
+ return &ProcStatus{}, nil
+}
diff --git a/ui/metrics/proc/status_linux.go b/ui/metrics/proc/status_linux.go
new file mode 100644
index 0000000..dc0f943
--- /dev/null
+++ b/ui/metrics/proc/status_linux.go
@@ -0,0 +1,46 @@
+package proc
+
+import (
+ "io/ioutil"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "android/soong/finder/fs"
+)
+
+// NewProcStatus returns an instance of the ProcStatus that contains memory
+// information of the process. The memory information is extracted from the
+// "/proc/<pid>/status" text file. This is only available for Linux
+// distribution that supports /proc.
+func NewProcStatus(pid int, fileSystem fs.FileSystem) (*ProcStatus, error) {
+ statusFname := filepath.Join("/proc", strconv.Itoa(pid), "status")
+ r, err := fileSystem.Open(statusFname)
+ if err != nil {
+ return &ProcStatus{}, err
+ }
+ defer r.Close()
+
+ data, err := ioutil.ReadAll(r)
+ if err != nil {
+ return &ProcStatus{}, err
+ }
+
+ s := &ProcStatus{
+ pid: pid,
+ }
+
+ for _, l := range strings.Split(string(data), "\n") {
+ // If the status file does not contain "key: values", just skip the line
+ // as the information we are looking for is not needed.
+ if !strings.Contains(l, ":") {
+ continue
+ }
+
+ // At this point, we're only considering entries that has key, single value pairs.
+ kv := strings.SplitN(l, ":", 2)
+ fillProcStatus(s, strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]))
+ }
+
+ return s, nil
+}
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
new file mode 100644
index 0000000..6709850
--- /dev/null
+++ b/ui/metrics/proc/status_linux_test.go
@@ -0,0 +1,112 @@
+package proc
+
+import (
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "testing"
+
+ "android/soong/finder/fs"
+)
+
+func TestNewProcStatus(t *testing.T) {
+ fs := fs.NewMockFs(nil)
+
+ pid := 4032827
+ procDir := filepath.Join("/proc", strconv.Itoa(pid))
+ if err := fs.MkDirs(procDir); err != nil {
+ t.Fatalf("failed to create proc pid dir %s: %v", procDir, err)
+ }
+ statusFilename := filepath.Join(procDir, "status")
+
+ if err := fs.WriteFile(statusFilename, statusData, 0644); err != nil {
+ t.Fatalf("failed to write proc file %s: %v", statusFilename, err)
+ }
+
+ status, err := NewProcStatus(pid, fs)
+ if err != nil {
+ t.Fatalf("got %v, want nil for error", err)
+ }
+
+ fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
+ if !reflect.DeepEqual(status, expectedStatus) {
+ t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
+ }
+}
+
+var statusData = []byte(`Name: fake_process
+Umask: 0022
+State: S (sleeping)
+Tgid: 4032827
+Ngid: 0
+Pid: 4032827
+PPid: 1
+TracerPid: 0
+Uid: 0 0 0 0
+Gid: 0 0 0 0
+FDSize: 512
+Groups:
+NStgid: 4032827
+NSpid: 4032827
+NSpgid: 4032827
+NSsid: 4032827
+VmPeak: 733232 kB
+VmSize: 733232 kB
+VmLck: 132 kB
+VmPin: 130 kB
+VmHWM: 69156 kB
+VmRSS: 69156 kB
+RssAnon: 50896 kB
+RssFile: 18260 kB
+RssShmem: 122 kB
+VmData: 112388 kB
+VmStk: 132 kB
+VmExe: 9304 kB
+VmLib: 8 kB
+VmPTE: 228 kB
+VmSwap: 10 kB
+HugetlbPages: 22 kB
+CoreDumping: 0
+THP_enabled: 1
+Threads: 46
+SigQ: 2/767780
+SigPnd: 0000000000000000
+ShdPnd: 0000000000000000
+SigBlk: fffffffe3bfa3a00
+SigIgn: 0000000000000000
+SigCgt: fffffffe7fc1feff
+CapInh: 0000000000000000
+CapPrm: 0000003fffffffff
+CapEff: 0000003fffffffff
+CapBnd: 0000003fffffffff
+CapAmb: 0000000000000000
+NoNewPrivs: 0
+Seccomp: 0
+Speculation_Store_Bypass: thread vulnerable
+Cpus_allowed: ff,ffffffff,ffffffff
+Cpus_allowed_list: 0-71
+Mems_allowed: 00000000,00000003
+Mems_allowed_list: 0-1
+voluntary_ctxt_switches: 1635
+nonvoluntary_ctxt_switches: 32
+`)
+
+var expectedStatus = &ProcStatus{
+ pid: 4032827,
+ VmPeak: 750829568,
+ VmSize: 750829568,
+ VmLck: 135168,
+ VmPin: 133120,
+ VmHWM: 70815744,
+ VmRss: 70815744,
+ RssAnon: 52117504,
+ RssShmem: 124928,
+ VmData: 115085312,
+ VmStk: 135168,
+ VmExe: 9527296,
+ VmLib: 8192,
+ VmPTE: 233472,
+ VmSwap: 10240,
+ HugetlbPages: 22528,
+}
diff --git a/ui/terminal/util.go b/ui/terminal/util.go
index 7a603d7..28d97fe 100644
--- a/ui/terminal/util.go
+++ b/ui/terminal/util.go
@@ -41,13 +41,13 @@
func termSize(w io.Writer) (width int, height int, ok bool) {
if f, ok := w.(*os.File); ok {
var winsize struct {
- ws_row, ws_column uint16
- ws_xpixel, ws_ypixel uint16
+ wsRow, wsColumn uint16
+ wsXpixel, wsYpixel uint16
}
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, f.Fd(),
syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&winsize)),
0, 0, 0)
- return int(winsize.ws_column), int(winsize.ws_row), err == 0
+ return int(winsize.wsColumn), int(winsize.wsRow), err == 0
} else if f, ok := w.(*fakeSmartTerminal); ok {
return f.termWidth, f.termHeight, true
}