Merge "Put external/libchromeos-rs in the Rust allowlist"
diff --git a/android/Android.bp b/android/Android.bp
index 4bd272d..f8c1d55 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -23,7 +23,8 @@
"csuite_config.go",
"defaults.go",
"defs.go",
- "depset.go",
+ "depset_generic.go",
+ "depset_paths.go",
"deptag.go",
"expand.go",
"filegroup.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 063830b..b32048a 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -12,6 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// This file offers AndroidMkEntriesProvider, which individual modules implement to output
+// Android.mk entries that contain information about the modules built through Soong. Kati reads
+// and combines them with the legacy Make-based module definitions to produce the complete view of
+// the source tree, which makes this a critical point of Make-Soong interoperability.
+//
+// Naturally, Soong-only builds do not rely on this mechanism.
+
package android
import (
@@ -36,8 +43,8 @@
ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
}
-// Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to
-// use the Custom function.
+// Deprecated: Use AndroidMkEntriesProvider instead, especially if you're not going to use the
+// Custom function. It's easier to use and test.
type AndroidMkDataProvider interface {
AndroidMk() AndroidMkData
BaseModuleName() string
@@ -63,37 +70,82 @@
type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
-// Allows modules to customize their Android*.mk output.
+// Interface for modules to declare their Android.mk outputs. Note that every module needs to
+// implement this in order to be included in the final Android-<product_name>.mk output, even if
+// they only need to output the common set of entries without any customizations.
type AndroidMkEntriesProvider interface {
+ // Returns AndroidMkEntries objects that contain all basic info plus extra customization data
+ // if needed. This is the core func to implement.
+ // Note that one can return multiple objects. For example, java_library may return an additional
+ // AndroidMkEntries object for its hostdex sub-module.
AndroidMkEntries() []AndroidMkEntries
+ // Modules don't need to implement this as it's already implemented by ModuleBase.
+ // AndroidMkEntries uses BaseModuleName() instead of ModuleName() because certain modules
+ // e.g. Prebuilts, override the Name() func and return modified names.
+ // If a different name is preferred, use SubName or OverrideName in AndroidMkEntries.
BaseModuleName() string
}
+// The core data struct that modules use to provide their Android.mk data.
type AndroidMkEntries struct {
- Class string
- SubName string
- OverrideName string
- DistFiles TaggedDistFiles
- OutputFile OptionalPath
- Disabled bool
- Include string
- Required []string
- Host_required []string
+ // Android.mk class string, e.g EXECUTABLES, JAVA_LIBRARIES, ETC
+ Class string
+ // Optional suffix to append to the module name. Useful when a module wants to return multiple
+ // AndroidMkEntries objects. For example, when a java_library returns an additional entry for
+ // its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
+ // different name than the parent's.
+ SubName string
+ // If set, this value overrides the base module name. SubName is still appended.
+ OverrideName string
+ // Dist files to output
+ DistFiles TaggedDistFiles
+ // The output file for Kati to process and/or install. If absent, the module is skipped.
+ OutputFile OptionalPath
+ // If true, the module is skipped and does not appear on the final Android-<product name>.mk
+ // file. Useful when a module needs to be skipped conditionally.
+ Disabled bool
+ // The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_prebuilt.mk
+ // If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
+ Include string
+ // Required modules that need to be built and included in the final build output when building
+ // this module.
+ Required []string
+ // Required host modules that need to be built and included in the final build output when
+ // building this module.
+ Host_required []string
+ // Required device modules that need to be built and included in the final build output when
+ // building this module.
Target_required []string
header bytes.Buffer
footer bytes.Buffer
+ // Funcs to append additional Android.mk entries or modify the common ones. Multiple funcs are
+ // accepted so that common logic can be factored out as a shared func.
ExtraEntries []AndroidMkExtraEntriesFunc
+ // Funcs to add extra lines to the module's Android.mk output. Unlike AndroidMkExtraEntriesFunc,
+ // which simply sets Make variable values, this can be used for anything since it can write any
+ // Make statements directly to the final Android-*.mk file.
+ // Primarily used to call macros or declare/update Make targets.
ExtraFooters []AndroidMkExtraFootersFunc
- EntryMap map[string][]string
+ // A map that holds the up-to-date Make variable values. Can be accessed from tests.
+ EntryMap map[string][]string
+ // A list of EntryMap keys in insertion order. This serves a few purposes:
+ // 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
+ // the outputted Android-*.mk file may change even though there have been no content changes.
+ // 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
+ // without worrying about the variables being mixed up in the actual mk file.
+ // 3. Makes troubleshooting and spotting errors easier.
entryOrder []string
}
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.
+
+// SetString sets a Make variable with the given name to the given value.
func (a *AndroidMkEntries) SetString(name, value string) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -101,6 +153,7 @@
a.EntryMap[name] = []string{value}
}
+// SetPath sets a Make variable with the given name to the given path string.
func (a *AndroidMkEntries) SetPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -108,12 +161,15 @@
a.EntryMap[name] = []string{path.String()}
}
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.SetPath(name, path.Path())
}
}
+// AddPath appends the given path string to a Make variable with the given name.
func (a *AndroidMkEntries) AddPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -121,12 +177,15 @@
a.EntryMap[name] = append(a.EntryMap[name], path.String())
}
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.AddPath(name, path.Path())
}
}
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
func (a *AndroidMkEntries) SetPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -134,12 +193,15 @@
a.EntryMap[name] = paths.Strings()
}
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) {
if len(paths) > 0 {
a.SetPaths(name, paths)
}
}
+// AddPaths appends the given path strings to a Make variable with the given name.
func (a *AndroidMkEntries) AddPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -147,6 +209,8 @@
a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
}
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
if flag {
if _, ok := a.EntryMap[name]; !ok {
@@ -156,6 +220,7 @@
}
}
+// SetBool sets a Make variable with the given name to if the given bool flag value.
func (a *AndroidMkEntries) SetBool(name string, flag bool) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -167,6 +232,7 @@
}
}
+// AddStrings appends the given strings to a Make variable with the given name.
func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
if len(value) == 0 {
return
@@ -177,6 +243,18 @@
a.EntryMap[name] = append(a.EntryMap[name], value...)
}
+// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
+// for partial MTS test suites.
+func (a *AndroidMkEntries) AddCompatibilityTestSuites(suites ...string) {
+ // MTS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+ // To reduce repetition, if we find a partial MTS test suite without an full MTS test suite,
+ // we add the full test suite to our list.
+ if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+ suites = append(suites, "mts")
+ }
+ a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
+}
+
// The contributions to the dist.
type distContributions struct {
// List of goals and the dist copy instructions.
@@ -356,6 +434,8 @@
return generateDistContributionsForMake(distContributions)
}
+// 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) {
a.EntryMap = make(map[string][]string)
amod := mod.(Module).base()
@@ -419,7 +499,9 @@
}
}
- a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
+ if !amod.InRamdisk() && !amod.InVendorRamdisk() {
+ a.AddStrings("LOCAL_INIT_RC", amod.commonProperties.Init_rc...)
+ }
a.AddStrings("LOCAL_VINTF_FRAGMENTS", amod.commonProperties.Vintf_fragments...)
a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
@@ -472,10 +554,12 @@
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)
}
}
+// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
func (a *AndroidMkEntries) write(w io.Writer) {
if a.Disabled {
return
@@ -496,6 +580,8 @@
return strings.Split(string(a.footer.Bytes()), "\n")
}
+// AndroidMkSingleton is a singleton to collect Android.mk data from all modules and dump them into
+// the final Android-<product_name>.mk file output.
func AndroidMkSingleton() Singleton {
return &androidMkSingleton{}
}
@@ -503,6 +589,7 @@
type androidMkSingleton struct{}
func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
+ // Skip if Soong wasn't invoked from Make.
if !ctx.Config().KatiEnabled() {
return
}
@@ -513,6 +600,8 @@
androidMkModulesList = append(androidMkModulesList, module)
})
+ // Sort the module list by the module names to eliminate random churns, which may erroneously
+ // invoke additional build processes.
sort.SliceStable(androidMkModulesList, func(i, j int) bool {
return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
})
@@ -605,6 +694,8 @@
}
}
+// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
+// m by making them phony targets.
func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
goBinary bootstrap.GoBinaryTool) error {
@@ -637,6 +728,8 @@
data.Target_required = data.Entries.Target_required
}
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
provider AndroidMkDataProvider) error {
@@ -683,6 +776,8 @@
return nil
}
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
if data.Disabled {
return
@@ -730,11 +825,14 @@
module.Os() == LinuxBionic
}
+// A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
+// to use this func.
func AndroidMkDataPaths(data []DataPath) []string {
var testFiles []string
for _, d := range data {
rel := d.SrcPath.Rel()
path := d.SrcPath.String()
+ // LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path.
if !strings.HasSuffix(path, rel) {
panic(fmt.Errorf("path %q does not end with %q", path, rel))
}
diff --git a/android/apex.go b/android/apex.go
index a4ff0f9..f6eca86 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -153,13 +153,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 +220,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()
@@ -257,7 +261,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 +306,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
@@ -442,7 +446,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
diff --git a/android/arch.go b/android/arch.go
index df407d4..34f9b29 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1337,17 +1337,6 @@
addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
- // An optional host target that uses the Bionic glibc runtime.
- if Bool(config.Host_bionic) {
- addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
- }
-
- // An optional cross-compiled host target that uses the Bionic glibc runtime on an arm64
- // architecture.
- if Bool(config.Host_bionic_arm64) {
- addTarget(LinuxBionic, "arm64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
- }
-
// Optional cross-compiled host targets, generally Windows.
if String(variables.CrossHost) != "" {
crossHostOs := osByName(*variables.CrossHost)
@@ -1437,53 +1426,6 @@
abi []string
}
-// getMegaDeviceConfig returns a list of archConfigs for every architecture simultaneously.
-func getMegaDeviceConfig() []archConfig {
- return []archConfig{
- {"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "generic", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a7", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a8", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a9", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a72", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a75", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a76", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "kryo385", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "exynos-m1", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "exynos-m2", []string{"armeabi-v7a"}},
- {"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "cortex-a72", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "cortex-a73", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a55", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a75", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a76", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
- {"x86", "atom", "", []string{"x86"}},
- {"x86", "haswell", "", []string{"x86"}},
- {"x86", "ivybridge", "", []string{"x86"}},
- {"x86", "sandybridge", "", []string{"x86"}},
- {"x86", "silvermont", "", []string{"x86"}},
- {"x86", "stoneyridge", "", []string{"x86"}},
- {"x86", "x86_64", "", []string{"x86"}},
- {"x86_64", "", "", []string{"x86_64"}},
- {"x86_64", "haswell", "", []string{"x86_64"}},
- {"x86_64", "ivybridge", "", []string{"x86_64"}},
- {"x86_64", "sandybridge", "", []string{"x86_64"}},
- {"x86_64", "silvermont", "", []string{"x86_64"}},
- {"x86_64", "stoneyridge", "", []string{"x86_64"}},
- }
-}
-
// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
func getNdkAbisConfig() []archConfig {
return []archConfig{
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 7d8d12f..d810726 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -26,6 +26,8 @@
"strings"
"sync"
+ "android/soong/bazel"
+ "android/soong/shared"
"github.com/google/blueprint/bootstrap"
)
@@ -68,6 +70,7 @@
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
@@ -153,6 +156,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 +168,10 @@
}
}
+func (context *bazelContext) BazelMetricsDir() string {
+ return context.metricsDir
+}
+
func (context *bazelContext) BazelEnabled() bool {
return true
}
@@ -189,12 +201,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...)
@@ -341,7 +354,7 @@
return err
}
buildroot_label := "//:buildroot"
- cqueryOutput, err = context.issueBazelCommand("cquery",
+ cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
[]string{fmt.Sprintf("deps(%s)", buildroot_label)},
"--output=starlark",
"--starlark:file="+cquery_file_relpath)
@@ -371,7 +384,7 @@
// bazel actions should either be added to the Ninja file and executed later,
// or bazel should handle execution.
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
- _, err = context.issueBazelCommand("build", []string{buildroot_label})
+ _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build", []string{buildroot_label})
if err != nil {
return err
diff --git a/android/config.go b/android/config.go
index 04c9129..89467d8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -54,28 +54,9 @@
isPreview: true,
}
-// configFileName is the name the file containing FileConfigurableOptions from
-// soong_ui for the soong_build primary builder.
-const configFileName = "soong.config"
-
-// productVariablesFileName contain the product configuration variables from soong_ui for the
-// soong_build primary builder and Kati.
+// The product variables file name, containing product config from Kati.
const productVariablesFileName = "soong.variables"
-// A FileConfigurableOptions contains options which can be configured by the
-// config file. These will be included in the config struct.
-type FileConfigurableOptions struct {
- Mega_device *bool `json:",omitempty"`
- Host_bionic *bool `json:",omitempty"`
- Host_bionic_arm64 *bool `json:",omitempty"`
-}
-
-// SetDefaultConfig resets the receiving FileConfigurableOptions to default
-// values.
-func (f *FileConfigurableOptions) SetDefaultConfig() {
- *f = FileConfigurableOptions{}
-}
-
// A Config object represents the entire build configuration for Android.
type Config struct {
*config
@@ -97,12 +78,8 @@
type VendorConfig soongconfig.SoongConfig
// Definition of general build configuration for soong_build. Some of these
-// configuration values are generated from soong_ui for soong_build,
-// communicated over JSON files like soong.config or soong.variables.
+// product configuration values are read from Kati-generated soong.variables.
type config struct {
- // Options configurable with soong.confg
- FileConfigurableOptions
-
// Options configurable with soong.variables
productVariables productVariables
@@ -113,7 +90,6 @@
// purposes.
BazelContext BazelContext
- ConfigFileName string
ProductVariablesFileName string
Targets map[OsType][]Target
@@ -170,11 +146,6 @@
}
func loadConfig(config *config) error {
- err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
- if err != nil {
- return err
- }
-
return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
@@ -384,7 +355,6 @@
func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
// Make a config with default options.
config := &config{
- ConfigFileName: filepath.Join(buildDir, configFileName),
ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
env: originalEnv,
@@ -439,9 +409,7 @@
targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
var archConfig []archConfig
- if Bool(config.Mega_device) {
- archConfig = getMegaDeviceConfig()
- } else if config.NdkAbis() {
+ if config.NdkAbis() {
archConfig = getNdkAbisConfig()
} else if config.AmlAbis() {
archConfig = getAmlAbisConfig()
@@ -864,11 +832,6 @@
return c.Targets[Android][0].Arch.ArchType
}
-func (c *config) SkipMegaDeviceInstall(path string) bool {
- return Bool(c.Mega_device) &&
- strings.HasPrefix(path, filepath.Join(c.buildDir, "target", "product"))
-}
-
func (c *config) SanitizeHost() []string {
return append([]string(nil), c.productVariables.SanitizeHost...)
}
@@ -1309,6 +1272,10 @@
return Bool(c.productVariables.Flatten_apex)
}
+func (c *config) CompressedApex() bool {
+ return Bool(c.productVariables.CompressedApex)
+}
+
func (c *config) EnforceSystemCertificate() bool {
return Bool(c.productVariables.EnforceSystemCertificate)
}
@@ -1321,6 +1288,14 @@
return Bool(c.productVariables.EnforceProductPartitionInterface)
}
+func (c *config) EnforceInterPartitionJavaSdkLibrary() bool {
+ return Bool(c.productVariables.EnforceInterPartitionJavaSdkLibrary)
+}
+
+func (c *config) InterPartitionJavaLibraryAllowList() []string {
+ return c.productVariables.InterPartitionJavaLibraryAllowList
+}
+
func (c *config) InstallExtraFlattenedApexes() bool {
return Bool(c.productVariables.InstallExtraFlattenedApexes)
}
@@ -1349,10 +1324,6 @@
return c.productVariables.ProductPrivateSepolicyDirs
}
-func (c *config) ProductCompatibleProperty() bool {
- return Bool(c.productVariables.ProductCompatibleProperty)
-}
-
func (c *config) MissingUsesLibraries() []string {
return c.productVariables.MissingUsesLibraries
}
diff --git a/android/config_test.go b/android/config_test.go
index 68f68a0..7bfc800 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -78,11 +78,6 @@
if err != nil {
t.Errorf(err.Error())
}
-
- validateConfigAnnotations(&FileConfigurableOptions{})
- if err != nil {
- t.Errorf(err.Error())
- }
}
func TestMissingVendorConfig(t *testing.T) {
diff --git a/android/csuite_config.go b/android/csuite_config.go
index a5b1533..bf24d98 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -44,7 +44,7 @@
if me.properties.Test_config != nil {
entries.SetString("LOCAL_TEST_CONFIG", *me.properties.Test_config)
}
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "csuite")
+ entries.AddCompatibilityTestSuites("csuite")
},
}
return []AndroidMkEntries{androidMkEntries}
diff --git a/android/defs.go b/android/defs.go
index 631dfe8..38ecb05 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -15,11 +15,12 @@
package android
import (
+ "fmt"
"strings"
"testing"
"github.com/google/blueprint"
- _ "github.com/google/blueprint/bootstrap"
+ "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
)
@@ -97,7 +98,7 @@
// content to file.
writeFile = pctx.AndroidStaticRule("writeFile",
blueprint.RuleParams{
- Command: `/bin/bash -c 'echo -e "$$0" > $out' $content`,
+ Command: `/bin/bash -c 'echo -e -n "$$0" > $out' $content`,
Description: "writing file $out",
},
"content")
@@ -133,9 +134,7 @@
shellUnescaper = strings.NewReplacer(`'\''`, `'`)
)
-// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
-// so that the file contains exactly the contents passed to the function, plus a trailing newline.
-func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
content = echoEscaper.Replace(content)
content = proptools.ShellEscape(content)
if content == "" {
@@ -151,6 +150,31 @@
})
}
+// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
+// so that the file contains exactly the contents passed to the function, plus a trailing newline.
+func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+ // This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes
+ const SHARD_SIZE = 131072 - 10000
+
+ content += "\n"
+ if len(content) > SHARD_SIZE {
+ var chunks WritablePaths
+ for i, c := range ShardString(content, SHARD_SIZE) {
+ tempPath := outputFile.ReplaceExtension(ctx, fmt.Sprintf("%s.%d", outputFile.Ext(), i))
+ buildWriteFileRule(ctx, tempPath, c)
+ chunks = append(chunks, tempPath)
+ }
+ ctx.Build(pctx, BuildParams{
+ Rule: Cat,
+ Inputs: chunks.Paths(),
+ Output: outputFile,
+ Description: "Merging to " + outputFile.Base(),
+ })
+ return
+ }
+ buildWriteFileRule(ctx, outputFile, content)
+}
+
// shellUnescape reverses proptools.ShellEscape
func shellUnescape(s string) string {
// Remove leading and trailing quotes if present
@@ -176,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/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/module.go b/android/module.go
index d680baa..d540324 100644
--- a/android/module.go
+++ b/android/module.go
@@ -331,6 +331,8 @@
type ModuleContext interface {
BaseModuleContext
+ blueprintModuleContext() blueprint.ModuleContext
+
// Deprecated: use ModuleContext.Build instead.
ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@@ -338,10 +340,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
@@ -441,6 +484,10 @@
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
}
// Qualified id for a module
@@ -1003,12 +1050,14 @@
// 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
+ 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
@@ -1338,19 +1387,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 +1408,10 @@
return m.packagingSpecs
}
+func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
+ return m.packagingSpecsDepSet.ToList()
+}
+
func (m *ModuleBase) NoAddressSanitizer() bool {
return m.noAddressSanitizer
}
@@ -1587,11 +1638,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
@@ -1700,6 +1755,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 +1932,6 @@
bp blueprint.ModuleContext
baseModuleContext
packagingSpecs []PackagingSpec
- installDeps InstallPaths
installFiles InstallPaths
checkbuildFiles Paths
module Module
@@ -2399,10 +2456,6 @@
if m.Config().KatiEnabled() && !m.InstallBypassMake() {
return true
}
-
- if m.Config().SkipMegaDeviceInstall(fullInstallPath.String()) {
- return true
- }
}
return false
@@ -2418,14 +2471,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()...)
+ deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
var implicitDeps, orderOnlyDeps Paths
@@ -2455,12 +2523,7 @@
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
@@ -2535,6 +2598,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) {
@@ -2668,7 +2735,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
}
@@ -2862,3 +2934,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..31edea3 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -44,6 +44,11 @@
}
}
+func registerMutatorsForBazelConversion(ctx *blueprint.Context) {
+ // FIXME(b/171263886): Start bringing in mutators to make the Bionic
+ // module subgraph suitable for automated conversion.
+}
+
func registerMutators(ctx *blueprint.Context, preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc) {
mctx := ®isterMutatorsContext{}
diff --git a/android/packaging.go b/android/packaging.go
index f168fa6..da745ff 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -40,6 +40,15 @@
executable bool
}
+// Get file name of installed package
+func (p *PackagingSpec) FileName() string {
+ if p.relPathInPackage != "" {
+ return filepath.Base(p.relPathInPackage)
+ }
+
+ return ""
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
@@ -144,15 +153,9 @@
// See PackageModule.CopyDepsToZip
func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
- var supportedArches []string
- for _, t := range p.getSupportedTargets(ctx) {
- supportedArches = append(supportedArches, t.Arch.ArchType.String())
- }
m := make(map[string]PackagingSpec)
ctx.WalkDeps(func(child Module, parent Module) bool {
- // Don't track modules with unsupported arch
- // TODO(jiyong): remove this when aosp/1501613 lands.
- if !InList(child.Target().Arch.ArchType.String(), supportedArches) {
+ if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
return false
}
for _, ps := range child.PackagingSpecs() {
@@ -163,7 +166,7 @@
return true
})
- builder := NewRuleBuilder()
+ builder := NewRuleBuilder(pctx, ctx)
dir := PathForModuleOut(ctx, ".zip").OutputPath
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
@@ -190,13 +193,33 @@
}
builder.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipOut).
FlagWithArg("-C ", dir.String()).
Flag("-L 0"). // no compression because this will be unzipped soon
FlagWithArg("-D ", dir.String())
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
- builder.Build(pctx, ctx, "zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
+ 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/packaging_test.go b/android/packaging_test.go
index 2acd15c..7269bfb 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -29,6 +29,12 @@
}
}
+// dep tag used in this test. All dependencies are considered as installable.
+type installDepTag struct {
+ blueprint.BaseDependencyTag
+ InstallAlwaysNeededDependencyTag
+}
+
func componentTestModuleFactory() Module {
m := &componentTestModule{}
m.AddProperties(&m.props)
@@ -37,7 +43,7 @@
}
func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -63,7 +69,7 @@
}
func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- m.AddDeps(ctx, struct{ blueprint.BaseDependencyTag }{})
+ m.AddDeps(ctx, installDepTag{})
}
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
diff --git a/android/paths.go b/android/paths.go
index b7117a3..0238a3f 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -138,6 +138,8 @@
// the writablePath method doesn't directly do anything,
// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
writablePath()
+
+ ReplaceExtension(ctx PathContext, ext string) OutputPath
}
type genPathProvider interface {
@@ -540,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 {
@@ -1249,6 +1290,10 @@
return p.config.buildDir
}
+func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
+ panic("Not implemented")
+}
+
var _ Path = InstallPath{}
var _ WritablePath = InstallPath{}
@@ -1511,6 +1556,10 @@
return p.config.buildDir
}
+func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
+ panic("Not implemented")
+}
+
var _ Path = PhonyPath{}
var _ WritablePath = PhonyPath{}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 294a6e0..8114a65 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()
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index e2555e4..b00dc2f 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,6 +75,12 @@
},
})
+ 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)
}
diff --git a/android/proto.go b/android/proto.go
index b712258..0be7893 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -122,7 +122,7 @@
} `android:"arch_variant"`
}
-func ProtoRule(ctx ModuleContext, rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
+func ProtoRule(rule *RuleBuilder, protoFile Path, flags ProtoFlags, deps Paths,
outDir WritablePath, depFile WritablePath, outputs WritablePaths) {
var protoBase string
@@ -134,7 +134,7 @@
}
rule.Command().
- BuiltTool(ctx, "aprotoc").
+ BuiltTool("aprotoc").
FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
FlagWithDepFile("--dependency_out=", depFile).
FlagWithArg("-I ", protoBase).
@@ -144,5 +144,5 @@
ImplicitOutputs(outputs)
rule.Command().
- BuiltTool(ctx, "dep_fixer").Flag(depFile.String())
+ BuiltTool("dep_fixer").Flag(depFile.String())
}
diff --git a/android/queryview.go b/android/queryview.go
index 970ae01..1b7e77d 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -26,15 +26,40 @@
// for calling the soong_build primary builder in the main build.ninja file.
func init() {
RegisterSingletonType("bazel_queryview", BazelQueryViewSingleton)
+ RegisterSingletonType("bazel_converter", BazelConverterSingleton)
}
+// 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"
+ additionalEnvVars := ""
+ descriptionTemplate := "[EXPERIMENTAL, PRE-PRODUCTION] Creating the Bazel QueryView workspace with %s at $outDir"
+ if converterMode {
+ name = "bp2build"
+ additionalEnvVars = "CONVERT_TO_BAZEL=true"
+ descriptionTemplate = "[EXPERIMENTAL, PRE-PRODUCTION] Converting all Android.bp to Bazel BUILD files 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,22 +67,23 @@
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",
blueprint.RuleParams{
Command: fmt.Sprintf(
"rm -rf ${outDir}/* && "+
- "%s --bazel_queryview_dir ${outDir} %s && "+
+ "%s %s --bazel_queryview_dir ${outDir} %s && "+
"echo WORKSPACE: `cat %s` > ${outDir}/.queryview-depfile.d",
+ additionalEnvVars,
primaryBuilder.String(),
strings.Join(os.Args[1:], " "),
moduleListFilePath.String(), // Use the contents of Android.bp.list as the depfile.
),
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 +99,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..b26f9b9 100644
--- a/android/register.go
+++ b/android/register.go
@@ -90,6 +90,21 @@
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))
+ }
+
+ bazelConverterSingleton := singleton{"bp2build", BazelConverterSingleton}
+ ctx.RegisterSingletonType(bazelConverterSingleton.name,
+ SingletonFactoryAdaptor(ctx, bazelConverterSingleton.factory))
+
+ registerMutatorsForBazelConversion(ctx.Context)
+}
+
func (ctx *Context) Register() {
for _, t := range preSingletons {
ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 3efe9f8..84501fe 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -32,11 +32,15 @@
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
// graph.
type RuleBuilder struct {
+ pctx PackageContext
+ ctx BuilderContext
+
commands []*RuleBuilderCommand
installs RuleBuilderInstalls
temporariesSet map[WritablePath]bool
@@ -44,14 +48,17 @@
sbox bool
highmem bool
remoteable RemoteRuleSupports
- sboxOutDir WritablePath
+ outDir WritablePath
+ sboxTools bool
sboxManifestPath WritablePath
missingDeps []string
}
// NewRuleBuilder returns a newly created RuleBuilder.
-func NewRuleBuilder() *RuleBuilder {
+func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
return &RuleBuilder{
+ pctx: pctx,
+ ctx: ctx,
temporariesSet: make(map[WritablePath]bool),
}
}
@@ -130,11 +137,24 @@
panic("Sbox() is not compatible with Restat()")
}
r.sbox = true
- r.sboxOutDir = outputDir
+ r.outDir = outputDir
r.sboxManifestPath = manifestPath
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) {
@@ -146,8 +166,7 @@
// race with any call to Build.
func (r *RuleBuilder) Command() *RuleBuilderCommand {
command := &RuleBuilderCommand{
- sbox: r.sbox,
- outDir: r.sboxOutDir,
+ rule: r,
}
r.commands = append(r.commands, command)
return command
@@ -395,19 +414,19 @@
var _ BuilderContext = ModuleContext(nil)
var _ BuilderContext = SingletonContext(nil)
-func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
+func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- BuiltTool(ctx, "dep_fixer").
+ BuiltTool("dep_fixer").
Inputs(depFiles.Paths())
}
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs.
-func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+func (r *RuleBuilder) Build(name string, desc string) {
name = ninjaNameEscape(name)
if len(r.missingDeps) > 0 {
- ctx.Build(pctx, BuildParams{
+ r.ctx.Build(pctx, BuildParams{
Rule: ErrorRule,
Outputs: r.Outputs(),
OrderOnly: r.OrderOnlys(),
@@ -426,13 +445,13 @@
depFormat = blueprint.DepsGCC
if len(depFiles) > 1 {
// Add a command locally that merges all depfiles together into the first depfile.
- r.depFileMergerCmd(ctx, depFiles)
+ r.depFileMergerCmd(depFiles)
if r.sbox {
// Check for Rel() errors, as all depfiles should be in the output dir. Errors
// will be reported to the ctx.
for _, path := range depFiles[1:] {
- Rel(ctx, r.sboxOutDir.String(), path.String())
+ Rel(r.ctx, r.outDir.String(), path.String())
}
}
}
@@ -464,11 +483,32 @@
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(ctx, r.sboxOutDir.String(), output.String())
+ rel := Rel(r.ctx, r.outDir.String(), output.String())
sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
From: proto.String(filepath.Join(sboxOutSubDir, rel)),
@@ -483,23 +523,27 @@
// Verify that the manifest textproto is not inside the sbox output directory, otherwise
// it will get deleted when the sbox rule clears its output directory.
- _, manifestInOutDir := MaybeRel(ctx, r.sboxOutDir.String(), r.sboxManifestPath.String())
+ _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
if manifestInOutDir {
- ReportPathErrorf(ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
- name, r.sboxManifestPath.String(), r.sboxOutDir.String())
+ ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
+ name, r.sboxManifestPath.String(), r.outDir.String())
}
// Create a rule to write the manifest as a the textproto.
- WriteFileRule(ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
+ WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
// Generate a new string to use as the command line of the sbox rule. This uses
// a RuleBuilderCommand as a convenience method of building the command line, then
// converts it to a string to replace commandString.
- sboxCmd := &RuleBuilderCommand{}
- sboxCmd.Text("rm -rf").Output(r.sboxOutDir)
+ sboxCmd := &RuleBuilderCommand{
+ rule: &RuleBuilder{
+ ctx: r.ctx,
+ },
+ }
+ sboxCmd.Text("rm -rf").Output(r.outDir)
sboxCmd.Text("&&")
- sboxCmd.BuiltTool(ctx, "sbox").
- Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
+ sboxCmd.BuiltTool("sbox").
+ Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Flag("--manifest").Input(r.sboxManifestPath)
// Replace the command string, and add the sbox tool and manifest textproto to the
@@ -528,19 +572,19 @@
}
var pool blueprint.Pool
- if ctx.Config().UseGoma() && r.remoteable.Goma {
+ if r.ctx.Config().UseGoma() && r.remoteable.Goma {
// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
- } else if ctx.Config().UseRBE() && r.remoteable.RBE {
+ } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
// When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
pool = remotePool
} else if r.highmem {
pool = highmemPool
- } else if ctx.Config().UseRemoteBuild() {
+ } else if r.ctx.Config().UseRemoteBuild() {
pool = localPool
}
- ctx.Build(pctx, BuildParams{
- Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
+ r.ctx.Build(r.pctx, BuildParams{
+ Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Command: commandString,
CommandDeps: tools.Strings(),
Restat: r.restat,
@@ -564,6 +608,8 @@
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
+ rule *RuleBuilder
+
buf strings.Builder
inputs Paths
implicits Paths
@@ -572,20 +618,16 @@
symlinkOutputs WritablePaths
depFiles WritablePaths
tools Paths
+ packagedTools []PackagingSpec
rspFileInputs Paths
// spans [start,end) of the command that should not be ninja escaped
unescapedSpans [][2]int
-
- sbox bool
- // outDir is the directory that will contain the output files of the rules. sbox will copy
- // the output files from the sandbox directory to this directory when it finishes.
- outDir WritablePath
}
func (c *RuleBuilderCommand) addInput(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
@@ -594,8 +636,8 @@
}
func (c *RuleBuilderCommand) addImplicit(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
@@ -607,20 +649,90 @@
c.orderOnlys = append(c.orderOnlys, path)
}
-func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
- if c.sbox {
- return SboxPathForOutput(path, c.outDir)
+// PathForOutput takes an output path and returns the appropriate path to use on the command
+// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
+// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
+// original path.
+func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
+ if c.rule.sbox {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+ return filepath.Join(sboxOutDir, rel)
}
return path.String()
}
-// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
-// and returns the corresponding path for the output in the sbox sandbox. This can be used
-// on the RuleBuilder command line to reference the output.
-func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
+// 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
- rel, _, _ := maybeRelErr(outDir.String(), path.String())
- return filepath.Join(sboxOutDir, rel)
+ 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
@@ -691,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
@@ -699,8 +823,8 @@
//
// It is equivalent to:
// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
-func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
- return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+ return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
}
// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
@@ -768,7 +892,7 @@
// RuleBuilder.Outputs.
func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(c.outputStr(path))
+ return c.Text(c.PathForOutput(path))
}
// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
@@ -783,7 +907,7 @@
// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
// and will be the temporary output directory managed by sbox, not the final one.
func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
- if !c.sbox {
+ if !c.rule.sbox {
panic("OutputDir only valid with Sbox")
}
return c.Text(sboxOutDir)
@@ -794,7 +918,7 @@
// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(c.outputStr(path))
+ return c.Text(c.PathForOutput(path))
}
// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
@@ -885,14 +1009,14 @@
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(flag + c.outputStr(path))
+ return c.Text(flag + c.PathForOutput(path))
}
// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(flag + c.outputStr(path))
+ return c.Text(flag + c.PathForOutput(path))
}
// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
@@ -987,3 +1111,21 @@
h.Write([]byte(srcFileList))
return fmt.Sprintf("%x", h.Sum(nil))
}
+
+// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
+// that need to call methods that take a BuilderContext.
+func BuilderContextForTesting(config Config) BuilderContext {
+ pathCtx := PathContextForTesting(config)
+ return builderContextForTests{
+ PathContext: pathCtx,
+ }
+}
+
+type builderContextForTests struct {
+ PathContext
+}
+
+func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
+ return nil
+}
+func (builderContextForTests) Build(PackageContext, BuildParams) {}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index dc360c3..06ea124 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -27,8 +27,8 @@
"android/soong/shared"
)
-func pathContext() PathContext {
- return PathContextForTesting(TestConfig("out", nil, "", map[string][]byte{
+func builderContext() BuilderContext {
+ return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
"ld": nil,
"a.o": nil,
"b.o": nil,
@@ -44,9 +44,9 @@
}
func ExampleRuleBuilder() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ld")).
@@ -55,7 +55,7 @@
rule.Command().Text("echo success")
// To add the command to the build graph:
- // rule.Build(pctx, ctx, "link", "link")
+ // rule.Build("link", "link")
fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
fmt.Printf("tools: %q\n", rule.Tools())
@@ -70,9 +70,9 @@
}
func ExampleRuleBuilder_SymlinkOutputs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ln")).
@@ -96,9 +96,9 @@
}
func ExampleRuleBuilder_Temporary() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -123,9 +123,9 @@
}
func ExampleRuleBuilder_DeleteTemporaryFiles() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -151,9 +151,9 @@
}
func ExampleRuleBuilder_Installs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
out := PathForOutput(ctx, "linked")
@@ -171,9 +171,9 @@
}
func ExampleRuleBuilderCommand() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
// chained
rule.Command().
@@ -194,24 +194,24 @@
}
func ExampleRuleBuilderCommand_Flag() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).Flag("-l"))
// Output:
// ls -l
}
func ExampleRuleBuilderCommand_Flags() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
// Output:
// ls -l -a
}
func ExampleRuleBuilderCommand_FlagWithArg() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagWithArg("--sort=", "time"))
// Output:
@@ -219,8 +219,8 @@
}
func ExampleRuleBuilderCommand_FlagForEachArg() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagForEachArg("--sort=", []string{"time", "size"}))
// Output:
@@ -228,8 +228,8 @@
}
func ExampleRuleBuilderCommand_FlagForEachInput() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "turbine")).
FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
// Output:
@@ -237,8 +237,8 @@
}
func ExampleRuleBuilderCommand_FlagWithInputList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "java")).
FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
// Output:
@@ -246,8 +246,8 @@
}
func ExampleRuleBuilderCommand_FlagWithInput() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "java")).
FlagWithInput("-classpath=", PathForSource(ctx, "a")))
// Output:
@@ -255,8 +255,8 @@
}
func ExampleRuleBuilderCommand_FlagWithList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagWithList("--sort=", []string{"time", "size"}, ","))
// Output:
@@ -264,8 +264,8 @@
}
func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "javac")).
FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
NinjaEscapedString())
@@ -274,7 +274,8 @@
}
func ExampleRuleBuilderCommand_String() {
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Text("FOO=foo").
Text("echo $FOO").
String())
@@ -283,7 +284,8 @@
}
func ExampleRuleBuilderCommand_NinjaEscapedString() {
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Text("FOO=foo").
Text("echo $FOO").
NinjaEscapedString())
@@ -305,7 +307,10 @@
"input3": nil,
}
- ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
+ pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
+ ctx := builderContextForTests{
+ PathContext: pathCtx,
+ }
addCommands := func(rule *RuleBuilder) {
cmd := rule.Command().
@@ -355,7 +360,7 @@
wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
t.Run("normal", func(t *testing.T) {
- rule := NewRuleBuilder()
+ rule := NewRuleBuilder(pctx, ctx)
addCommands(rule)
wantCommands := []string{
@@ -389,13 +394,13 @@
t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
}
- if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
}
})
t.Run("sbox", func(t *testing.T) {
- rule := NewRuleBuilder().Sbox(PathForOutput(ctx, ""),
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
PathForOutput(ctx, "sbox.textproto"))
addCommands(rule)
@@ -427,7 +432,45 @@
t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
}
- if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ 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)
}
})
@@ -476,7 +519,7 @@
}
func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
- rule := NewRuleBuilder()
+ rule := NewRuleBuilder(pctx, ctx)
if sbox {
rule.Sbox(outDir, manifestPath)
@@ -488,7 +531,7 @@
rule.Restat()
}
- rule.Build(pctx, ctx, "rule", "desc")
+ rule.Build("rule", "desc")
}
func TestRuleBuilder_Build(t *testing.T) {
diff --git a/android/test_suites.go b/android/test_suites.go
index 19444a8..7ecb8d2 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -63,13 +63,13 @@
testCasesDir := pathForInstall(ctx, BuildOs, X86, "testcases", false).ToMakePath()
outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
- rule := NewRuleBuilder()
- rule.Command().BuiltTool(ctx, "soong_zip").
+ rule := NewRuleBuilder(pctx, ctx)
+ rule.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", outputFile).
FlagWithArg("-P ", "host/testcases").
FlagWithArg("-C ", testCasesDir.String()).
FlagWithRspFileInputList("-r ", installedPaths.Paths())
- rule.Build(pctx, ctx, "robolectric_tests_zip", "robolectric-tests.zip")
+ rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
return outputFile
}
diff --git a/android/util.go b/android/util.go
index 8e4c0f4..0f940fa 100644
--- a/android/util.go
+++ b/android/util.go
@@ -401,6 +401,23 @@
return ret
}
+// ShardString takes a string and returns a slice of strings where the length of each one is
+// at most shardSize.
+func ShardString(s string, shardSize int) []string {
+ if len(s) == 0 {
+ return nil
+ }
+ ret := make([]string, 0, (len(s)+shardSize-1)/shardSize)
+ for len(s) > shardSize {
+ ret = append(ret, s[0:shardSize])
+ s = s[shardSize:]
+ }
+ if len(s) > 0 {
+ ret = append(ret, s)
+ }
+ return ret
+}
+
// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
// elements.
func ShardStrings(s []string, shardSize int) [][]string {
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 a9a9c87..93dac3d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -319,8 +319,9 @@
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"`
+ CompressedApex *bool `json:",omitempty"`
+ Aml_abis *bool `json:",omitempty"`
DexpreoptGlobalConfig *string `json:",omitempty"`
@@ -337,7 +338,6 @@
ProductPublicSepolicyDirs []string `json:",omitempty"`
ProductPrivateSepolicyDirs []string `json:",omitempty"`
- ProductCompatibleProperty *bool `json:",omitempty"`
ProductVndkVersion *string `json:",omitempty"`
@@ -347,6 +347,9 @@
EnforceProductPartitionInterface *bool `json:",omitempty"`
+ EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"`
+ InterPartitionJavaLibraryAllowList []string `json:",omitempty"`
+
InstallExtraFlattenedApexes *bool `json:",omitempty"`
BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
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/allowed_deps.txt b/apex/allowed_deps.txt
index 5b8563d..c5f2bf8 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -132,6 +132,7 @@
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)
@@ -208,6 +209,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 +224,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 +241,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,6 +281,8 @@
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))
@@ -294,7 +302,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 +313,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 +323,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 +349,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 +361,32 @@
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)
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 +407,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 +417,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 +432,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 +451,7 @@
media_plugin_headers(minSdkVersion:29)
mediaswcodec(minSdkVersion:29)
metrics-constants-protos(minSdkVersion:29)
+modules-utils-build(minSdkVersion:29)
ndk_crtbegin_so.19(minSdkVersion:(no version))
ndk_crtbegin_so.21(minSdkVersion:(no version))
ndk_crtbegin_so.27(minSdkVersion:(no version))
@@ -446,6 +480,7 @@
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)
PermissionController(minSdkVersion:28)
permissioncontroller-statsd(minSdkVersion:current)
philox_random(minSdkVersion:(no version))
diff --git a/apex/androidmk.go b/apex/androidmk.go
index da38c2a..e7f8b7f 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -360,7 +360,11 @@
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
@@ -421,6 +425,15 @@
for _, dist := range data.Entries.GetDistForGoals(a) {
fmt.Fprintf(w, dist)
}
+
+ if a.coverageOutputPath.String() != "" {
+ goal := "apps_only"
+ distFile := a.coverageOutputPath.String()
+ fmt.Fprintf(w, "ifneq (,$(filter $(my_register_name),$(TARGET_BUILD_APPS)))\n"+
+ " $(call dist-for-goals,%s,%s:ndk_apis_usedby_apex/$(notdir %s))\n"+
+ "endif",
+ goal, distFile, distFile)
+ }
}
}}
}
diff --git a/apex/apex.go b/apex/apex.go
index f127757..261284c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -55,7 +55,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()
@@ -120,6 +120,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.
@@ -353,6 +359,11 @@
lintReports android.Paths
prebuiltFileToDelete string
+
+ isCompressed bool
+
+ // Path of API coverage generate file
+ coverageOutputPath android.ModuleOutPath
}
// apexFileClass represents a type of file that can be included in APEX.
@@ -480,12 +491,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
@@ -519,11 +530,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
@@ -718,22 +726,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
@@ -812,6 +820,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?
@@ -907,7 +934,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
@@ -1519,6 +1546,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 {
@@ -1663,7 +1693,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
@@ -1678,6 +1708,24 @@
// 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.
+ 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
}
@@ -1694,6 +1742,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
@@ -2013,7 +2063,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
}
@@ -2126,7 +2178,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
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a94e3b4..f71e7ef 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -15,6 +15,7 @@
package apex
import (
+ "fmt"
"io/ioutil"
"os"
"path"
@@ -346,6 +347,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 +363,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 +403,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 +458,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 +587,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 +602,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")
@@ -1782,6 +1814,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) {
@@ -2831,18 +2888,18 @@
// non-APEX variant does not have __ANDROID_APEX__ defined
mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=10000")
+ ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=10000")
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
// APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined
mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"]
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=29")
+ ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__=29")
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
// When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and
@@ -2864,10 +2921,10 @@
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
- // recovery variant does not set __ANDROID_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
// When a dependency of a cc_library sets use_apex_name_macro: true each apex gets a unique
// variant.
@@ -2888,10 +2945,10 @@
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
- // recovery variant does not set __ANDROID_SDK_VERSION__
+ // recovery variant does not set __ANDROID_APEX_MIN_SDK_VERSION__
mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
- ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__")
+ ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MIN_SDK_VERSION__")
}
func TestHeaderLibsDependency(t *testing.T) {
@@ -5635,6 +5692,13 @@
],
}
`
+
+ testDexpreoptWithApexes(t, bp, errmsg, transformDexpreoptConfig)
+}
+
+func testDexpreoptWithApexes(t *testing.T, bp, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+ t.Helper()
+
bp += cc.GatherRequiredDepsForTest(android.Android)
bp += java.GatherRequiredDepsForTest()
bp += dexpreopt.BpToolModulesForTest()
@@ -5964,9 +6028,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 +6056,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 +6278,294 @@
`)
}
+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.IsSkipInstall() {
+ 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)
+ }
+ }
+ })
+ }
+ })
+ }
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/apex/builder.go b/apex/builder.go
index b858135..9db8e59 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -37,6 +37,7 @@
func init() {
pctx.Import("android/soong/android")
+ pctx.Import("android/soong/cc/config")
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
@@ -65,6 +66,8 @@
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")
}
var (
@@ -176,6 +179,12 @@
Description: "Diff ${image_content_file} and ${allowed_files_file}",
}, "image_content_file", "allowed_files_file", "apex_module_name")
+ generateAPIsUsedbyApexRule = pctx.StaticRule("generateAPIsUsedbyApexRule", blueprint.RuleParams{
+ Command: "$genNdkUsedbyApexPath ${image_dir} ${readelf} ${out}",
+ CommandDeps: []string{"${genNdkUsedbyApexPath}"},
+ Description: "Generate symbol list used by Apex",
+ }, "image_dir", "readelf")
+
// Don't add more rules here. Consider using android.NewRuleBuilder instead.
)
@@ -262,7 +271,7 @@
}
output := android.PathForModuleOut(ctx, "file_contexts")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
switch a.properties.ApexType {
case imageApex:
@@ -294,7 +303,7 @@
panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
}
- rule.Build(pctx, ctx, "file_contexts."+a.Name(), "Generate file_contexts")
+ rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
return output.OutputPath
}
@@ -329,14 +338,14 @@
// included in the APEX without actually downloading and extracting it.
func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
output := android.PathForModuleOut(ctx, "installed-files.txt")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Implicit(builtApex).
Text("(cd " + imageDir.String() + " ; ").
Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") ").
Text(" | sort -nr > ").
Output(output)
- rule.Build(pctx, ctx, "installed-files."+a.Name(), "Installed files")
+ rule.Build("installed-files."+a.Name(), "Installed files")
return output.OutputPath
}
@@ -675,6 +684,22 @@
Description: "apex proto convert",
})
+ implicitInputs = append(implicitInputs, unsignedOutputFile)
+
+ // Run coverage analysis
+ apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+".txt")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: generateAPIsUsedbyApexRule,
+ Implicits: implicitInputs,
+ Description: "coverage",
+ Output: apisUsedbyOutputFile,
+ Args: map[string]string{
+ "image_dir": imageDir.String(),
+ "readelf": "${config.ClangBin}/llvm-readelf",
+ },
+ })
+ a.coverageOutputPath = apisUsedbyOutputFile
+
bundleConfig := a.buildBundleConfig(ctx)
var abis []string
@@ -714,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
@@ -726,16 +751,47 @@
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
+ compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, true)
+ if compressionEnabled && apexType == imageApex {
+ 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")
+ 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() {
diff --git a/bazel/Android.bp b/bazel/Android.bp
index 0113726..d557be5 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -2,6 +2,7 @@
name: "soong-bazel",
pkgPath: "android/soong/bazel",
srcs: [
+ "constants.go",
"properties.go",
],
pluginFor: [
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/cc/Android.bp b/cc/Android.bp
index ff2cdf3..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",
@@ -72,6 +73,8 @@
"vendor_public_library.go",
"testing.go",
+
+ "stub_library.go",
],
testSrcs: [
"cc_test.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index d32e4de..187a2ff 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
@@ -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)
@@ -334,8 +339,7 @@
entries.Class = "NATIVE_TESTS"
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
if len(benchmark.Properties.Test_suites) > 0 {
- entries.SetString("LOCAL_COMPATIBILITY_SUITE",
- strings.Join(benchmark.Properties.Test_suites, " "))
+ entries.AddCompatibilityTestSuites(benchmark.Properties.Test_suites...)
}
if benchmark.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", benchmark.testConfig.String())
@@ -360,8 +364,7 @@
}
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
if len(test.Properties.Test_suites) > 0 {
- entries.SetString("LOCAL_COMPATIBILITY_SUITE",
- strings.Join(test.Properties.Test_suites, " "))
+ entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
}
if test.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
@@ -576,7 +579,7 @@
}
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 fbd293e..fa3966f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -373,7 +373,7 @@
if String(binary.Properties.Prefix_symbols) != "" {
afterPrefixSymbols := outputFile
outputFile = android.PathForModuleOut(ctx, "unprefixed", fileName)
- TransformBinaryPrefixSymbols(ctx, String(binary.Properties.Prefix_symbols), outputFile,
+ transformBinaryPrefixSymbols(ctx, String(binary.Properties.Prefix_symbols), outputFile,
builderFlags, afterPrefixSymbols)
}
@@ -428,13 +428,13 @@
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
// Register link action.
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
builderFlags, outputFile, nil)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
- binary.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
+ binary.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
// Need to determine symlinks early since some targets (ie APEX) need this
// information but will not call 'install'
diff --git a/cc/builder.go b/cc/builder.go
index 81c09b1..9cd78d5 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -19,7 +19,6 @@
// functions.
import (
- "fmt"
"path/filepath"
"runtime"
"strings"
@@ -40,6 +39,7 @@
var (
pctx = android.NewPackageContext("android/soong/cc")
+ // Rule to invoke gcc with given command, flags, and dependencies. Outputs a .d depfile.
cc = pctx.AndroidRemoteStaticRule("cc", android.RemoteRuleSupports{Goma: true, RBE: true},
blueprint.RuleParams{
Depfile: "${out}.d",
@@ -49,6 +49,7 @@
},
"ccCmd", "cFlags")
+ // Rule to invoke gcc with given command and flags, but no dependencies.
ccNoDeps = pctx.AndroidStaticRule("ccNoDeps",
blueprint.RuleParams{
Command: "$relPwd $ccCmd -c $cFlags -o $out $in",
@@ -56,6 +57,8 @@
},
"ccCmd", "cFlags")
+ // Rules to invoke ld to link binaries. Uses a .rsp file to list dependencies, as there may
+ // be many.
ld, ldRE = remoteexec.StaticRules(pctx, "ld",
blueprint.RuleParams{
Command: "$reTemplate$ldCmd ${crtBegin} @${out}.rsp " +
@@ -76,6 +79,7 @@
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
}, []string{"ldCmd", "crtBegin", "libFlags", "crtEnd", "ldFlags", "extraLibFlags"}, []string{"implicitInputs", "implicitOutputs"})
+ // Rules for .o files to combine to other .o files, using ld partial linking.
partialLd, partialLdRE = remoteexec.StaticRules(pctx, "partialLd",
blueprint.RuleParams{
// Without -no-pie, clang 7.0 adds -pie to link Android files,
@@ -91,6 +95,7 @@
Platform: map[string]string{remoteexec.PoolKey: "${config.RECXXLinksPool}"},
}, []string{"ldCmd", "ldFlags"}, []string{"implicitInputs", "inCommaList", "implicitOutputs"})
+ // Rule to invoke `ar` with given cmd and flags, but no static library depenencies.
ar = pctx.AndroidStaticRule("ar",
blueprint.RuleParams{
Command: "rm -f ${out} && $arCmd $arFlags $out @${out}.rsp",
@@ -100,6 +105,8 @@
},
"arCmd", "arFlags")
+ // Rule to invoke `ar` with given cmd, flags, and library dependencies. Generates a .a
+ // (archive) file from .o files.
arWithLibs = pctx.AndroidStaticRule("arWithLibs",
blueprint.RuleParams{
Command: "rm -f ${out} && $arCmd $arObjFlags $out @${out}.rsp && $arCmd $arLibFlags $out $arLibs",
@@ -109,12 +116,7 @@
},
"arCmd", "arObjFlags", "arObjs", "arLibFlags", "arLibs")
- darwinStrip = pctx.AndroidStaticRule("darwinStrip",
- blueprint.RuleParams{
- Command: "${config.MacStripPath} -u -r -o $out $in",
- CommandDeps: []string{"${config.MacStripPath}"},
- })
-
+ // Rule to run objcopy --prefix-symbols (to prefix all symbols in a file with a given string).
prefixSymbols = pctx.AndroidStaticRule("prefixSymbols",
blueprint.RuleParams{
Command: "$objcopyCmd --prefix-symbols=${prefix} ${in} ${out}",
@@ -125,6 +127,24 @@
_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
_ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
+ // Rule to invoke `strip` (to discard symbols and data from object files).
+ strip = pctx.AndroidStaticRule("strip",
+ blueprint.RuleParams{
+ Depfile: "${out}.d",
+ Deps: blueprint.DepsGCC,
+ Command: "CROSS_COMPILE=$crossCompile XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
+ CommandDeps: []string{"$stripPath", "$xzCmd"},
+ Pool: darwinStripPool,
+ },
+ "args", "crossCompile")
+
+ // Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture.
+ darwinStrip = pctx.AndroidStaticRule("darwinStrip",
+ blueprint.RuleParams{
+ Command: "${config.MacStripPath} -u -r -o $out $in",
+ CommandDeps: []string{"${config.MacStripPath}"},
+ })
+
// b/132822437: objcopy uses a file descriptor per .o file when called on .a files, which runs the system out of
// file descriptors on darwin. Limit concurrent calls to 5 on darwin.
darwinStripPool = func() blueprint.Pool {
@@ -137,18 +157,9 @@
}
}()
- strip = pctx.AndroidStaticRule("strip",
- blueprint.RuleParams{
- Depfile: "${out}.d",
- Deps: blueprint.DepsGCC,
- Command: "CROSS_COMPILE=$crossCompile XZ=$xzCmd CLANG_BIN=${config.ClangBin} $stripPath ${args} -i ${in} -o ${out} -d ${out}.d",
- CommandDeps: []string{"$stripPath", "$xzCmd"},
- Pool: darwinStripPool,
- },
- "args", "crossCompile")
-
_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
+ // Rule to repack an archive (.a) file with a subset of object files.
archiveRepack = pctx.AndroidStaticRule("archiveRepack",
blueprint.RuleParams{
Depfile: "${out}.d",
@@ -158,6 +169,7 @@
},
"objects")
+ // Rule to create an empty file at a given path.
emptyFile = pctx.AndroidStaticRule("emptyFile",
blueprint.RuleParams{
Command: "rm -f $out && touch $out",
@@ -165,6 +177,7 @@
_ = pctx.SourcePathVariable("tocPath", "build/soong/scripts/toc.sh")
+ // A rule for extracting a table of contents from a shared library (.so).
toc = pctx.AndroidStaticRule("toc",
blueprint.RuleParams{
Depfile: "${out}.d",
@@ -175,6 +188,7 @@
},
"crossCompile", "format")
+ // Rule for invoking clang-tidy (a clang-based linter).
clangTidy, clangTidyRE = remoteexec.StaticRules(pctx, "clangTidy",
blueprint.RuleParams{
Command: "rm -f $out && $reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && touch $out",
@@ -193,6 +207,7 @@
_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
+ // Rule for invoking yasm to compile .asm assembly files.
yasm = pctx.AndroidStaticRule("yasm",
blueprint.RuleParams{
Command: "$yasmCmd $asFlags -o $out $in && $yasmCmd $asFlags -M $in >$out.d",
@@ -202,6 +217,7 @@
},
"asFlags")
+ // Rule to invoke windres, for interaction with Windows resources.
windres = pctx.AndroidStaticRule("windres",
blueprint.RuleParams{
Command: "$windresCmd $flags -I$$(dirname $in) -i $in -o $out --preprocessor \"${config.ClangBin}/clang -E -xc-header -DRC_INVOKED\"",
@@ -219,14 +235,17 @@
}, &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}",
+ remoteexec.PoolKey: "${config.RECXXPool}",
},
}, []string{"cFlags", "exportDirs"}, nil)
_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
_ = pctx.SourcePathVariable("sAbiLinkerLibs", "prebuilts/clang-tools/${config.HostPrebuiltTag}/lib64")
+ // Rule to combine .dump sAbi dump files from multiple source files into a single .ldump
+ // sAbi dump file.
sAbiLink, sAbiLinkRE = remoteexec.StaticRules(pctx, "sAbiLink",
blueprint.RuleParams{
Command: "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch $exportedHeaderFlags @${out}.rsp ",
@@ -245,6 +264,7 @@
_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
+ // Rule to compare linked sAbi dump files (.ldump).
sAbiDiff = pctx.RuleFunc("sAbiDiff",
func(ctx android.PackageRuleContext) blueprint.RuleParams {
commandStr := "($sAbiDiffer ${extraFlags} -lib ${libName} -arch ${arch} -o ${out} -new ${in} -old ${referenceDump})"
@@ -258,11 +278,13 @@
},
"extraFlags", "referenceDump", "libName", "arch", "createReferenceDumpFlags")
+ // Rule to unzip a reference abi dump.
unzipRefSAbiDump = pctx.AndroidStaticRule("unzipRefSAbiDump",
blueprint.RuleParams{
Command: "gunzip -c $in > $out",
})
+ // Rule to zip files.
zip = pctx.AndroidStaticRule("zip",
blueprint.RuleParams{
Command: "${SoongZipCmd} -o ${out} -C $$OUT_DIR -r ${out}.rsp",
@@ -278,6 +300,8 @@
func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
_ = pctx.VariableFunc("kytheCuEncoding",
func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
+
+ // Rule to use kythe extractors to generate .kzip files, used to build code cross references.
kytheExtract = pctx.StaticRule("kythe",
blueprint.RuleParams{
Command: `rm -f $out && ` +
@@ -310,7 +334,11 @@
pctx.Import("android/soong/remoteexec")
}
+// builderFlags contains various types of command line flags (and settings) for use in building
+// build statements related to C++.
type builderFlags struct {
+ // Global flags (which build system or toolchain is responsible for). These are separate from
+ // local flags because they should appear first (so that they may be overridden by local flags).
globalCommonFlags string
globalAsFlags string
globalYasmFlags string
@@ -321,6 +349,7 @@
globalCppFlags string
globalLdFlags string
+ // Local flags (which individual modules are responsible for). These may override global flags.
localCommonFlags string
localAsFlags string
localYasmFlags string
@@ -331,32 +360,37 @@
localCppFlags string
localLdFlags string
- libFlags string
- extraLibFlags string
- tidyFlags string
- sAbiFlags string
- aidlFlags string
- rsFlags string
+ libFlags string // Flags to add to the linker directly after specifying libraries to link.
+ extraLibFlags string // Flags to add to the linker last.
+ tidyFlags string // Flags that apply to clang-tidy
+ sAbiFlags string // Flags that apply to header-abi-dumps
+ aidlFlags string // Flags that apply to aidl source files
+ rsFlags string // Flags that apply to renderscript source files
toolchain config.Toolchain
- tidy bool
- gcovCoverage bool
- sAbiDump bool
- emitXrefs bool
- assemblerWithCpp bool
+ // True if these extra features are enabled.
+ tidy bool
+ gcovCoverage bool
+ sAbiDump bool
+ emitXrefs bool
+
+ assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
systemIncludeFlags string
+ // True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
groupStaticLibs bool
proto android.ProtoFlags
- protoC bool
- protoOptionsFile bool
+ protoC bool // If true, compile protos as `.c` files. Otherwise, output as `.cc`.
+ protoOptionsFile bool // If true, output a proto options file.
yacc *YaccProperties
lex *LexProperties
}
+// StripFlags represents flags related to stripping. This is separate from builderFlags, as these
+// flags are useful outside of this package (such as for Rust).
type StripFlags struct {
Toolchain config.Toolchain
StripKeepSymbols bool
@@ -367,6 +401,7 @@
StripUseGnuStrip bool
}
+// Objects is a collection of file paths corresponding to outputs for C++ related build statements.
type Objects struct {
objFiles android.Paths
tidyFiles android.Paths
@@ -396,9 +431,10 @@
}
// Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func TransformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths,
+func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles android.Paths,
flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+ // Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
objFiles := make(android.Paths, len(srcFiles))
var tidyFiles android.Paths
if flags.tidy {
@@ -468,6 +504,7 @@
objFiles[i] = objFile
+ // Register compilation build statements. The actual rule used depends on the source file type.
switch srcFile.Ext() {
case ".asm":
ctx.Build(pctx, android.BuildParams{
@@ -532,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
}
@@ -562,6 +602,7 @@
},
})
+ // Register post-process build statements (such as for tidy or kythe).
if emitXref {
kytheFile := android.ObjPathWithExt(ctx, subdir, srcFile, "kzip")
ctx.Build(pctx, android.BuildParams{
@@ -639,7 +680,7 @@
}
// Generate a rule for compiling multiple .o files to a static library (.a)
-func TransformObjToStaticLib(ctx android.ModuleContext,
+func transformObjToStaticLib(ctx android.ModuleContext,
objFiles android.Paths, wholeStaticLibs android.Paths,
flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
@@ -682,7 +723,7 @@
// Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
// and shared libraries, to a shared library (.so) or dynamic executable
-func TransformObjToDynamicBinary(ctx android.ModuleContext,
+func transformObjToDynamicBinary(ctx android.ModuleContext,
objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
@@ -763,7 +804,7 @@
// Generate a rule to combine .dump sAbi dump files from multiple source files
// into a single .ldump sAbi dump file
-func TransformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
+func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path,
baseName, exportedHeaderFlags string, symbolFile android.OptionalPath,
excludedSymbolVersions, excludedSymbolTags []string) android.OptionalPath {
@@ -810,7 +851,8 @@
return android.OptionalPathForPath(outputFile)
}
-func UnzipRefDump(ctx android.ModuleContext, zippedRefDump android.Path, baseName string) android.Path {
+// unzipRefDump registers a build statement to unzip a reference abi dump.
+func unzipRefDump(ctx android.ModuleContext, zippedRefDump android.Path, baseName string) android.Path {
outputFile := android.PathForModuleOut(ctx, baseName+"_ref.lsdump")
ctx.Build(pctx, android.BuildParams{
Rule: unzipRefSAbiDump,
@@ -821,7 +863,8 @@
return outputFile
}
-func SourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
+// sourceAbiDiff registers a build statement to compare linked sAbi dump files (.ldump).
+func sourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceDump android.Path,
baseName, exportedHeaderFlags string, checkAllApis, isLlndk, isNdk, isVndkExt bool) android.OptionalPath {
outputFile := android.PathForModuleOut(ctx, baseName+".abidiff")
@@ -872,7 +915,7 @@
}
// Generate a rule for extracting a table of contents from a shared library (.so)
-func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
+func transformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath, flags builderFlags) {
var format string
@@ -901,7 +944,7 @@
}
// Generate a rule for compiling multiple .o files to a .o using ld partial linking
-func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
+func transformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
flags builderFlags, outputFile android.WritablePath, deps android.Paths) {
ldCmd := "${config.ClangBin}/clang++"
@@ -926,8 +969,8 @@
})
}
-// Generate a rule for runing objcopy --prefix-symbols on a binary
-func TransformBinaryPrefixSymbols(ctx android.ModuleContext, prefix string, inputFile android.Path,
+// Generate a rule for running objcopy --prefix-symbols on a binary
+func transformBinaryPrefixSymbols(ctx android.ModuleContext, prefix string, inputFile android.Path,
flags builderFlags, outputFile android.WritablePath) {
objcopyCmd := gccCmd(flags.toolchain, "objcopy")
@@ -944,7 +987,8 @@
})
}
-func TransformStrip(ctx android.ModuleContext, inputFile android.Path,
+// Registers a build statement to invoke `strip` (to discard symbols and data from object files).
+func transformStrip(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath, flags StripFlags) {
crossCompile := gccCmd(flags.Toolchain, "")
@@ -980,7 +1024,8 @@
})
}
-func TransformDarwinStrip(ctx android.ModuleContext, inputFile android.Path,
+// Registers build statement to invoke `strip` on darwin architecture.
+func transformDarwinStrip(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath) {
ctx.Build(pctx, android.BuildParams{
@@ -991,7 +1036,8 @@
})
}
-func TransformCoverageFilesToZip(ctx android.ModuleContext,
+// Registers build statement to zip one or more coverage files.
+func transformCoverageFilesToZip(ctx android.ModuleContext,
inputs Objects, baseName string) android.OptionalPath {
if len(inputs.coverageFiles) > 0 {
@@ -1010,7 +1056,8 @@
return android.OptionalPath{}
}
-func TransformArchiveRepack(ctx android.ModuleContext, inputFile android.Path,
+// Rule to repack an archive (.a) file with a subset of object files.
+func transformArchiveRepack(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath, objects []string) {
ctx.Build(pctx, android.BuildParams{
@@ -1027,33 +1074,3 @@
func gccCmd(toolchain config.Toolchain, cmd string) string {
return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
}
-
-func splitListForSize(list android.Paths, limit int) (lists []android.Paths, err error) {
- var i int
-
- start := 0
- bytes := 0
- for i = range list {
- l := len(list[i].String())
- if l > limit {
- return nil, fmt.Errorf("list element greater than size limit (%d)", limit)
- }
- if bytes+l > limit {
- lists = append(lists, list[start:i])
- start = i
- bytes = 0
- }
- bytes += l + 1 // count a space between each list element
- }
-
- lists = append(lists, list[start:])
-
- totalLen := 0
- for _, l := range lists {
- totalLen += len(l)
- }
- if totalLen != len(list) {
- panic(fmt.Errorf("Failed breaking up list, %d != %d", len(list), totalLen))
- }
- return lists, nil
-}
diff --git a/cc/cc.go b/cc/cc.go
index 6deb1b4..89f32f1 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -79,7 +79,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,9 +87,22 @@
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)
}
+// Deps is a struct containing module names of dependencies, separated by the kind of dependency.
+// Mutators should use `AddVariationDependencies` or its sibling methods to add actual dependency
+// edges to these modules.
+// This object is constructed in DepsMutator, by calling to various module delegates to set
+// relevant fields. For example, `module.compiler.compilerDeps()` may append type-specific
+// dependencies.
+// This is then consumed by the same DepsMutator, which will call `ctx.AddVariationDependencies()`
+// (or its sibling methods) to set real dependencies on the given modules.
type Deps struct {
SharedLibs, LateSharedLibs []string
StaticLibs, LateStaticLibs, WholeStaticLibs []string
@@ -103,6 +115,7 @@
// Used by DepsMutator to pass system_shared_libs information to check_elf_file.py.
SystemSharedLibs []string
+ // If true, statically link the unwinder into native libraries/binaries.
StaticUnwinderIfLegacy bool
ReexportSharedLibHeaders, ReexportStaticLibHeaders, ReexportHeaderLibHeaders []string
@@ -120,8 +133,16 @@
// 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.
+// It's constructed in depsToPath() by traversing the direct dependencies of the current module.
+// It's used to construct flags for various build statements (such as for compiling and linking).
+// It is then passed to module decorator functions responsible for registering build statements
+// (such as `module.compiler.compile()`).`
type PathDeps struct {
// Paths to .so files
SharedLibs, EarlySharedLibs, LateSharedLibs android.Paths
@@ -182,8 +203,12 @@
LdFlags []string // Flags that apply to linker command lines
}
+// Flags contains various types of command line flags (and settings) for use in building build
+// statements related to C++.
type Flags struct {
- Local LocalOrGlobalFlags
+ // Local flags (which individual modules are responsible for). These may override global flags.
+ Local LocalOrGlobalFlags
+ // Global flags (which build system or toolchain is responsible for).
Global LocalOrGlobalFlags
aidlFlags []string // Flags that apply to aidl source files
@@ -198,19 +223,23 @@
SystemIncludeFlags []string
Toolchain config.Toolchain
- Tidy bool
- GcovCoverage bool
- SAbiDump bool
+ Tidy bool // True if clang-tidy is enabled.
+ GcovCoverage bool // True if coverage files should be generated.
+ SAbiDump bool // True if header abi dumps should be generated.
EmitXrefs bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+ // The instruction set required for clang ("arm" or "thumb").
RequiredInstructionSet string
- DynamicLinker string
+ // The target-device system path to the dynamic linker.
+ DynamicLinker string
CFlagsDeps android.Paths // Files depended on by compiler flags
LdFlagsDeps android.Paths // Files depended on by linker flags
+ // True if .s files should be processed with the c preprocessor.
AssemblerWithCpp bool
- GroupStaticLibs bool
+ // True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
+ GroupStaticLibs bool
proto android.ProtoFlags
protoC bool // Whether to use C instead of C++
@@ -310,6 +339,11 @@
// framework module from a snapshot.
Exclude_from_vendor_snapshot *bool
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 {
@@ -358,6 +392,10 @@
Double_loadable *bool
}
+// ModuleContextIntf is an interface (on a module context helper) consisting of functions related
+// to understanding details about the type of the current module.
+// For example, one might call these functions to determine whether the current module is a static
+// library and/or is installed in vendor directories.
type ModuleContextIntf interface {
static() bool
staticBinary() bool
@@ -375,13 +413,12 @@
isVndkPrivate(config android.Config) 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
@@ -412,6 +449,8 @@
ModuleContextIntf
}
+// feature represents additional (optional) steps to building cc-related modules, such as invocation
+// of clang-tidy.
type feature interface {
begin(ctx BaseModuleContext)
deps(ctx DepsContext, deps Deps) Deps
@@ -419,6 +458,9 @@
props() []interface{}
}
+// compiler is the interface for a compiler helper object. Different module decorators may implement
+// this helper differently. For example, compiling a `cc_library` may use a different build
+// statement than building a `toolchain_library`.
type compiler interface {
compilerInit(ctx BaseModuleContext)
compilerDeps(ctx DepsContext, deps Deps) Deps
@@ -430,6 +472,9 @@
compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects
}
+// linker is the interface for a linker decorator object. Individual module types can provide
+// their own implementation for this decorator, and thus specify custom logic regarding build
+// statements pertaining to linking.
type linker interface {
linkerInit(ctx BaseModuleContext)
linkerDeps(ctx DepsContext, deps Deps) Deps
@@ -445,15 +490,19 @@
coverageOutputFilePath() android.OptionalPath
// Get the deps that have been explicitly specified in the properties.
- // Only updates the
linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps
}
+// specifiedDeps is a tuple struct representing dependencies of a linked binary owned by the linker.
type specifiedDeps struct {
- sharedLibs []string
- systemSharedLibs []string // Note nil and [] are semantically distinct.
+ sharedLibs []string
+ // Note nil and [] are semantically distinct. [] prevents linking against the defaults (usually
+ // libc, libm, etc.)
+ systemSharedLibs []string
}
+// installer is the interface for an installer helper object. This helper is responsible for
+// copying build outputs to the appropriate locations so that they may be installed on device.
type installer interface {
installerProps() []interface{}
install(ctx ModuleContext, path android.Path)
@@ -534,6 +583,9 @@
staticUnwinder bool
makeSuffix string
+
+ // 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.
@@ -584,7 +636,7 @@
genHeaderExportDepTag = dependencyTag{name: "gen header export"}
objDepTag = dependencyTag{name: "obj"}
linkerFlagsDepTag = dependencyTag{name: "linker flags file"}
- dynamicLinkerDepTag = dependencyTag{name: "dynamic linker"}
+ dynamicLinkerDepTag = installDependencyTag{name: "dynamic linker"}
reuseObjTag = dependencyTag{name: "reuse objects"}
staticVariantTag = dependencyTag{name: "static variant"}
vndkExtDepTag = dependencyTag{name: "vndk extends"}
@@ -626,7 +678,18 @@
// Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
-// to construct the output file. Behavior can be customized with a Customizer interface
+// to construct the output file. Behavior can be customized with a Customizer, or "decorator",
+// interface.
+//
+// To define a C/C++ related module, construct a new Module object and point its delegates to
+// type-specific structs. These delegates will be invoked to register module-specific build
+// statements which may be unique to the module type. For example, module.compiler.compile() should
+// be defined so as to register build statements which are responsible for compiling the module.
+//
+// Another example: to construct a cc_binary module, one can create a `cc.binaryDecorator` struct
+// which implements the `linker` and `installer` interfaces, and points the `linker` and `installer`
+// members of the cc.Module to this decorator. Thus, a cc_binary module has custom linker and
+// installer logic.
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -643,18 +706,23 @@
// Allowable SdkMemberTypes of this module type.
sdkMemberTypes []android.SdkMemberType
- // delegates, initialize before calling Init
- features []feature
+ // decorator delegates, initialize before calling Init
+ // these may contain module-specific implementations, and effectively allow for custom
+ // type-specific logic. These members may reference different objects or the same object.
+ // Functions of these decorators will be invoked to initialize and register type-specific
+ // build statements.
compiler compiler
linker linker
installer installer
- stl *stl
- sanitize *sanitize
- coverage *coverage
- sabi *sabi
- vndkdep *vndkdep
- lto *lto
- pgo *pgo
+
+ features []feature
+ stl *stl
+ sanitize *sanitize
+ coverage *coverage
+ sabi *sabi
+ vndkdep *vndkdep
+ lto *lto
+ pgo *pgo
library libraryInterface
@@ -970,7 +1038,7 @@
return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
}
-func (c *Module) isVndkPrivate(config android.Config) bool {
+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)
}
@@ -1003,7 +1071,7 @@
return false
}
-func (c *Module) isVndkExt() bool {
+func (c *Module) IsVndkExt() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndkExt()
}
@@ -1045,6 +1113,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)
}
@@ -1074,7 +1155,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
@@ -1174,7 +1255,7 @@
}
func (ctx *moduleContextImpl) isVndkPrivate(config android.Config) bool {
- return ctx.mod.isVndkPrivate(config)
+ return ctx.mod.IsVndkPrivate(config)
}
func (ctx *moduleContextImpl) isVndk() bool {
@@ -1193,50 +1274,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
@@ -1347,7 +1392,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 {
@@ -1381,7 +1426,7 @@
c.hideApexVariantFromMake = true
}
- c.makeLinkType = c.getMakeLinkType(actx)
+ c.makeLinkType = GetMakeLinkType(actx, c)
c.Properties.SubName = ""
@@ -1508,13 +1553,14 @@
}
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
@@ -1523,7 +1569,7 @@
// 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) {
+ if shouldCollectHeadersForSnapshot(ctx, c, apexInfo) {
i.collectHeadersForSnapshot(ctx)
}
}
@@ -1814,13 +1860,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() {
@@ -1843,7 +1882,7 @@
lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
- if buildStubs {
+ if c.IsStubs() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
depTag, lib)
} else {
@@ -1851,12 +1890,6 @@
}
}
- 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
@@ -1883,6 +1916,9 @@
if inList(lib, deps.ReexportStaticLibHeaders) {
depTag.reexportFlags = true
}
+ if inList(lib, deps.ExcludeLibsForApex) {
+ depTag.excludeInApex = true
+ }
if impl, ok := syspropImplLibraries[lib]; ok {
lib = impl
@@ -1920,6 +1956,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
@@ -2030,7 +2069,7 @@
return
}
- if from.Module().Target().Os != android.Android {
+ if from.Target().Os != android.Android {
// Host code is not restricted
return
}
@@ -2044,6 +2083,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")
}
@@ -2170,6 +2214,11 @@
return false
}
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if IsHeaderDepTag(depTag) {
+ 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.
@@ -2203,8 +2252,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]
@@ -2215,7 +2264,7 @@
var err error
ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version)
if err != nil {
- return SharedLibraryStubsInfo{}, err
+ return SharedStubLibrary{}, err
}
}
if ver.LessThanOrEqualTo(maxSdkVersion) {
@@ -2226,7 +2275,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
@@ -2349,6 +2398,10 @@
return
}
+ if !apexInfo.IsForPlatform() && libDepTag.excludeInApex {
+ return
+ }
+
depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
var ptr *android.Paths
@@ -2358,7 +2411,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() {
@@ -2368,10 +2428,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
@@ -2386,7 +2447,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.
@@ -2406,7 +2467,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
@@ -2416,6 +2477,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
@@ -2443,6 +2510,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 {
@@ -2570,7 +2644,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(
@@ -2648,7 +2724,7 @@
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
}
@@ -2695,7 +2771,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
@@ -2807,22 +2883,24 @@
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 ccModule, ok := c.Module().(*Module); ok {
+ // Only CC modules provide stubs at the moment.
+ if lib, ok := ccModule.linker.(*llndkStubDecorator); ok {
+ if Bool(lib.Properties.Vendor_available) {
+ return "native:vndk"
+ }
+ return "native:vndk_private"
}
- return "native:vndk_private"
}
- 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(actx.Config()) {
+ return "native:vndk_private"
}
- return "native:vndk_private"
+ return "native:vndk"
}
- if c.inProduct() {
+ if c.InProduct() {
return "native:product"
}
return "native:vendor"
@@ -2832,7 +2910,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)
@@ -2868,13 +2946,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 {
@@ -2948,6 +3020,10 @@
// linked; the dependency is used only during the compilation phase.
return false
}
+
+ if isLibDepTag && libDepTag.excludeInApex {
+ return false
+ }
}
if depTag == stubImplDepTag || depTag == llndkImplDep {
// We don't track beyond LLNDK or from an implementation library to its stubs.
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7c98585..c16cce8 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -250,8 +250,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 {
@@ -2913,114 +2913,6 @@
}
}
-var (
- str11 = "01234567891"
- str10 = str11[:10]
- str9 = str11[:9]
- str5 = str11[:5]
- str4 = str11[:4]
-)
-
-var splitListForSizeTestCases = []struct {
- in []string
- out [][]string
- size int
-}{
- {
- in: []string{str10},
- out: [][]string{{str10}},
- size: 10,
- },
- {
- in: []string{str9},
- out: [][]string{{str9}},
- size: 10,
- },
- {
- in: []string{str5},
- out: [][]string{{str5}},
- size: 10,
- },
- {
- in: []string{str11},
- out: nil,
- size: 10,
- },
- {
- in: []string{str10, str10},
- out: [][]string{{str10}, {str10}},
- size: 10,
- },
- {
- in: []string{str9, str10},
- out: [][]string{{str9}, {str10}},
- size: 10,
- },
- {
- in: []string{str10, str9},
- out: [][]string{{str10}, {str9}},
- size: 10,
- },
- {
- in: []string{str5, str4},
- out: [][]string{{str5, str4}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5},
- out: [][]string{{str5, str4}, {str5}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5, str4},
- out: [][]string{{str5, str4}, {str5, str4}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5, str5},
- out: [][]string{{str5, str4}, {str5}, {str5}},
- size: 10,
- },
- {
- in: []string{str5, str5, str5, str4},
- out: [][]string{{str5}, {str5}, {str5, str4}},
- size: 10,
- },
- {
- in: []string{str9, str11},
- out: nil,
- size: 10,
- },
- {
- in: []string{str11, str9},
- out: nil,
- size: 10,
- },
-}
-
-func TestSplitListForSize(t *testing.T) {
- for _, testCase := range splitListForSizeTestCases {
- out, _ := splitListForSize(android.PathsForTesting(testCase.in...), testCase.size)
-
- var outStrings [][]string
-
- if len(out) > 0 {
- outStrings = make([][]string, len(out))
- for i, o := range out {
- outStrings[i] = o.Strings()
- }
- }
-
- if !reflect.DeepEqual(outStrings, testCase.out) {
- t.Errorf("incorrect output:")
- t.Errorf(" input: %#v", testCase.in)
- t.Errorf(" size: %d", testCase.size)
- t.Errorf(" expected: %#v", testCase.out)
- t.Errorf(" got: %#v", outStrings)
- }
- }
-}
-
var staticLinkDepOrderTestCases = []struct {
// This is a string representation of a map[moduleName][]moduleDependency .
// It models the dependencies declared in an Android.bp file.
@@ -4164,3 +4056,35 @@
}
}
+
+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)
+ }
+}
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
index 855ff25..be46fc0 100644
--- a/cc/cflag_artifacts.go
+++ b/cc/cflag_artifacts.go
@@ -68,7 +68,7 @@
cleanedName := strings.Replace(flag, "=", "_", -1)
filename, filepath := s.incrementFile(ctx, cleanedName, part)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Textf("rm -f %s", filepath.String())
if using {
@@ -84,7 +84,7 @@
length := len(modules)
if length == 0 {
- rule.Build(pctx, ctx, filename, "gen "+filename)
+ rule.Build(filename, "gen "+filename)
part++
}
@@ -98,11 +98,11 @@
strings.Join(proptools.ShellEscapeList(shard), " ")).
FlagWithOutput(">> ", filepath).
Text("; done")
- rule.Build(pctx, ctx, filename, "gen "+filename)
+ rule.Build(filename, "gen "+filename)
if index+1 != len(moduleShards) {
filename, filepath = s.incrementFile(ctx, cleanedName, part+index+1)
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
rule.Command().Textf("rm -f %s", filepath.String())
}
}
@@ -121,14 +121,14 @@
for _, flag := range TrackedCFlags {
// Generate build rule to combine related intermediary files into a
// C Flag artifact
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
filename := s.genFlagFilename(flag)
outputpath := android.PathForOutput(ctx, "cflags", filename)
rule.Command().
Text("cat").
Inputs(s.interOutputs[flag].Paths()).
FlagWithOutput("> ", outputpath)
- rule.Build(pctx, ctx, filename, "gen "+filename)
+ rule.Build(filename, "gen "+filename)
s.outputs = append(s.outputs, outputpath)
}
}
diff --git a/cc/compiler.go b/cc/compiler.go
index 04ed80d..b78bb6c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -366,7 +366,7 @@
}
if ctx.Device() {
flags.Global.CommonFlags = append(flags.Global.CommonFlags,
- fmt.Sprintf("-D__ANDROID_SDK_VERSION__=%d",
+ fmt.Sprintf("-D__ANDROID_APEX_MIN_SDK_VERSION__=%d",
ctx.apexSdkVersion().FinalOrFutureInt()))
}
}
@@ -649,7 +649,7 @@
func compileObjs(ctx android.ModuleContext, flags builderFlags,
subdir string, srcFiles, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
- return TransformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
+ return transformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
}
var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 441bff2..519a9e2 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -249,6 +249,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/vndk.go b/cc/config/vndk.go
index 563ce76..4bcad4b 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -21,11 +21,11 @@
"android.hardware.automotive.occupant_awareness-ndk_platform",
"android.hardware.light-ndk_platform",
"android.hardware.identity-ndk_platform",
- "android.hardware.keymint-ndk_platform",
- "android.hardware.keymint-unstable-ndk_platform",
"android.hardware.nfc@1.2",
+ "android.hardware.memtrack-unstable-ndk_platform",
"android.hardware.power-ndk_platform",
"android.hardware.rebootescrow-ndk_platform",
+ "android.hardware.security.keymint-unstable-ndk_platform",
"android.hardware.vibrator-ndk_platform",
"android.system.keystore2-unstable-ndk_platform",
"libbinder",
diff --git a/cc/coverage.go b/cc/coverage.go
index aa1fdf6..acf98dd 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -149,6 +149,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 dddfe94..6b17c48 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -246,25 +246,25 @@
fuzz.binaryDecorator.baseInstaller.install(ctx, file)
fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
intermediateDir := android.PathForModuleOut(ctx, "corpus")
for _, entry := range fuzz.corpus {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Base()))
}
- builder.Build(pctx, ctx, "copy_corpus", "copy corpus")
+ builder.Build("copy_corpus", "copy corpus")
fuzz.corpusIntermediateDir = intermediateDir
fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
- builder = android.NewRuleBuilder()
+ builder = android.NewRuleBuilder(pctx, ctx)
intermediateDir = android.PathForModuleOut(ctx, "data")
for _, entry := range fuzz.data {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Rel()))
}
- builder.Build(pctx, ctx, "copy_data", "copy data")
+ builder.Build("copy_data", "copy data")
fuzz.dataIntermediateDir = intermediateDir
if fuzz.Properties.Dictionary != nil {
@@ -419,12 +419,12 @@
sharedLibraries := collectAllSharedDependencies(ctx, module)
var files []fileToZip
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
// Package the corpora into a zipfile.
if fuzzModule.corpus != nil {
corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", corpusZip)
command.FlagWithRspFileInputList("-r ", fuzzModule.corpus)
@@ -434,7 +434,7 @@
// Package the data into a zipfile.
if fuzzModule.data != nil {
dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", dataZip)
for _, f := range fuzzModule.data {
intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
@@ -492,7 +492,7 @@
}
fuzzZip := archDir.Join(ctx, module.Name()+".zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", fuzzZip)
for _, file := range files {
@@ -504,7 +504,7 @@
command.FlagWithInput("-f ", file.SourceFilePath)
}
- builder.Build(pctx, ctx, "create-"+fuzzZip.String(),
+ builder.Build("create-"+fuzzZip.String(),
"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
// Don't add modules to 'make haiku' that are set to not be exported to the
@@ -531,11 +531,11 @@
filesToZip := archDirs[archOs]
arch := archOs.arch
hostOrTarget := archOs.hostOrTarget
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip")
s.packages = append(s.packages, outputFile)
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", outputFile).
Flag("-L 0") // No need to try and re-compress the zipfiles.
@@ -549,7 +549,7 @@
command.FlagWithInput("-f ", fileToZip.SourceFilePath)
}
- builder.Build(pctx, ctx, "create-fuzz-package-"+arch+"-"+hostOrTarget,
+ builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
"Create fuzz target packages for "+arch+"-"+hostOrTarget)
}
}
diff --git a/cc/gen.go b/cc/gen.go
index 5895b31..75b9e49 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -75,10 +75,9 @@
cmd := rule.Command()
// Fix up #line markers to not use the sbox temporary directory
- // android.SboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
+ // android.sboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
// directory itself, without any filename appended.
- // TODO(ccross): make this cmd.PathForOutput(outDir) instead.
- sboxOutDir := android.SboxPathForOutput(outDir, outDir)
+ sboxOutDir := cmd.PathForOutput(outDir)
sedCmd := "sed -i.bak 's#" + sboxOutDir + "#" + outDir.String() + "#'"
rule.Command().Text(sedCmd).Input(outFile)
rule.Command().Text(sedCmd).Input(headerFile)
@@ -137,7 +136,7 @@
}
cmd := rule.Command()
- cmd.BuiltTool(ctx, "aidl-cpp").
+ cmd.BuiltTool("aidl-cpp").
FlagWithDepFile("-d", depFile).
Flag("--ninja").
Flag(aidlFlags).
@@ -232,7 +231,7 @@
var yaccRule_ *android.RuleBuilder
yaccRule := func() *android.RuleBuilder {
if yaccRule_ == nil {
- yaccRule_ = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "yacc"),
+ yaccRule_ = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "yacc"),
android.PathForModuleGen(ctx, "yacc.sbox.textproto"))
}
return yaccRule_
@@ -262,7 +261,7 @@
deps = append(deps, headerFile)
case ".aidl":
if aidlRule == nil {
- aidlRule = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "aidl"),
+ aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
}
cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
@@ -285,11 +284,11 @@
}
if aidlRule != nil {
- aidlRule.Build(pctx, ctx, "aidl", "gen aidl")
+ aidlRule.Build("aidl", "gen aidl")
}
if yaccRule_ != nil {
- yaccRule_.Build(pctx, ctx, "yacc", "gen yacc")
+ yaccRule_.Build("yacc", "gen yacc")
}
if len(rsFiles) > 0 {
diff --git a/cc/image.go b/cc/image.go
index 3d6769e..32325b9 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -41,7 +41,7 @@
return hostImageVariant
} else if c.inVendor() {
return vendorImageVariant
- } else if c.inProduct() {
+ } else if c.InProduct() {
return productImageVariant
} else if c.InRamdisk() {
return ramdiskImageVariant
@@ -67,7 +67,7 @@
func (ctx *moduleContext) ProductSpecific() bool {
//TODO(b/150902910): Replace HasNonSystemVariants() with HasProductVariant()
return ctx.ModuleContext.ProductSpecific() ||
- (ctx.mod.HasNonSystemVariants() && ctx.mod.inProduct())
+ (ctx.mod.HasNonSystemVariants() && ctx.mod.InProduct())
}
func (ctx *moduleContext) SocSpecific() bool {
@@ -76,7 +76,7 @@
}
func (ctx *moduleContextImpl) inProduct() bool {
- return ctx.mod.inProduct()
+ return ctx.mod.InProduct()
}
func (ctx *moduleContextImpl) inVendor() bool {
@@ -111,7 +111,7 @@
}
// 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
}
@@ -265,7 +265,7 @@
} 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
diff --git a/cc/library.go b/cc/library.go
index 7ae75f2..01fcb74 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -28,6 +28,7 @@
"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"`
@@ -115,14 +116,23 @@
Llndk_stubs *string
}
+// 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,16 +252,23 @@
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 {
@@ -261,25 +278,35 @@
}
}
+// 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: "+
@@ -457,6 +484,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 +505,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 +558,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 {
@@ -559,59 +594,12 @@
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 {
@@ -633,7 +621,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() {
@@ -683,6 +671,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)
@@ -727,10 +719,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())
- 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()
@@ -897,22 +891,28 @@
}
}
- TransformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
+ transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
- library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
+ library.coverageOutputFile = transformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
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
}
@@ -974,7 +974,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
library.tocFile = android.OptionalPathForPath(tocFile)
- TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
stripFlags := flagsToStripFlags(flags)
if library.stripper.NeedsStrip(ctx) {
@@ -1019,7 +1019,7 @@
if Bool(library.Properties.Sort_bss_symbols_by_size) {
unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
@@ -1029,7 +1029,7 @@
linkerDeps = append(linkerDeps, symbolOrderingFile)
}
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
@@ -1039,7 +1039,7 @@
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
- library.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
+ library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
var staticAnalogue *StaticLibraryInfo
@@ -1058,18 +1058,18 @@
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()),
})
@@ -1115,13 +1115,13 @@
return refAbiDumpTextFile.Path()
}
if refAbiDumpGzipFile.Valid() {
- return UnzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
+ return unzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
}
return nil
}
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() {
@@ -1141,26 +1141,32 @@
SourceAbiFlags = append(SourceAbiFlags, "-I"+reexportedInclude)
}
exportedHeaderFlags := strings.Join(SourceAbiFlags, " ")
- library.sAbiOutputFile = TransformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
+ library.sAbiOutputFile = transformDumpToLinkedDump(ctx, objs.sAbiDumpFiles, soFile, fileName, exportedHeaderFlags,
android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)),
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(),
+ 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.Config()), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
}
}
}
+// 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 {
+ // 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 +1175,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 +1183,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 +1195,7 @@
}
}
+ // Optionally export proto headers.
if Bool(library.Properties.Proto.Export_proto_headers) {
if library.baseCompiler.hasSrcExt(".proto") {
var includes android.Paths
@@ -1221,25 +1230,30 @@
}
}
+ // Add sysprop-related directories to the exported directories of this library.
library.reexportDirs(dir)
library.reexportDeps(library.baseCompiler.pathDeps...)
library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
}
+ // Add stub-related flags if this library is a stub library.
if library.buildStubs() && !library.skipAPIDefine {
library.reexportFlags("-D" + versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx)) + "=" + library.stubsVersion())
}
+ // Propagate a Provider containing information about exported flags, deps, and include paths.
library.flagExporter.setProvider(ctx)
return out
}
+// 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)
@@ -1269,7 +1283,7 @@
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 {
@@ -1278,7 +1292,7 @@
}
// In some cases we want to use core variant for VNDK-Core libs
- if ctx.isVndk() && !ctx.isVndkSp() && !ctx.isVndkExt() {
+ if ctx.isVndk() && !ctx.isVndkSp() && !ctx.IsVndkExt() {
mayUseCoreVariant := true
if ctx.mustUseVendorVariant() {
@@ -1299,7 +1313,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() {
@@ -1346,36 +1360,46 @@
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
@@ -1458,6 +1482,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 +1504,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,6 +1570,8 @@
}
}
+// 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
if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
@@ -1607,6 +1649,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 {
@@ -1754,13 +1799,13 @@
hashedOutputfile := outputFile
outputFile = android.PathForModuleOut(ctx, "unhashed", fileName)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "bssl_inject_hash").
+ BuiltTool("bssl_inject_hash").
Flag("-sha256").
FlagWithInput("-in-object ", outputFile).
FlagWithOutput("-o ", hashedOutputfile)
- rule.Build(pctx, ctx, "injectCryptoHash", "inject crypto hash")
+ rule.Build("injectCryptoHash", "inject crypto hash")
}
return outputFile
diff --git a/cc/linkable.go b/cc/linkable.go
index 0609b28..d010985 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -6,7 +6,10 @@
"github.com/google/blueprint"
)
+// 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
@@ -41,7 +44,10 @@
UseVndk() bool
MustUseVendorVariant() bool
IsVndk() bool
+ IsVndkExt() bool
+ IsVndkPrivate(config android.Config) bool
HasVendorVariant() bool
+ InProduct() bool
SdkVersion() string
AlwaysSdk() bool
@@ -51,23 +57,30 @@
}
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"}
)
+// 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 +93,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 +130,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..7bc4105 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,6 +249,16 @@
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")
}
diff --git a/cc/object.go b/cc/object.go
index ab2672b..3ce7676 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -124,7 +124,7 @@
if String(object.Properties.Prefix_symbols) != "" {
output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
- TransformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), outputFile,
+ transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), outputFile,
builderFlags, output)
outputFile = output
}
@@ -134,12 +134,12 @@
if String(object.Properties.Prefix_symbols) != "" {
input := android.PathForModuleOut(ctx, "unprefixed", ctx.ModuleName()+objectExtension)
- TransformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), input,
+ transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), input,
builderFlags, output)
output = input
}
- TransformObjsToObj(ctx, objs.objFiles, builderFlags, output, flags.LdFlagsDeps)
+ transformObjsToObj(ctx, objs.objFiles, builderFlags, output, flags.LdFlagsDeps)
}
ctx.CheckbuildFile(outputFile)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 8873883..df71340 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -146,7 +146,7 @@
// 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, outputFile, tocFile, builderFlags)
+ transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
if ctx.Windows() && p.properties.Windows_import_lib != nil {
// Consumers of this library actually links to the import library in build
@@ -190,6 +190,12 @@
}
}
+ if p.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+
+ return nil
+ }
+
return nil
}
diff --git a/cc/proto.go b/cc/proto.go
index 9c102a2..4466144 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -50,11 +50,11 @@
depFile := ccFile.ReplaceExtension(ctx, "d")
outputs := android.WritablePaths{ccFile, headerFile}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
- android.ProtoRule(ctx, rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
+ android.ProtoRule(rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
- rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+ rule.Build("protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return ccFile, headerFile
}
diff --git a/cc/sabi.go b/cc/sabi.go
index ef6bead..99e718e 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.isLlndkPublic(ctx.Config()) {
+ return "LLNDK"
+ }
+ if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate(ctx.Config()) {
+ 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.IsSkipInstall() {
+ 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.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 dbc52a5..22ee25f 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -169,14 +169,14 @@
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
- No_recover []string
- }
+ No_recover []string `android:"arch_variant"`
+ } `android:"arch_variant"`
// Sanitizers to run with flag configuration specified
Config struct {
// Enables CFI support flags for assembly-heavy libraries
Cfi_assembly_support *bool `android:"arch_variant"`
- }
+ } `android:"arch_variant"`
// value to pass to -fsanitize-recover=
Recover []string
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
new file mode 100644
index 0000000..b291bd0
--- /dev/null
+++ b/cc/snapshot_prebuilt.go
@@ -0,0 +1,844 @@
+// 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"
+)
+
+// 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()
+
+ // 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 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)
+}
+
+func (vendorSnapshotImage) inImage(m *Module) func() bool {
+ return m.inVendor
+}
+
+func (vendorSnapshotImage) available(m *Module) *bool {
+ return m.VendorProperties.Vendor_available
+}
+
+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 (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) inImage(m *Module) func() bool {
+ return m.InRecovery
+}
+
+func (recoverySnapshotImage) available(m *Module) *bool {
+ return m.Properties.Recovery_available
+}
+
+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()
+}
+
+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")
+)
+
+// 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)
+}
+
+type baseSnapshotDecoratorProperties struct {
+ // snapshot version.
+ Version string
+
+ // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
+ Target_arch 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
+ moduleSuffix string
+}
+
+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.moduleSuffix + versionSuffix
+}
+
+func (p *baseSnapshotDecorator) version() string {
+ return p.baseProperties.Version
+}
+
+func (p *baseSnapshotDecorator) arch() string {
+ return p.baseProperties.Target_arch
+}
+
+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.moduleSuffix = 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"`
+ }
+ 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
+}
+
+// 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)
+ 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 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
+ 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
+}
+
+// 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)
+ 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
+}
+
+// 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
+ 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
+}
+
+// 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)
+ p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
+
+ 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) {
+ 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())
+}
+
+// VendorSnapshotSourceMutator disables source modules which have corresponding 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/snapshot_utils.go b/cc/snapshot_utils.go
index a3d52e6..e841a54 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,14 @@
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 _, _, 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) {
+ } else if isVendorSnapshotAware(m, isVendorProprietaryPath(ctx.ModuleDir()), apexInfo) ||
+ isRecoverySnapshotAware(m, isRecoveryProprietaryPath(ctx.ModuleDir()), apexInfo) {
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/strip.go b/cc/strip.go
index e9aec91..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,14 +51,18 @@
// 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,
flags StripFlags, isStaticLib bool) {
if actx.Darwin() {
- TransformDarwinStrip(actx, in, out)
+ transformDarwinStrip(actx, in, out)
} else {
if Bool(stripper.StripProperties.Strip.Keep_symbols) {
flags.StripKeepSymbols = true
@@ -68,7 +76,7 @@
if actx.Config().Debuggable() && !flags.StripKeepMiniDebugInfo && !isStaticLib {
flags.StripAddGnuDebuglink = true
}
- TransformStrip(actx, in, out, flags)
+ transformStrip(actx, in, out, flags)
}
}
diff --git a/cc/stub_library.go b/cc/stub_library.go
new file mode 100644
index 0000000..76d236c
--- /dev/null
+++ b/cc/stub_library.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 cc
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+func init() {
+ // Use singleton type to gather all generated soong modules.
+ android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+}
+
+type stubLibraries struct {
+ stubLibraryMap map[string]bool
+}
+
+// Check if the module defines stub, or itself is stub
+func isStubTarget(m *Module) bool {
+ if m.IsStubs() || m.HasStubsVariants() {
+ return true
+ }
+
+ // Library which defines LLNDK Stub is also Stub target.
+ // Pure LLNDK Stub target would not contain any packaging
+ // with target file path.
+ if library, ok := m.linker.(*libraryDecorator); ok {
+ if library.Properties.Llndk_stubs != nil {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Get target file name to be installed from this module
+func getInstalledFileName(m *Module) string {
+ for _, ps := range m.PackagingSpecs() {
+ if name := ps.FileName(); name != "" {
+ return name
+ }
+ }
+ return ""
+}
+
+func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) {
+ // Visit all generated soong modules and store stub library file names.
+ ctx.VisitAllModules(func(module android.Module) {
+ if m, ok := module.(*Module); ok {
+ if isStubTarget(m) {
+ if name := getInstalledFileName(m); name != "" {
+ s.stubLibraryMap[name] = true
+ }
+ }
+ }
+ })
+}
+
+func stubLibrariesSingleton() android.Singleton {
+ return &stubLibraries{
+ stubLibraryMap: make(map[string]bool),
+ }
+}
+
+func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
+ // Convert stub library file names into Makefile variable.
+ ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedStringKeys(s.stubLibraryMap), " "))
+}
diff --git a/cc/test.go b/cc/test.go
index 3772691..a9be6f9 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.
@@ -241,10 +236,6 @@
return BoolDefault(test.Properties.Gtest, true)
}
-func (test *testDecorator) testFor() []string {
- return test.Properties.Test_for
-}
-
func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
if !test.gtest() {
return flags
diff --git a/cc/testing.go b/cc/testing.go
index 95a93a0..fc5b030 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)
@@ -334,7 +335,7 @@
},
apex_available: [
"//apex_available:platform",
- "myapex"
+ "//apex_available:anyapex",
],
}
cc_library {
@@ -437,6 +438,13 @@
ndk_prebuilt_shared_stl {
name: "ndk_libc++_shared",
}
+
+ cc_library_static {
+ name: "libgoogle-benchmark",
+ sdk_version: "current",
+ stl: "none",
+ system_shared_libs: [],
+ }
`
supportLinuxBionic := false
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 0c934ad..bda73ea 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -90,7 +90,7 @@
if library.Properties.Repack_objects_to_keep != nil {
fileName := ctx.ModuleName() + staticLibraryExtension
repackedPath := android.PathForModuleOut(ctx, fileName)
- TransformArchiveRepack(ctx, outputFile, repackedPath, library.Properties.Repack_objects_to_keep)
+ transformArchiveRepack(ctx, outputFile, repackedPath, library.Properties.Repack_objects_to_keep)
outputFile = repackedPath
}
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 6563f6e..da37d0f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -13,625 +13,27 @@
// 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,
}
var recoverySnapshotSingleton = snapshotSingleton{
@@ -639,7 +41,7 @@
"SOONG_RECOVERY_SNAPSHOT_ZIP",
android.OptionalPath{},
false,
- recoveryImageSingleton,
+ recoverySnapshotImageSingleton,
}
func VendorSnapshotSingleton() android.Singleton {
@@ -667,13 +69,12 @@
// 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
}
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,7 +84,6 @@
// 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",
@@ -694,7 +94,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 +137,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 +147,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
@@ -766,15 +162,21 @@
// 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 isVendorSnapshotAware(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
+ return isSnapshotAware(m, inVendorProprietaryPath, apexInfo, vendorSnapshotImageSingleton)
}
-func isRecoverySnapshotModule(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
- return isSnapshotModule(m, inRecoveryProprietaryPath, apexInfo, recoveryImageSingleton)
+// Determine if a module is going to be included in recovery snapshot or not.
+//
+// Targets of recovery snapshot are "recovery: true" or "recovery_available: true"
+// modules in AOSP. They are not guaranteed to be compatible with older recovery images.
+// So they are captured as recovery snapshot To build older recovery image.
+func isRecoverySnapshotAware(m *Module, inRecoveryProprietaryPath bool, apexInfo android.ApexInfo) bool {
+ return isSnapshotAware(m, inRecoveryProprietaryPath, apexInfo, recoverySnapshotImageSingleton)
}
-func isSnapshotModule(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image image) bool {
+// Determines if the module is a candidate for snapshot.
+func isSnapshotAware(m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
if !m.Enabled() || m.Properties.HideFromMake {
return false
}
@@ -799,7 +201,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
}
@@ -817,7 +219,6 @@
// 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.
@@ -827,6 +228,8 @@
}
}
// 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
}
@@ -842,7 +245,7 @@
if !m.IsVndk() {
return true
}
- return m.isVndkExt()
+ return m.IsVndkExt()
}
}
return true
@@ -856,6 +259,33 @@
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" {
@@ -909,6 +339,8 @@
var headers android.Paths
+ // 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 +349,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"
@@ -968,7 +377,7 @@
out := filepath.Join(configsDir, path.Base())
if !installedConfigs[out] {
installedConfigs[out] = true
- ret = append(ret, copyFile(ctx, path, out))
+ ret = append(ret, copyFileRule(ctx, path, out))
}
}
@@ -1019,7 +428,7 @@
prop.ModuleName += ".cfi"
}
snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
- ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+ ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
} else {
stem = ctx.ModuleName(m)
}
@@ -1033,7 +442,7 @@
// install bin
binPath := m.outputFile.Path()
snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
- ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
+ ret = append(ret, copyFileRule(ctx, binPath, snapshotBinOut))
propOut = snapshotBinOut + ".json"
} else if m.object() {
// object files aren't installed to the device, so their names can conflict.
@@ -1041,7 +450,7 @@
objPath := m.outputFile.Path()
snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
- ret = append(ret, copyFile(ctx, objPath, snapshotObjOut))
+ ret = append(ret, copyFileRule(ctx, objPath, snapshotObjOut))
propOut = snapshotObjOut + ".json"
} else {
ctx.Errorf("unknown module %q in vendor snapshot", m.String())
@@ -1053,7 +462,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
}
@@ -1088,11 +497,14 @@
}
}
- if !isSnapshotModule(m, inProprietaryPath, apexInfo, c.image) {
+ if !isSnapshotAware(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,7 +515,7 @@
// skip already copied notice file
if !installedNotices[noticeOut] {
installedNotices[noticeOut] = true
- snapshotOutputs = append(snapshotOutputs, combineNotices(
+ snapshotOutputs = append(snapshotOutputs, combineNoticesRule(
ctx, m.NoticeFiles(), noticeOut))
}
}
@@ -1111,7 +523,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())))
}
@@ -1124,7 +536,7 @@
ctx,
snapshotDir,
c.name+"-"+ctx.Config().DeviceName()+".zip")
- zipRule := android.NewRuleBuilder()
+ zipRule := android.NewRuleBuilder(pctx, ctx)
// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
snapshotOutputList := android.PathForOutput(
@@ -1140,12 +552,12 @@
zipRule.Temporary(snapshotOutputList)
zipRule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipPath).
FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
FlagWithInput("-l ", snapshotOutputList)
- zipRule.Build(pctx, ctx, zipPath.String(), c.name+" snapshot "+zipPath.String())
+ zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
c.snapshotZipFile = android.OptionalPathForPath(zipPath)
}
@@ -1155,141 +567,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 4a005f3..1529ac5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -389,7 +389,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
}
@@ -533,7 +533,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 {
@@ -549,7 +549,7 @@
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 +622,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 +635,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 +657,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 +670,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 +703,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 +715,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 +725,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 +747,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 {
@@ -762,9 +755,9 @@
})
zipPath := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+".zip")
- zipRule := android.NewRuleBuilder()
+ 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").
@@ -775,12 +768,12 @@
zipRule.Temporary(snapshotOutputList)
zipRule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipPath).
FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
FlagWithInput("-l ", snapshotOutputList)
- zipRule.Build(pctx, ctx, zipPath.String(), "vndk snapshot "+zipPath.String())
+ zipRule.Build(zipPath.String(), "vndk snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
c.vndkSnapshotZipFile = android.OptionalPathForPath(zipPath)
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index c0320eb..e6e2ad8 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -154,7 +154,7 @@
// 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)
+ transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
p.androidMkSuffix = p.NameSuffix()
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0a9b156..c079e83 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -185,7 +185,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 +216,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 {
@@ -344,7 +343,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/main.go b/cmd/soong_build/main.go
index b88803a..907bed3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -51,10 +51,22 @@
return android.NewNameResolver(exportFilter)
}
+// bazelConversionRequested checks that the user is intending to convert
+// Blueprint to Bazel BUILD files.
+func bazelConversionRequested(configuration android.Config) bool {
+ return configuration.IsEnvTrue("CONVERT_TO_BAZEL")
+}
+
func newContext(srcDir string, configuration android.Config) *android.Context {
ctx := android.NewContext(configuration)
- ctx.Register()
- if !shouldPrepareBuildActions() {
+ if bazelConversionRequested(configuration) {
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ ctx.RegisterForBazelConversion()
+ } else {
+ ctx.Register()
+ }
+ if !shouldPrepareBuildActions(configuration) {
configuration.SetStopBefore(bootstrap.StopBeforePrepareBuildActions)
}
ctx.SetNameInterface(newNameResolver(configuration))
@@ -79,7 +91,7 @@
srcDir := filepath.Dir(flag.Arg(0))
var ctx *android.Context
configuration := newConfig(srcDir)
- extraNinjaDeps := []string{configuration.ConfigFileName, configuration.ProductVariablesFileName}
+ extraNinjaDeps := []string{configuration.ProductVariablesFileName}
// Read the SOONG_DELVE again through configuration so that there is a dependency on the environment variable
// and soong_build will rerun when it is set for the first time.
@@ -114,6 +126,8 @@
ctx = newContext(srcDir, 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 +144,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 +154,19 @@
}
}
-func shouldPrepareBuildActions() bool {
- // If we're writing soong_docs or queryview, don't write build.ninja or
- // collect metrics.
- return docFile == "" && bazelQueryViewDir == ""
+// 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/writedocs.go b/cmd/soong_build/writedocs.go
index 5fb6e6b..253979e 100644
--- a/cmd/soong_build/writedocs.go
+++ b/cmd/soong_build/writedocs.go
@@ -44,9 +44,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.
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 29030d6..bd1d450 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() {
+ 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,72 @@
// 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 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..deaf77f 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -437,7 +437,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.
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index df68563..be7d4c6 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -195,7 +195,7 @@
// 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\"")
+ checkError(t, err, "invalid build path for <uses-library> \"a\"")
})
}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 65380fe..b0a684e 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -51,7 +51,7 @@
// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
// ModuleConfig. The produced files and their install locations will be available through rule.Installs().
-func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig,
+func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
defer func() {
@@ -67,7 +67,7 @@
}
}()
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index e89f045..32c4f84 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/dexpreopt"
+ "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
)
@@ -38,12 +39,16 @@
outDir = flag.String("out_dir", "", "path to output directory")
)
-type pathContext struct {
+type builderContext struct {
config android.Config
}
-func (x *pathContext) Config() android.Config { return x.config }
-func (x *pathContext) AddNinjaFileDeps(...string) {}
+func (x *builderContext) Config() android.Config { return x.config }
+func (x *builderContext) AddNinjaFileDeps(...string) {}
+func (x *builderContext) Build(android.PackageContext, android.BuildParams) {}
+func (x *builderContext) Rule(android.PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
+ return nil
+}
func main() {
flag.Parse()
@@ -76,7 +81,7 @@
usage("--module configuration file is required")
}
- ctx := &pathContext{android.NullConfig(*outDir)}
+ ctx := &builderContext{android.NullConfig(*outDir)}
globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
if err != nil {
@@ -133,7 +138,7 @@
writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
}
-func writeScripts(ctx android.PathContext, globalSoong *dexpreopt.GlobalSoongConfig,
+func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index feabd70..59278fd 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -60,7 +60,7 @@
func TestDexPreopt(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
@@ -82,7 +82,7 @@
func TestDexPreoptSystemOther(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
@@ -142,7 +142,7 @@
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index ecbfbab..c6181bc 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -29,8 +29,16 @@
type filesystem struct {
android.ModuleBase
android.PackagingBase
+
+ output android.OutputPath
+ installDir android.InstallPath
}
+// 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).
+// The modules are placed in the filesystem image just like they are installed to the ordinary
+// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
android.InitPackageModule(module)
@@ -44,6 +52,10 @@
f.AddDeps(ctx, dependencyTag)
}
+func (f *filesystem) installFileName() string {
+ return f.BaseModuleName() + ".img"
+}
+
var pctx = android.NewPackageContext("android/soong/filesystem")
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -51,9 +63,9 @@
f.CopyDepsToZip(ctx, zipFile)
rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().
- BuiltTool(ctx, "zipsync").
+ BuiltTool("zipsync").
FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
Input(zipFile)
@@ -68,13 +80,32 @@
Text(">").Output(propFile).
Implicit(mkuserimg)
- image := android.PathForModuleOut(ctx, "filesystem.img").OutputPath
- builder.Command().BuiltTool(ctx, "build_image").
+ f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
- Output(image).
+ Output(f.output).
Text(rootDir.String()) // directory where to find fs_config_files|dirs
// rootDir is not deleted. Might be useful for quick inspection.
- builder.Build(pctx, ctx, "build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+ builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+
+ f.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+}
+
+var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(f.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+ },
+ },
+ }}
}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e5ee3fc..78af97c 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
@@ -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
@@ -342,8 +362,27 @@
return
}
+ // Pick a unique path outside the task.genDir for the sbox manifest textproto,
+ // a unique rule name, and the user-visible description.
+ manifestName := "genrule.sbox.textproto"
+ desc := "generate"
+ name := "generator"
+ if task.shards > 0 {
+ manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
+ desc += " " + strconv.Itoa(task.shard)
+ name += strconv.Itoa(task.shard)
+ } else if len(task.out) == 1 {
+ desc += " " + task.out[0].Base()
+ }
+
+ 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).SandboxTools()
+ cmd := rule.Command()
+
for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{android.SboxPathForOutput(out, task.genDir)})
+ addLocationLabel(out.Rel(), []string{cmd.PathForOutput(out)})
}
referencedDepfile := false
@@ -374,7 +413,7 @@
case "out":
var sandboxOuts []string
for _, out := range task.out {
- sandboxOuts = append(sandboxOuts, android.SboxPathForOutput(out, task.genDir))
+ sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
}
return strings.Join(sandboxOuts, " "), nil
case "depfile":
@@ -384,7 +423,7 @@
}
return "__SBOX_DEPFILE__", nil
case "genDir":
- return android.SboxPathForOutput(task.genDir, task.genDir), nil
+ return cmd.PathForOutput(task.genDir), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -426,35 +465,18 @@
}
g.rawCommands = append(g.rawCommands, rawCommand)
- // Pick a unique path outside the task.genDir for the sbox manifest textproto,
- // a unique rule name, and the user-visible description.
- manifestName := "genrule.sbox.textproto"
- desc := "generate"
- name := "generator"
- if task.shards > 0 {
- manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
- desc += " " + strconv.Itoa(task.shard)
- name += strconv.Itoa(task.shard)
- } else if len(task.out) == 1 {
- desc += " " + task.out[0].Base()
- }
-
- manifestPath := android.PathForModuleOut(ctx, manifestName)
-
- // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
- rule := android.NewRuleBuilder().Sbox(task.genDir, manifestPath)
- cmd := rule.Command()
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)
}
// Create the rule to run the genrule command inside sbox.
- rule.Build(pctx, ctx, name, desc)
+ rule.Build(name, desc)
if len(task.copyTo) > 0 {
// If copyTo is set, multiple shards need to be copied into a single directory.
@@ -612,6 +634,10 @@
}
genDir := android.PathForModuleGen(ctx, genSubDir)
+ // 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).SandboxTools()
for _, in := range shard {
outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -634,11 +660,11 @@
case "in":
return in.String(), nil
case "out":
- return android.SboxPathForOutput(outFile, genDir), nil
+ return rule.Command().PathForOutput(outFile), nil
case "depfile":
// Generate a depfile for each output file. Store the list for
// later in order to combine them all into a single depfile.
- depFile := android.SboxPathForOutput(outFile.ReplaceExtension(ctx, "d"), genDir)
+ depFile := rule.Command().PathForOutput(outFile.ReplaceExtension(ctx, "d"))
commandDepFiles = append(commandDepFiles, depFile)
return depFile, nil
default:
@@ -664,7 +690,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)
}
@@ -703,7 +730,7 @@
Shard_size *int64
}
-const defaultShardSize = 100
+const defaultShardSize = 50
func NewGenRule() *Module {
properties := &genRuleProperties{}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 8d3cfcb..3cbfaf1 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"},
@@ -757,7 +757,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 9e8dc78..39502b3 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -48,6 +48,7 @@
"robolectric.go",
"sdk.go",
"sdk_library.go",
+ "sdk_library_external.go",
"support_libraries.go",
"sysprop.go",
"system_modules.go",
diff --git a/java/aapt2.go b/java/aapt2.go
index 04e4de5..5346ddf 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -25,12 +25,9 @@
"android/soong/android"
)
-const AAPT2_SHARD_SIZE = 100
-
// Convert input resource file path to output file path.
// values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;
-// For other resource file, just replace the last "/" with "_" and
-// add .flat extension.
+// For other resource file, just replace the last "/" with "_" and add .flat extension.
func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath {
name := res.Base()
@@ -43,6 +40,7 @@
return android.PathForModuleOut(ctx, "aapt2", subDir, name)
}
+// pathsToAapt2Paths Calls pathToAapt2Path on each entry of the given Paths, i.e. []Path.
func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths {
outPaths := make(android.WritablePaths, len(resPaths))
@@ -53,6 +51,9 @@
return outPaths
}
+// Shard resource files for efficiency. See aapt2Compile for details.
+const AAPT2_SHARD_SIZE = 100
+
var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
blueprint.RuleParams{
Command: `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`,
@@ -60,14 +61,26 @@
},
"outDir", "cFlags")
+// aapt2Compile compiles resources and puts the results in the requested directory.
func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
flags []string) android.WritablePaths {
+ // Shard the input paths so that they can be processed in parallel. If we shard them into too
+ // small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The
+ // current shard size, 100, seems to be a good balance between the added cost and the gain.
+ // The aapt2 compile actions are trivially short, but each action in ninja takes on the order of
+ // ~10 ms to run. frameworks/base/core/res/res has >10k resource files, so compiling each one
+ // with an individual action could take 100 CPU seconds. Sharding them reduces the overhead of
+ // starting actions by a factor of 100, at the expense of recompiling more files when one
+ // changes. Since the individual compiles are trivial it's a good tradeoff.
shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE)
ret := make(android.WritablePaths, 0, len(paths))
for i, shard := range shards {
+ // This should be kept in sync with pathToAapt2Path. The aapt2 compile command takes an
+ // output directory path, but not output file paths. So, outPaths is just where we expect
+ // the output files will be located.
outPaths := pathsToAapt2Paths(ctx, shard)
ret = append(ret, outPaths...)
@@ -82,6 +95,12 @@
Inputs: shard,
Outputs: outPaths,
Args: map[string]string{
+ // The aapt2 compile command takes an output directory path, but not output file paths.
+ // outPaths specified above is only used for dependency management purposes. In order for
+ // the outPaths values to match the actual outputs from aapt2, the dir parameter value
+ // must be a common prefix path of the paths values, and the top-level path segment used
+ // below, "aapt2", must always be kept in sync with the one in pathToAapt2Path.
+ // TODO(b/174505750): Make this easier and robust to use.
"outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
"cFlags": strings.Join(flags, " "),
},
@@ -104,6 +123,8 @@
},
}, "cFlags", "resZipDir", "zipSyncFlags")
+// Unzips the given compressed file and compiles the resource source files in it. The zipPrefix
+// parameter points to the subdirectory in the zip file where the resource files are located.
func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string,
flags []string) {
@@ -163,6 +184,7 @@
var inFlags []string
if len(compiledRes) > 0 {
+ // Create a file that contains the list of all compiled resource file paths.
resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list")
// Write out file lists to files
ctx.Build(pctx, android.BuildParams{
@@ -174,10 +196,12 @@
deps = append(deps, compiledRes...)
deps = append(deps, resFileList)
+ // aapt2 filepath arguments that start with "@" mean file-list files.
inFlags = append(inFlags, "@"+resFileList.String())
}
if len(compiledOverlay) > 0 {
+ // Compiled overlay files are processed the same way as compiled resources.
overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list")
ctx.Build(pctx, android.BuildParams{
Rule: fileListToFileRule,
@@ -188,9 +212,11 @@
deps = append(deps, compiledOverlay...)
deps = append(deps, overlayFileList)
+ // Compiled overlay files are passed over to aapt2 using -R option.
inFlags = append(inFlags, "-R", "@"+overlayFileList.String())
}
+ // Set auxiliary outputs as implicit outputs to establish correct dependency chains.
implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages)
linkOutput := packageRes
@@ -212,6 +238,10 @@
Implicits: deps,
Output: linkOutput,
ImplicitOutputs: implicitOutputs,
+ // Note the absence of splitPackages. The caller is supposed to compose and provide --split flag
+ // values via the flags parameter when it wants to split outputs.
+ // TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably
+ // tidy.
Args: map[string]string{
"flags": strings.Join(flags, " "),
"inFlags": strings.Join(inFlags, " "),
@@ -230,6 +260,8 @@
CommandDeps: []string{"${config.Aapt2Cmd}"},
})
+// Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto
+// format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto.
func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) {
ctx.Build(pctx, android.BuildParams{
Rule: aapt2ConvertRule,
diff --git a/java/aar.go b/java/aar.go
index 1940d7f..3750f72 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)
@@ -407,6 +417,7 @@
ctx.VisitDirectDeps(func(module android.Module) {
depName := ctx.OtherModuleName(module)
+ depTag := ctx.OtherModuleDependencyTag(module)
var exportPackage android.Path
aarDep, _ := module.(AndroidLibraryDependency)
@@ -414,7 +425,7 @@
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:
@@ -439,7 +450,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 +468,8 @@
}
}
- // 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)
- }
+ // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
+ maybeAddCLCFromDep(module, depTag, depName, classLoaderContexts)
})
deps = append(deps, sharedLibs...)
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 9ad3f9b..aaad44f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -139,9 +139,9 @@
func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) {
entries.SetString("LOCAL_MODULE_TAGS", "tests")
if len(test_suites) > 0 {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...)
+ entries.AddCompatibilityTestSuites(test_suites...)
} else {
- entries.SetString("LOCAL_COMPATIBILITY_SUITE", "null-suite")
+ entries.AddCompatibilityTestSuites("null-suite")
}
}
@@ -274,7 +274,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 +289,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 +393,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 +548,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/app.go b/java/app.go
index 3384bbd..e6d9550 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1115,8 +1115,8 @@
}
fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
- rule := android.NewRuleBuilder()
- command := rule.Command().BuiltTool(ctx, "test_config_fixer").Input(testConfig).Output(fixedConfig)
+ rule := android.NewRuleBuilder(pctx, ctx)
+ command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig)
fixNeeded := false
if ctx.ModuleName() != a.installApkName {
@@ -1131,7 +1131,7 @@
}
if fixNeeded {
- rule.Build(pctx, ctx, "fix_test_config", "fix test config")
+ rule.Build("fix_test_config", "fix test config")
return fixedConfig
}
return testConfig
@@ -1400,6 +1400,13 @@
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,
@@ -1440,15 +1447,15 @@
})
return
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'lib/**/*.so'").
Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build(pctx, ctx, "uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+ rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
}
// Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -1467,15 +1474,15 @@
func (a *AndroidAppImport) uncompressDex(
ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'classes*.dex'").
Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build(pctx, ctx, "uncompress-dex", "Uncompress dex files")
+ rule.Build("uncompress-dex", "Uncompress dex files")
}
func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2015,8 +2022,8 @@
func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
- rule := android.NewRuleBuilder()
- cmd := rule.Command().BuiltTool(ctx, "manifest_check").
+ rule := android.NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command().BuiltTool("manifest_check").
Flag("--enforce-uses-libraries").
Input(manifest).
FlagWithOutput("-o ", outputFile)
@@ -2029,7 +2036,7 @@
cmd.FlagWithArg("--optional-uses-library ", lib)
}
- rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>")
+ rule.Build("verify_uses_libraries", "verify <uses-library>")
return outputFile
}
@@ -2039,7 +2046,7 @@
func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
aapt := ctx.Config().HostToolPath(ctx, "aapt")
rule.Command().
Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
@@ -2048,7 +2055,7 @@
Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk)
rule.Command().Text("cp -f").Input(apk).Output(outputFile)
- rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>")
+ rule.Build("verify_uses_libraries", "verify <uses-library>")
return outputFile
}
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_test.go b/java/app_test.go
index 6efb0dc..e13c6b9 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2524,6 +2524,24 @@
`,
expected: "prebuilts/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+)")
@@ -2531,6 +2549,12 @@
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 {
@@ -2542,6 +2566,34 @@
}
}
+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 {
@@ -2730,6 +2782,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 +2812,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: [
diff --git a/java/boot_jars.go b/java/boot_jars.go
index e706547..823275b 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -85,8 +85,8 @@
timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
- rule := android.NewRuleBuilder()
- checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars").
+ rule := android.NewRuleBuilder(pctx, ctx)
+ checkBootJars := rule.Command().BuiltTool("check_boot_jars").
Input(ctx.Config().HostToolPath(ctx, "dexdump")).
Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
@@ -109,7 +109,7 @@
}
checkBootJars.Text("&& touch").Output(timestamp)
- rule.Build(pctx, ctx, "boot_jars_package_check", "check boot jar packages")
+ rule.Build("boot_jars_package_check", "check boot jar packages")
// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
ctx.Phony("check-boot-jars", timestamp)
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 a21fb76..67738d4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -216,7 +216,7 @@
return dexJarFile
}
- dexpreoptRule.Build(pctx, ctx, "dexpreopt", "dexpreopt")
+ dexpreoptRule.Build("dexpreopt", "dexpreopt")
d.builtInstalled = dexpreoptRule.Installs().String()
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 5df5845..062005b 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -418,43 +418,53 @@
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
- }
-
+ // Ignore any module that is not listed in the boot image configuration.
name := ctx.ModuleName(module)
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 +475,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 +506,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 +523,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)
@@ -539,14 +556,14 @@
}
if image.zip != nil {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", image.zip).
FlagWithArg("-C ", image.dir.Join(ctx, android.Android.String()).String()).
FlagWithInputList("-f ", zipFiles, " -f ")
- rule.Build(pctx, ctx, "zip_"+image.name, "zip "+image.name+" image")
+ rule.Build("zip_"+image.name, "zip "+image.name+" image")
}
return image
@@ -568,7 +585,7 @@
oatLocation := dexpreopt.PathToLocation(outputPath, arch)
imagePath := outputPath.ReplaceExtension(ctx, "art")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String())
@@ -689,7 +706,7 @@
android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
}
- rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
+ rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
// save output and installed files for makevars
image.installs = rule.Installs()
@@ -713,7 +730,7 @@
profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
defaultProfile := "frameworks/base/config/boot-image-profile.txt"
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
var bootImageProfile android.Path
@@ -744,7 +761,7 @@
rule.Install(profile, "/system/etc/boot-image.prof")
- rule.Build(pctx, ctx, "bootJarsProfile", "profile boot jars")
+ rule.Build("bootJarsProfile", "profile boot jars")
image.profileInstalls = rule.Installs()
@@ -766,7 +783,7 @@
return nil
}
return ctx.Config().Once(bootFrameworkProfileRuleKey, func() interface{} {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
// Some branches like master-art-host don't have frameworks/base, so manually
@@ -779,7 +796,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")
@@ -794,7 +811,7 @@
FlagWithOutput("--reference-profile-file=", profile)
rule.Install(profile, "/system/etc/boot-image.bprof")
- rule.Build(pctx, ctx, "bootFrameworkProfile", "profile boot framework jars")
+ rule.Build("bootFrameworkProfile", "profile boot framework jars")
image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
return profile
@@ -839,7 +856,7 @@
// WriteFileRule automatically adds the last end-of-line.
android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n"))
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName)
// TODO: Rename `profileInstalls` to `extraInstalls`?
@@ -863,25 +880,25 @@
}
// Create a rule to call oatdump.
output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
// TODO: for now, use the debug version for better error reporting
- BuiltTool(ctx, "oatdumpd").
+ BuiltTool("oatdumpd").
FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":").
FlagWithArg("--image=", strings.Join(image.imageLocations(), ":")).Implicits(image.imagesDeps.Paths()).
FlagWithOutput("--output=", output).
FlagWithArg("--instruction-set=", arch.String())
- rule.Build(pctx, ctx, "dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+ rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
// Create a phony rule that depends on the output file and prints the path.
phony := android.PathForPhony(ctx, "dump-oat-boot-"+suffix)
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
rule.Command().
Implicit(output).
ImplicitOutput(phony).
Text("echo").FlagWithArg("Output in ", output.String())
- rule.Build(pctx, ctx, "phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
+ rule.Build("phony-dump-oat-boot-"+suffix, "dump oat boot "+arch.String())
allPhonies = append(allPhonies, phony)
}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index da8489a..9c88a3c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -671,7 +671,7 @@
j.stubsSrcJar = nil
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Text(outDir.String())
rule.Command().Text("mkdir -p").Text(outDir.String())
@@ -689,7 +689,7 @@
Flag("-Xdoclint:none")
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", j.docZip).
@@ -700,7 +700,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", "javadoc")
+ rule.Build("javadoc", "javadoc")
}
//
@@ -845,7 +845,7 @@
outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
cmd := rule.Command().
- BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
+ BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
FlagWithRspFileInputList("@", srcs).
@@ -914,7 +914,7 @@
dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
return rule.Command().
- BuiltTool(ctx, "dokka").
+ BuiltTool("dokka").
Flag(config.JavacVmFlags).
Flag(srcJarDir.String()).
FlagWithInputList("-classpath ", dokkaClasspath, ":").
@@ -934,7 +934,7 @@
outDir := android.PathForModuleOut(ctx, "out")
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
@@ -968,7 +968,7 @@
}
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", d.docZip).
@@ -979,7 +979,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", desc)
+ rule.Build("javadoc", desc)
}
//
@@ -1278,7 +1278,7 @@
}).NoVarTemplate(ctx.Config()))
}
- cmd.BuiltTool(ctx, "metalava").
+ cmd.BuiltTool("metalava").
Flag(config.JavacVmFlags).
Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
FlagWithArg("-encoding ", "UTF-8").
@@ -1333,7 +1333,7 @@
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
if BoolDefault(d.properties.High_mem, false) {
// This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
@@ -1480,19 +1480,19 @@
cmd.FlagWithArg("--error-message:compatibility:released ", msg)
}
- impRule := android.NewRuleBuilder()
+ impRule := android.NewRuleBuilder(pctx, ctx)
impCmd := impRule.Command()
// An action that copies the ninja generated rsp file to a new location. This allows us to
// add a large number of inputs to a file without exceeding bash command length limits (which
// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
// rsp file to be ${output}.rsp.
impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
- impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
+ impRule.Build("implicitsGen", "implicits generation")
cmd.Implicit(implicitsRsp)
if generateStubs {
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-jar").
FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
@@ -1503,7 +1503,7 @@
if Bool(d.properties.Write_sdk_values) {
d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", d.metadataZip).
@@ -1524,7 +1524,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "metalava", "metalava merged")
+ rule.Build("metalava", "metalava merged")
if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
@@ -1542,7 +1542,7 @@
d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
// Diff command line.
// -F matches the closest "opening" line, such as "package android {"
@@ -1564,7 +1564,7 @@
` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
` to the new methods, etc. shown in the above diff.\n\n`+
` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
- ` make %s-update-current-api\n\n`+
+ ` m %s-update-current-api\n\n`+
` To submit the revised current.txt to the main Android repository,\n`+
` you will need approval.\n`+
`******************************\n`, ctx.ModuleName())
@@ -1576,12 +1576,12 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "check current API")
+ rule.Build("metalavaCurrentApiCheck", "check current API")
d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
// update API rule
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("( true")
@@ -1602,7 +1602,7 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API")
+ rule.Build("metalavaCurrentApiUpdate", "update current API")
}
if String(d.properties.Check_nullability_warnings) != "" {
@@ -1625,7 +1625,7 @@
` and submitting the updated file as part of your change.`,
d.nullabilityWarningsFile, checkNullabilityWarnings)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("(").
@@ -1637,7 +1637,7 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
+ rule.Build("nullabilityWarningsCheck", "nullability warnings check")
}
}
@@ -1722,7 +1722,7 @@
rule.Temporary(srcJarList)
- rule.Command().BuiltTool(ctx, "zipsync").
+ rule.Command().BuiltTool("zipsync").
FlagWithArg("-d ", srcJarDir.String()).
FlagWithOutput("-l ", srcJarList).
FlagWithArg("-f ", `"*.java"`).
@@ -1783,9 +1783,9 @@
srcGlob := localSrcDir + "/**/*"
srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-jar").
FlagWithOutput("-o ", p.stubsSrcJar).
@@ -1794,7 +1794,7 @@
rule.Restat()
- rule.Build(pctx, ctx, "zip src", "Create srcjar from prebuilt source")
+ rule.Build("zip src", "Create srcjar from prebuilt source")
}
func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
diff --git a/java/gen.go b/java/gen.go
index d50a665..5766a94 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -57,7 +57,7 @@
outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
@@ -98,7 +98,7 @@
ruleDesc += " " + strconv.Itoa(i)
}
- rule.Build(pctx, ctx, ruleName, ruleDesc)
+ rule.Build(ruleName, ruleDesc)
}
return srcJarFiles
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 63b801a..71f1e57 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -135,12 +135,12 @@
})
h.metadataCSVPath = metadataCSV
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithInput("--zip_input=", classesJar).
FlagWithOutput("--output=", indexCSV)
- rule.Build(pctx, ctx, "merged-hiddenapi-index", "Merged Hidden API index")
+ rule.Build("merged-hiddenapi-index", "Merged Hidden API index")
h.indexCSVPath = indexCSV
}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 8f0e09c..419dc34 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -177,19 +177,20 @@
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)
}
}
}
}
// Singleton rule which applies hiddenapi on all boot class path dex files.
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).stubFlags
tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
@@ -208,7 +209,7 @@
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags")
+ rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
}
// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
@@ -236,7 +237,7 @@
ctx.Errorf("Failed to find combined-removed-dex.")
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).flags
tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
@@ -266,7 +267,7 @@
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags")
+ rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
return outputPath
}
@@ -274,14 +275,14 @@
// emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that
// have a partial manifest without frameworks/base but still need to build a boot image.
func emptyFlagsRule(ctx android.SingletonContext) android.Path {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).flags
rule.Command().Text("rm").Flag("-f").Output(outputPath)
rule.Command().Text("touch").Output(outputPath)
- rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
+ rule.Build("emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
return outputPath
}
@@ -299,16 +300,16 @@
}
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).metadata
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithOutput("--output=", outputPath).
Inputs(metadataCSV)
- rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
+ rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
return outputPath
}
@@ -399,13 +400,13 @@
}
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
Inputs(indexes)
- rule.Build(pctx, ctx, "singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
+ rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
h.index = hiddenAPISingletonPaths(ctx).index
}
diff --git a/java/java.go b/java/java.go
index 8738e00..d44719e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -772,6 +772,37 @@
libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
+ if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
+ // Require java_sdk_library at inter-partition java dependency to ensure stable
+ // interface between partitions. If inter-partition java_library dependency is detected,
+ // raise build error because java_library doesn't have a stable interface.
+ //
+ // Inputs:
+ // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
+ // if true, enable enforcement
+ // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
+ // exception list of java_library names to allow inter-partition dependency
+ for idx, lib := range j.properties.Libs {
+ if libDeps[idx] == nil {
+ continue
+ }
+
+ if _, ok := syspropPublicStubs[lib]; ok {
+ continue
+ }
+
+ if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
+ // java_sdk_library is always allowed at inter-partition dependency.
+ // So, skip check.
+ if _, ok := javaDep.(*SdkLibrary); ok {
+ continue
+ }
+
+ j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
+ }
+ }
+ }
+
// For library dependencies that are component libraries (like stubs), add the implementation
// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
for _, dep := range libDeps {
@@ -1050,7 +1081,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:
@@ -1062,7 +1092,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()
@@ -1075,8 +1104,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...)
@@ -1151,6 +1178,9 @@
deps.systemModules = &systemModules{outputDir, outputDeps}
}
}
+
+ // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
+ maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts)
})
return deps
@@ -1415,6 +1445,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.
@@ -2781,8 +2814,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()...)
}
@@ -2790,10 +2821,12 @@
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())
}
}
+
+ // Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
+ maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts)
})
var installFile android.Path
@@ -3049,21 +3082,21 @@
dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar")
if j.dexpreopter.uncompressedDex {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
temporary := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar.unaligned")
rule.Temporary(temporary)
// use zip2zip to uncompress classes*.dex files
rule.Command().
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputJar).
FlagWithOutput("-o ", temporary).
FlagWithArg("-0 ", "'classes*.dex'")
// use zipalign to align uncompressed classes*.dex files
rule.Command().
- BuiltTool(ctx, "zipalign").
+ BuiltTool("zipalign").
Flag("-f").
Text("4").
Input(temporary).
@@ -3071,7 +3104,7 @@
rule.DeleteTemporaryFiles()
- rule.Build(pctx, ctx, "uncompress_dex", "uncompress dex")
+ rule.Build("uncompress_dex", "uncompress dex")
} else {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
@@ -3214,3 +3247,31 @@
var BoolDefault = proptools.BoolDefault
var String = proptools.String
var inList = android.InList
+
+// Add class loader context of a given dependency to the given class loader context, provided that
+// all the necessary conditions are met.
+func maybeAddCLCFromDep(depModule android.Module, depTag blueprint.DependencyTag,
+ depName string, clcMap dexpreopt.ClassLoaderContextMap) {
+
+ if dep, ok := depModule.(Dependency); ok {
+ if depTag == libTag {
+ // 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 comp, isComp := depModule.(SdkLibraryComponentDependency); isComp {
+ if compName := comp.OptionalImplicitSdkLibrary(); compName != nil {
+ dep = nil
+ }
+ }
+ } else {
+ // Don't propagate <uses-library> for other dependency tags.
+ dep = nil
+ }
+
+ if dep != nil {
+ clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+ }
+ }
+}
diff --git a/java/java_test.go b/java/java_test.go
index 83db443..f7cf03f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -805,6 +805,165 @@
})
}
+func TestJavaSdkLibraryEnforce(t *testing.T) {
+ partitionToBpOption := func(partition string) string {
+ switch partition {
+ case "system":
+ return ""
+ case "vendor":
+ return "soc_specific: true,"
+ case "product":
+ return "product_specific: true,"
+ default:
+ panic("Invalid partition group name: " + partition)
+ }
+ }
+
+ type testConfigInfo struct {
+ libraryType string
+ fromPartition string
+ toPartition string
+ enforceVendorInterface bool
+ enforceProductInterface bool
+ enforceJavaSdkLibraryCheck bool
+ allowList []string
+ }
+
+ createTestConfig := func(info testConfigInfo) android.Config {
+ bpFileTemplate := `
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ libs: ["bar"],
+ sdk_version: "current",
+ %s
+ }
+
+ %s {
+ name: "bar",
+ srcs: ["bar.java"],
+ sdk_version: "current",
+ %s
+ }
+ `
+
+ bpFile := fmt.Sprintf(bpFileTemplate,
+ partitionToBpOption(info.fromPartition),
+ info.libraryType,
+ partitionToBpOption(info.toPartition))
+
+ config := testConfig(nil, bpFile, nil)
+ configVariables := config.TestProductVariables
+
+ configVariables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
+ if info.enforceVendorInterface {
+ configVariables.DeviceVndkVersion = proptools.StringPtr("current")
+ }
+ configVariables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
+ configVariables.InterPartitionJavaLibraryAllowList = info.allowList
+
+ 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"}
+
+ for _, fromPartition := range partitions {
+ for _, toPartition := range partitions {
+ result = append(result, [2]string{fromPartition, toPartition})
+ }
+ }
+
+ return result
+ }
+
+ 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))
+ }
+ }
+ }
+ }
+
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ allowList: []string{"bar"},
+ }))
+
+ testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ allowList: []string{"foo"},
+ }))
+}
+
func TestDefaults(t *testing.T) {
ctx, _ := testJava(t, `
java_defaults {
diff --git a/java/kotlin.go b/java/kotlin.go
index e8c030a..8067ad5 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -63,9 +63,9 @@
// we can't use the rsp file because it is already being used for srcs.
// Insert a second rule to write out the list of resources to a file.
commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList)
- rule.Build(pctx, ctx, "kotlin_common_srcs_list", "kotlin common_srcs list")
+ rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list")
return android.OptionalPathForPath(commonSrcsList)
}
return android.OptionalPath{}
diff --git a/java/lint.go b/java/lint.go
index 11f92e5..cd2a904 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -176,9 +176,9 @@
// we can't use the rsp file because it is already being used for srcs.
// Insert a second rule to write out the list of resources to a file.
resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
- resListRule := android.NewRuleBuilder()
+ resListRule := android.NewRuleBuilder(pctx, ctx)
resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
- resListRule.Build(pctx, ctx, "lint_resources_list", "lint resources list")
+ resListRule.Build("lint_resources_list", "lint resources list")
deps = append(deps, l.resources...)
}
@@ -192,7 +192,7 @@
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
cmd := rule.Command().
- BuiltTool(ctx, "lint-project-xml").
+ BuiltTool("lint-project-xml").
FlagWithOutput("--project_out ", projectXMLPath).
FlagWithOutput("--config_out ", configXMLPath).
FlagWithArg("--name ", ctx.ModuleName())
@@ -284,7 +284,7 @@
}
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
if l.manifest == nil {
manifest := l.generateManifest(ctx, rule)
@@ -347,7 +347,7 @@
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
- rule.Build(pctx, ctx, "lint", "lint")
+ rule.Build("lint", "lint")
l.outputs = lintOutputs{
html: html,
@@ -511,12 +511,12 @@
return paths[i].String() < paths[j].String()
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().BuiltTool(ctx, "soong_zip").
+ rule.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", outputPath).
FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
FlagWithRspFileInputList("-r ", paths)
- rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+ rule.Build(outputPath.Base(), outputPath.Base())
}
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index cb8e684..9bc821d 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -86,15 +86,15 @@
return
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := platformCompatConfigPath(ctx)
rule.Command().
- BuiltTool(ctx, "process-compat-config").
+ BuiltTool("process-compat-config").
FlagForEachInput("--xml ", compatConfigMetadata).
FlagWithOutput("--merged-config ", outputPath)
- rule.Build(pctx, ctx, "merged-compat-config", "Merge compat config")
+ rule.Build("merged-compat-config", "Merge compat config")
p.metadata = outputPath
}
@@ -106,7 +106,7 @@
}
func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
configFileName := p.Name() + ".xml"
metadataFileName := p.Name() + "_meta.xml"
@@ -115,13 +115,13 @@
path := android.PathForModuleSrc(ctx, String(p.properties.Src))
rule.Command().
- BuiltTool(ctx, "process-compat-config").
+ BuiltTool("process-compat-config").
FlagWithInput("--jar ", path).
FlagWithOutput("--device-config ", p.configFile).
FlagWithOutput("--merged-config ", p.metadataFile)
p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
- rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
+ rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
}
diff --git a/java/proto.go b/java/proto.go
index 4d735eb..cc9abbe 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -34,7 +34,7 @@
outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
@@ -42,13 +42,13 @@
for _, protoFile := range shard {
depFile := srcJarFile.InSameDir(ctx, protoFile.String()+".d")
rule.Command().Text("mkdir -p").Flag(filepath.Dir(depFile.String()))
- android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+ android.ProtoRule(rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
}
// Proto generated java files have an unknown package name in the path, so package the entire output directory
// into a srcjar.
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-jar").
Flag("-write_if_changed").
FlagWithOutput("-o ", srcJarFile).
@@ -66,7 +66,7 @@
ruleDesc += " " + strconv.Itoa(i)
}
- rule.Build(pctx, ctx, ruleName, ruleDesc)
+ rule.Build(ruleName, ruleDesc)
}
return srcJarFiles
diff --git a/java/robolectric.go b/java/robolectric.go
index 62d1d99..c821e5b 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -199,7 +199,7 @@
func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
instrumentedApp *AndroidApp) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
manifest := instrumentedApp.mergedManifestFile
resourceApk := instrumentedApp.outputFile
@@ -213,11 +213,11 @@
Implicit(manifest).
Implicit(resourceApk)
- rule.Build(pctx, ctx, "generate_test_config", "generate test_config.properties")
+ rule.Build("generate_test_config", "generate test_config.properties")
}
func generateSameDirRoboTestConfigJar(ctx android.ModuleContext, outputFile android.ModuleOutPath) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputDir := outputFile.InSameDir(ctx)
configFile := outputDir.Join(ctx, "com/android/tools/test_config.properties")
@@ -230,12 +230,12 @@
Textf(`echo "android_resource_apk=%s.apk"`, ctx.ModuleName()).
Text(") >>").Output(configFile)
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithArg("-C ", outputDir.String()).
FlagWithInput("-f ", configFile).
FlagWithOutput("-o ", outputFile)
- rule.Build(pctx, ctx, "generate_test_config_samedir", "generate test_config.properties")
+ rule.Build("generate_test_config_samedir", "generate test_config.properties")
}
func (r *robolectricTest) generateRoboSrcJar(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -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/sdk.go b/java/sdk.go
index 971791f..32a4b5a 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -544,7 +544,7 @@
commitChangeForRestat(rule, tempPath, combinedAidl)
- rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
+ rule.Build("framework_aidl", "generate framework.aidl")
}
// Creates a version of framework.aidl for the non-updatable part of the platform.
@@ -558,7 +558,7 @@
commitChangeForRestat(rule, tempPath, combinedAidl)
- rule.Build(pctx, ctx, "framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
+ rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
}
func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder {
@@ -586,7 +586,7 @@
}
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
var aidls android.Paths
@@ -597,7 +597,7 @@
rule.Command().
Text("rm -f").Output(aidl)
rule.Command().
- BuiltTool(ctx, "sdkparcelables").
+ BuiltTool("sdkparcelables").
Input(jar).
Output(aidl)
@@ -632,7 +632,7 @@
func createAPIFingerprint(ctx android.SingletonContext) {
out := ApiFingerprintPath(ctx)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("rm -f").Output(out)
@@ -659,7 +659,7 @@
Output(out)
}
- rule.Build(pctx, ctx, "api_fingerprint", "generate api_fingerprint.txt")
+ rule.Build("api_fingerprint", "generate api_fingerprint.txt")
}
func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 32bc077..4e33d74 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2176,12 +2176,12 @@
xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx))
module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > ").
Output(module.outputFilePath)
- rule.Build(pctx, ctx, "java_sdk_xml", "Permission XML")
+ rule.Build("java_sdk_xml", "Permission XML")
module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
}
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
new file mode 100644
index 0000000..2934936
--- /dev/null
+++ b/java/sdk_library_external.go
@@ -0,0 +1,109 @@
+// 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 (
+ "android/soong/android"
+)
+
+type partitionGroup int
+
+// Representation of partition group for checking inter-partition library dependencies.
+// Between system and system_ext, there are no restrictions of dependencies,
+// so we can treat these partitions as the same in terms of inter-partition dependency.
+// Same policy is applied between vendor and odm partiton.
+const (
+ partitionGroupNone partitionGroup = iota
+ // group for system, and system_ext partition
+ partitionGroupSystem
+ // group for vendor and odm partition
+ partitionGroupVendor
+ // product partition
+ partitionGroupProduct
+)
+
+func (g partitionGroup) String() string {
+ switch g {
+ case partitionGroupSystem:
+ return "system"
+ case partitionGroupVendor:
+ return "vendor"
+ case partitionGroupProduct:
+ return "product"
+ }
+
+ return ""
+}
+
+// Get partition group of java module that can be used at inter-partition dependency check.
+// We currently have three groups
+// (system, system_ext) => system partition group
+// (vendor, odm) => vendor partition group
+// (product) => product partition group
+func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
+ // system and system_ext partition can be treated as the same in terms of inter-partition dependency.
+ if j.Platform() || j.SystemExtSpecific() {
+ return partitionGroupSystem
+ }
+
+ // vendor and odm partition can be treated as the same in terms of inter-partition dependency.
+ if j.SocSpecific() || j.DeviceSpecific() {
+ return partitionGroupVendor
+ }
+
+ // product partition is independent.
+ if j.ProductSpecific() {
+ return partitionGroupProduct
+ }
+
+ panic("Cannot determine partition type")
+}
+
+func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool {
+ return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
+}
+
+type javaSdkLibraryEnforceContext interface {
+ Name() string
+ allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
+ partitionGroup(ctx android.EarlyModuleContext) partitionGroup
+}
+
+var _ javaSdkLibraryEnforceContext = (*Module)(nil)
+
+func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) {
+ if dep.allowListedInterPartitionJavaLibrary(ctx) {
+ return
+ }
+
+ // If product interface is not enforced, skip check between system and product partition.
+ // But still need to check between product and vendor partition because product interface flag
+ // just represents enforcement between product and system, and vendor interface enforcement
+ // that is enforced here by precondition is representing enforcement between vendor and other partitions.
+ if !ctx.Config().EnforceProductPartitionInterface() {
+ productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem
+ systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct
+
+ if productToSystem || systemToProduct {
+ return
+ }
+ }
+
+ // If module and dependency library is inter-partition
+ if j.partitionGroup(ctx) != dep.partitionGroup(ctx) {
+ errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead"
+ ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx))
+ }
+}
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 1c44c74..d538ce4 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -68,13 +68,13 @@
inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
l.installDirPath = android.PathForModuleInstall(ctx, "etc")
- linkerConfigRule := android.NewRuleBuilder()
+ linkerConfigRule := android.NewRuleBuilder(pctx, ctx)
linkerConfigRule.Command().
- BuiltTool(ctx, "conv_linker_config").
+ BuiltTool("conv_linker_config").
Flag("proto").
FlagWithInput("-s ", inputFile).
FlagWithOutput("-o ", l.outputFilePath)
- linkerConfigRule.Build(pctx, ctx, "conv_linker_config",
+ linkerConfigRule.Build("conv_linker_config",
"Generate linker config protobuf "+l.outputFilePath.String())
if proptools.BoolDefault(l.properties.Installable, true) {
@@ -102,6 +102,7 @@
entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
+ entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
},
},
}}
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/python/androidmk.go b/python/androidmk.go
index e60c538..60637d3 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -49,7 +49,7 @@
entries.Class = "EXECUTABLES"
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", p.binaryProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
})
base.subAndroidMk(entries, p.pythonInstaller)
}
@@ -58,7 +58,7 @@
entries.Class = "NATIVE_TESTS"
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", p.binaryDecorator.binaryProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
if p.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
}
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/proto.go b/python/proto.go
index b71e047..53ebb58 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -24,17 +24,17 @@
outDir := srcsZipFile.ReplaceExtension(ctx, "tmp")
depFile := srcsZipFile.ReplaceExtension(ctx, "srcszip.d")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
- android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+ android.ProtoRule(rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
// Proto generated python files have an unknown package name in the path, so package the entire output directory
// into a srcszip.
zipCmd := rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", srcsZipFile)
if pkgPath != "" {
zipCmd.FlagWithArg("-P ", pkgPath)
@@ -44,7 +44,7 @@
rule.Command().Text("rm -rf").Flag(outDir.String())
- rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+ rule.Build("protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return srcsZipFile
}
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 4e8b14d..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)
},
},
}
@@ -90,7 +91,7 @@
test.binaryDecorator.AndroidMk(ctx, ret)
ret.Class = "NATIVE_TESTS"
ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test.Properties.Test_suites...)
+ entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
if test.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
diff --git a/rust/binary.go b/rust/binary.go
index af39d38..c2d97f3 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
diff --git a/rust/builder.go b/rust/builder.go
index 6079e30..8ec2da2 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -189,6 +189,7 @@
implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
implicits = append(implicits, deps.StaticLibs...)
implicits = append(implicits, deps.SharedLibs...)
+ 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..4312452 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
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 a536f2a..21df024 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -14,6 +14,7 @@
"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/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..4951d2b
--- /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.SkipInstall()
+ }
+ }
+}
+
+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 mod.VendorProperties.Product_available != nil {
+ 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 mod.VendorProperties.Double_loadable != nil {
+ mctx.PropertyErrorf("double_loadable",
+ "Rust modules do not yet support double loading")
+ }
+
+ coreVariantNeeded := true
+ var vendorVariants []string
+
+ if mod.VendorProperties.Vendor_available != nil {
+ 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 235b4ad..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()
+ 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")
- android.ProtoRule(ctx, rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
+ 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)
- rule.Build(pctx, ctx, "protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
+ // 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")
- proto.BaseSourceProvider.OutputFiles = outputs.Paths()
+ 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())
+
+ // 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..3d70121 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
@@ -109,33 +122,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 +162,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 +181,15 @@
}
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(config android.Config) bool {
return false
}
@@ -260,7 +245,8 @@
CrtEnd android.OptionalPath
// Paths to generated source files
- SrcDeps android.Paths
+ SrcDeps android.Paths
+ srcProviderFiles android.Paths
}
type RustLibraries []RustLibrary
@@ -376,6 +362,7 @@
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
+ &cc.VendorProperties{},
&BindgenProperties{},
&BaseCompilerProperties{},
&BinaryCompilerProperties{},
@@ -472,11 +459,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() {
@@ -496,6 +478,7 @@
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 +598,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.
@@ -956,10 +945,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,7 +1061,29 @@
var _ android.ApexModule = (*Module)(nil)
+func (mod *Module) minSdkVersion() string {
+ return String(mod.Properties.Min_sdk_version)
+}
+
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
}
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..1be3b8a 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
@@ -81,6 +86,7 @@
{
"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 +100,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:-false}"
+ }
+ },
+
+ "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index b8dd7aa..6db870f 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -16,15 +16,16 @@
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
)
# 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..1a33219 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,9 @@
"Safestack": false,
"Ndk_abis": true,
- "Exclude_draft_ndk_apis": true
+ "Exclude_draft_ndk_apis": true,
+
+ "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
m --skip-make ${SOONG_OUT}/ndk.timestamp
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 81425fb..22fe9f6 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -18,8 +18,10 @@
import argparse
import collections
import json
+import os
import linker_config_pb2
+from google.protobuf.descriptor import FieldDescriptor
from google.protobuf.json_format import ParseDict
from google.protobuf.text_format import MessageToString
@@ -43,6 +45,40 @@
print(MessageToString(pb))
+def SystemProvide(args):
+ pb = linker_config_pb2.LinkerConfig()
+ with open(args.source, 'rb') as f:
+ pb.ParseFromString(f.read())
+ libraries = args.value.split()
+
+ def IsInLibPath(lib_name):
+ lib_path = os.path.join(args.system, 'lib', lib_name)
+ lib64_path = os.path.join(args.system, 'lib64', lib_name)
+ return os.path.exists(lib_path) or os.path.islink(lib_path) or os.path.exists(lib64_path) or os.path.islink(lib64_path)
+
+ installed_libraries = list(filter(IsInLibPath, libraries))
+ for item in installed_libraries:
+ if item not in getattr(pb, 'provideLibs'):
+ getattr(pb, 'provideLibs').append(item)
+ with open(args.output, 'wb') as f:
+ f.write(pb.SerializeToString())
+
+
+def Append(args):
+ pb = linker_config_pb2.LinkerConfig()
+ with open(args.source, 'rb') as f:
+ pb.ParseFromString(f.read())
+
+ if getattr(type(pb), args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
+ for value in args.value.split():
+ getattr(pb, args.key).append(value)
+ else:
+ setattr(pb, args.key, args.value)
+
+ with open(args.output, 'wb') as f:
+ f.write(pb.SerializeToString())
+
+
def GetArgParser():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
@@ -73,6 +109,58 @@
help='Source linker configuration file in protobuf.')
print_proto.set_defaults(func=Print)
+ system_provide_libs = subparsers.add_parser(
+ 'systemprovide', help='Append system provide libraries into the configuration.')
+ system_provide_libs.add_argument(
+ '-s',
+ '--source',
+ required=True,
+ type=str,
+ help='Source linker configuration file in protobuf.')
+ system_provide_libs.add_argument(
+ '-o',
+ '--output',
+ required=True,
+ type=str,
+ help='Target linker configuration file to write in protobuf.')
+ system_provide_libs.add_argument(
+ '--value',
+ required=True,
+ type=str,
+ help='Values of the libraries to append. If there are more than one it should be separated by empty space')
+ system_provide_libs.add_argument(
+ '--system',
+ required=True,
+ type=str,
+ help='Path of the system image.')
+ system_provide_libs.set_defaults(func=SystemProvide)
+
+ append = subparsers.add_parser(
+ 'append', help='Append value(s) to given key.')
+ append.add_argument(
+ '-s',
+ '--source',
+ required=True,
+ type=str,
+ help='Source linker configuration file in protobuf.')
+ append.add_argument(
+ '-o',
+ '--output',
+ required=True,
+ type=str,
+ help='Target linker configuration file to write in protobuf.')
+ append.add_argument(
+ '--key',
+ required=True,
+ type=str,
+ help='.')
+ append.add_argument(
+ '--value',
+ required=True,
+ type=str,
+ help='Values of the libraries to append. If there are more than one it should be separated by empty space')
+ append.set_defaults(func=Append)
+
return parser
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
new file mode 100755
index 0000000..f143161
--- /dev/null
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -0,0 +1,72 @@
+#!/bin/bash -e
+
+# Copyright 2020 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Generates NDK API txt file used by Mainline modules. NDK APIs would have value
+# "UND" in Ndx column and have suffix "@LIB_NAME" in Name column.
+# For example, current line llvm-readelf output is:
+# 1: 00000000 0 FUNC GLOBAL DEFAULT UND dlopen@LIBC
+# After the parse function below "dlopen" would be write to the output file.
+printHelp() {
+ echo "**************************** Usage Instructions ****************************"
+ echo "This script is used to generate the Mainline modules used-by NDK symbols."
+ echo ""
+ echo "To run this script use: ./ndk_usedby_module.sh \$BINARY_IMAGE_DIRECTORY \$BINARY_LLVM_PATH \$OUTPUT_FILE_PATH"
+ echo "For example: If all the module image files that you would like to run is under directory '/myModule' and output write to /myModule.txt then the command would be:"
+ echo "./ndk_usedby_module.sh /myModule \$BINARY_LLVM_PATH /myModule.txt"
+}
+
+parseReadelfOutput() {
+ while IFS= read -r line
+ do
+ if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
+ then
+ echo "$line" | sed -r 's/.*UND (.*)@.*/\1/g' >> "$2"
+ fi
+ done < "$1"
+ echo "" >> "$2"
+}
+
+unzipJarAndApk() {
+ tmpUnzippedDir="$1"/tmpUnzipped
+ [[ -e "$tmpUnzippedDir" ]] && rm -rf "$tmpUnzippedDir"
+ mkdir -p "$tmpUnzippedDir"
+ find "$1" -name "*.jar" -exec unzip -o {} -d "$tmpUnzippedDir" \;
+ find "$1" -name "*.apk" -exec unzip -o {} -d "$tmpUnzippedDir" \;
+ find "$tmpUnzippedDir" -name "*.MF" -exec rm {} \;
+}
+
+lookForExecFile() {
+ dir="$1"
+ readelf="$2"
+ find "$dir" -type f -name "*.so" -exec "$2" --dyn-symbols {} >> "$dir"/../tmpReadelf.txt \;
+ find "$dir" -type f -perm /111 ! -name "*.so" -exec "$2" --dyn-symbols {} >> "$dir"/../tmpReadelf.txt \;
+}
+
+if [[ "$1" == "help" ]]
+then
+ printHelp
+elif [[ "$#" -ne 3 ]]
+then
+ echo "Wrong argument length. Expecting 3 argument representing image file directory, llvm-readelf tool path, output path."
+else
+ unzipJarAndApk "$1"
+ lookForExecFile "$1" "$2"
+ tmpReadelfOutput="$1/../tmpReadelf.txt"
+ [[ -e "$3" ]] && rm "$3"
+ parseReadelfOutput "$tmpReadelfOutput" "$3"
+ [[ -e "$tmpReadelfOutput" ]] && rm "$tmpReadelfOutput"
+ rm -rf "$1/tmpUnzipped"
+fi
\ No newline at end of file
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/update.go b/sdk/update.go
index ba63542..377aaae 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -92,7 +92,7 @@
}
func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
- rb := android.NewRuleBuilder()
+ rb := android.NewRuleBuilder(pctx, ctx)
content := gf.content.String()
@@ -108,7 +108,7 @@
Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
rb.Command().
Text("chmod a+x").Output(gf.path)
- rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
+ rb.Build(gf.path.Base(), "Build "+gf.path.Base())
}
// Collect all the members.
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 7e5c344..f86e1fd 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -398,7 +398,7 @@
func(entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
if s.testConfig != nil {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
}
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 828d1cf..6a53414 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -247,16 +247,16 @@
m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
// dump API rule
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
rule.Command().
- BuiltTool(ctx, "sysprop_api_dump").
+ BuiltTool("sysprop_api_dump").
Output(m.dumpedApiFile).
Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
- rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump")
+ rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump")
// check API rule
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
// 1. compares current.txt to api-dump.txt
// current.txt should be identical to api-dump.txt.
@@ -284,7 +284,7 @@
rule.Command().
Text("( ").
- BuiltTool(ctx, "sysprop_api_checker").
+ BuiltTool("sysprop_api_checker").
Input(m.latestApiFile).
Input(m.currentApiFile).
Text(" || ( echo").Flag("-e").
@@ -297,7 +297,7 @@
Text("touch").
Output(m.checkApiFileTimeStamp)
- rule.Build(pctx, ctx, baseModuleName+"_check_api", baseModuleName+" check api")
+ rule.Build(baseModuleName+"_check_api", baseModuleName+" check api")
}
func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
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 cfd0b83..926da31 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -28,7 +28,7 @@
func SetupOutDir(ctx Context, config Config) {
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
- if !config.SkipMake() {
+ if !config.SkipKati() {
// Run soong_build with Kati for a hybrid build, e.g. running the
// AndroidMk singleton and postinstall commands. Communicate this to
// soong_build by writing an empty .soong.kati_enabled marker file in the
@@ -44,7 +44,7 @@
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
- err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
+ err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
if err != nil {
ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
}
@@ -67,8 +67,8 @@
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
- // If we're in SkipMake mode, skip creating this file if it already exists
- if config.SkipMake() {
+ // If we're in SkipKati mode, skip creating this file if it already exists
+ if config.SkipKati() {
if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
return
}
@@ -85,6 +85,7 @@
}
}
+// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
const (
BuildNone = iota
BuildProductConfig = 1 << iota
@@ -97,6 +98,7 @@
BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
)
+// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
func checkProblematicFiles(ctx Context) {
files := []string{"Android.mk", "CleanSpec.mk"}
for _, file := range files {
@@ -108,6 +110,7 @@
}
}
+// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
func checkCaseSensitivity(ctx Context, config Config) {
outDir := config.OutDir()
lowerCase := filepath.Join(outDir, "casecheck.txt")
@@ -115,13 +118,11 @@
lowerData := "a"
upperData := "B"
- err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
- if err != nil {
+ if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw
ctx.Fatalln("Failed to check case sensitivity:", err)
}
- err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
- if err != nil {
+ if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw
ctx.Fatalln("Failed to check case sensitivity:", err)
}
@@ -139,18 +140,15 @@
}
}
-func help(ctx Context, config Config, what int) {
+// help prints a help/usage message, via the build/make/help.sh script.
+func help(ctx Context, config Config) {
cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
cmd.Sandbox = dumpvarsSandbox
cmd.RunAndPrintOrFatal()
}
-// Build the tree. The 'what' argument can be used to chose which components of
-// the build to run.
-func Build(ctx Context, config Config, what int) {
- ctx.Verboseln("Starting build with args:", config.Arguments())
- ctx.Verboseln("Environment:", config.Environment().Environ())
-
+// checkRAM warns if there probably isn't enough RAM to complete a build.
+func checkRAM(ctx Context, config Config) {
if totalRAM := config.TotalRAM(); totalRAM != 0 {
ram := float32(totalRAM) / (1024 * 1024 * 1024)
ctx.Verbosef("Total RAM: %.3vGB", ram)
@@ -166,24 +164,24 @@
ctx.Println("-j value.")
ctx.Println("************************************************************")
} else if ram <= float32(config.Parallel()) {
+ // Want at least 1GB of RAM per job.
ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
ctx.Println("If you run into segfaults or other errors, try a lower -j value")
}
}
+}
+
+// Build the tree. The 'what' argument can be used to chose which components of
+// the build to run, via checking various bitmasks.
+func Build(ctx Context, config Config, what int) {
+ ctx.Verboseln("Starting build with args:", config.Arguments())
+ ctx.Verboseln("Environment:", config.Environment().Environ())
ctx.BeginTrace(metrics.Total, "total")
defer ctx.EndTrace()
- if config.SkipMake() {
- ctx.Verboseln("Skipping Make/Kati as requested")
- what = what & (BuildSoong | BuildNinja)
- }
-
if inList("help", config.Arguments()) {
- help(ctx, config, what)
- return
- } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
- clean(ctx, config)
+ help(ctx, config)
return
}
@@ -191,16 +189,35 @@
buildLock := BecomeSingletonOrFail(ctx, config)
defer buildLock.Unlock()
+ if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
+ clean(ctx, config)
+ return
+ }
+
+ // checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
checkProblematicFiles(ctx)
+ checkRAM(ctx, config)
+
SetupOutDir(ctx, config)
+ // checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
checkCaseSensitivity(ctx, config)
ensureEmptyDirectoriesExist(ctx, config.TempDir())
SetupPath(ctx, config)
+ if config.SkipConfig() {
+ ctx.Verboseln("Skipping Config as requested")
+ what = what &^ BuildProductConfig
+ }
+
+ if config.SkipKati() {
+ ctx.Verboseln("Skipping Kati as requested")
+ what = what &^ BuildKati
+ }
+
if config.StartGoma() {
// Ensure start Goma compiler_proxy
startGoma(ctx, config)
@@ -216,12 +233,16 @@
runMakeProductConfig(ctx, config)
}
+ // Everything below here depends on product config.
+
if inList("installclean", config.Arguments()) ||
inList("install-clean", config.Arguments()) {
installClean(ctx, config)
ctx.Println("Deleted images and staging directories.")
return
- } else if inList("dataclean", config.Arguments()) ||
+ }
+
+ if inList("dataclean", config.Arguments()) ||
inList("data-clean", config.Arguments()) {
dataClean(ctx, config)
ctx.Println("Deleted data files.")
@@ -240,7 +261,7 @@
runKatiBuild(ctx, config)
runKatiPackage(ctx, config)
- ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
+ ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
} else {
// Load last Kati Suffix if it exists
if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
@@ -259,7 +280,7 @@
}
if what&BuildNinja != 0 {
- if !config.SkipMake() {
+ if what&BuildKati != 0 {
installCleanIfNecessary(ctx, config)
}
@@ -267,6 +288,7 @@
runNinja(ctx, config)
}
+ // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
if what&BuildBazel != 0 {
runBazel(ctx, config)
}
@@ -280,16 +302,13 @@
}
subDir := filepath.Join(subDirs...)
- destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+ destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
- err := os.MkdirAll(destDir, 0777)
- if err != nil {
+ if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
-
}
- err = gzipFileToDir(src, destDir)
- if err != nil {
+ if err := gzipFileToDir(src, destDir); err != nil {
ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
}
}
@@ -302,16 +321,13 @@
}
subDir := filepath.Join(subDirs...)
- destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
+ destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
- err := os.MkdirAll(destDir, 0777)
- if err != nil {
+ if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
-
}
- _, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
- if err != nil {
+ if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
}
}
diff --git a/ui/build/config.go b/ui/build/config.go
index 229bd5c..ecca4de 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -46,7 +46,8 @@
verbose bool
checkbuild bool
dist bool
- skipMake bool
+ skipConfig bool
+ skipKati bool
skipSoongTests bool
// From the product config
@@ -64,6 +65,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"
@@ -220,7 +227,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)
@@ -274,16 +281,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
@@ -536,7 +553,10 @@
} else if arg == "showcommands" {
c.verbose = true
} else if arg == "--skip-make" {
- c.skipMake = true
+ c.skipConfig = true
+ c.skipKati = true
+ } else if arg == "--skip-kati" {
+ c.skipKati = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
} else if len(arg) > 0 && arg[0] == '-' {
@@ -693,11 +713,19 @@
}
func (c *configImpl) DistDir() string {
+ if c.UseBazel() {
+ return c.riggedDistDirForBazel
+ } else {
+ return c.distDir
+ }
+}
+
+func (c *configImpl) RealDistDir() string {
return c.distDir
}
func (c *configImpl) NinjaArgs() []string {
- if c.skipMake {
+ if c.skipKati {
return c.arguments
}
return c.ninjaArgs
@@ -740,8 +768,12 @@
return c.verbose
}
-func (c *configImpl) SkipMake() bool {
- return c.skipMake
+func (c *configImpl) SkipKati() bool {
+ return c.skipKati
+}
+
+func (c *configImpl) SkipConfig() bool {
+ return c.skipConfig
}
func (c *configImpl) TargetProduct() string {
@@ -855,13 +887,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 {
@@ -878,14 +904,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()
}
@@ -896,7 +922,7 @@
return v
}
}
- return c.logDir()
+ return c.rbeLogDir()
}
func (c *configImpl) rbeLogPath() string {
@@ -905,7 +931,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 {
@@ -1113,3 +1139,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/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 bb5cbf0..6a93672 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -30,16 +30,33 @@
"android/soong/ui/status"
)
+// This uses Android.bp files and various tools to generate <builddir>/build.ninja.
+//
+// However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build()
+//
+// We want to rely on as few prebuilts as possible, so there is some bootstrapping here.
+//
+// "Microfactory" is a tool for compiling Go code. We use it to build two other tools:
+// - minibp, used to generate build.ninja files. This is really build/blueprint/bootstrap/command.go#Main()
+// - bpglob, used during incremental builds to identify files in a glob that have changed
+//
+// In reality, several build.ninja files are generated and/or used during the bootstrapping and build process.
+// See build/blueprint/bootstrap/doc.go for more information.
+//
func runSoong(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunSoong, "soong")
defer ctx.EndTrace()
+ // Use an anonymous inline function for tracing purposes (this pattern is used several times below).
func() {
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
+ // Use validations to depend on tests.
args := []string{"-n"}
+
if !config.skipSoongTests {
+ // Run tests.
args = append(args, "-t")
}
@@ -139,13 +156,17 @@
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
cmd.RunAndStreamOrFatal()
}
+ // This build generates .bootstrap/build.ninja, which is used in the next step.
ninja("minibootstrap", ".minibootstrap/build.ninja")
+
+ // This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
ninja("bootstrap", ".bootstrap/build.ninja")
soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
@@ -153,7 +174,7 @@
distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
- if !config.SkipMake() {
+ if !config.SkipKati() {
distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
}
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 3164680..41acc26 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -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
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..2a09461 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -39,13 +39,13 @@
type Metrics struct {
metrics soong_metrics_proto.MetricsBase
- EventTracer EventTracer
+ EventTracer *EventTracer
}
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
- EventTracer: &eventTracerImpl{},
+ EventTracer: &EventTracer{},
}
return m
}