Merge "Read the proc status file when PID is given for metrics purpose."
diff --git a/Android.bp b/Android.bp
index 4d69877..866ed25 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,6 +46,7 @@
name: "libatomic",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
@@ -71,8 +72,13 @@
name: "libgcc",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
arch: {
arm: {
@@ -94,6 +100,7 @@
name: "libgcc_stripped",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
ramdisk_available: true,
vendor_ramdisk_available: true,
recovery_available: true,
diff --git a/android/Android.bp b/android/Android.bp
index 2de0ca9..f8c1d55 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -4,8 +4,10 @@
deps: [
"blueprint",
"blueprint-bootstrap",
+ "sbox_proto",
"soong",
"soong-android-soongconfig",
+ "soong-bazel",
"soong-env",
"soong-shared",
"soong-ui-metrics_proto",
@@ -15,13 +17,15 @@
"apex.go",
"api_levels.go",
"arch.go",
+ "arch_list.go",
"bazel_handler.go",
- "bazel_overlay.go",
"config.go",
"csuite_config.go",
"defaults.go",
"defs.go",
- "depset.go",
+ "depset_generic.go",
+ "depset_paths.go",
+ "deptag.go",
"expand.go",
"filegroup.go",
"hooks.go",
@@ -33,17 +37,20 @@
"mutator.go",
"namespace.go",
"neverallow.go",
+ "ninja_deps.go",
"notices.go",
"onceper.go",
"override_module.go",
"package.go",
"package_ctx.go",
+ "packaging.go",
"path_properties.go",
"paths.go",
"phony.go",
"prebuilt.go",
"prebuilt_build_tool.go",
"proto.go",
+ "queryview.go",
"register.go",
"rule_builder.go",
"sandbox.go",
@@ -68,13 +75,16 @@
"config_test.go",
"csuite_config_test.go",
"depset_test.go",
+ "deptag_test.go",
"expand_test.go",
"module_test.go",
"mutator_test.go",
"namespace_test.go",
"neverallow_test.go",
+ "ninja_deps_test.go",
"onceper_test.go",
"package_test.go",
+ "packaging_test.go",
"path_properties_test.go",
"paths_test.go",
"prebuilt_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index cfd7c91..062dcb3 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,24 +243,96 @@
a.EntryMap[name] = append(a.EntryMap[name], value...)
}
-// Compute the list of Make strings to declare phone goals and dist-for-goals
-// calls from the module's dist and dists properties.
-func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
+// 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.
+ copiesForGoals []*copiesForGoals
+}
+
+// getCopiesForGoals returns a copiesForGoals into which copy instructions that
+// must be processed when building one or more of those goals can be added.
+func (d *distContributions) getCopiesForGoals(goals string) *copiesForGoals {
+ copiesForGoals := &copiesForGoals{goals: goals}
+ d.copiesForGoals = append(d.copiesForGoals, copiesForGoals)
+ return copiesForGoals
+}
+
+// Associates a list of dist copy instructions with a set of goals for which they
+// should be run.
+type copiesForGoals struct {
+ // goals are a space separated list of build targets that will trigger the
+ // copy instructions.
+ goals string
+
+ // A list of instructions to copy a module's output files to somewhere in the
+ // dist directory.
+ copies []distCopy
+}
+
+// Adds a copy instruction.
+func (d *copiesForGoals) addCopyInstruction(from Path, dest string) {
+ d.copies = append(d.copies, distCopy{from, dest})
+}
+
+// Instruction on a path that must be copied into the dist.
+type distCopy struct {
+ // The path to copy from.
+ from Path
+
+ // The destination within the dist directory to copy to.
+ dest string
+}
+
+// Compute the contributions that the module makes to the dist.
+func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContributions {
amod := mod.(Module).base()
name := amod.BaseModuleName()
- var ret []string
+ // Collate the set of associated tag/paths available for copying to the dist.
+ // Start with an empty (nil) set.
var availableTaggedDists TaggedDistFiles
+ // Then merge in any that are provided explicitly by the module.
if a.DistFiles != nil {
- availableTaggedDists = a.DistFiles
- } else if a.OutputFile.Valid() {
- availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
- } else {
+ // Merge the DistFiles into the set.
+ availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
+ }
+
+ // If no paths have been provided for the DefaultDistTag and the output file is
+ // valid then add that as the default dist path.
+ if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
+ availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
+ }
+
+ // If the distFiles created by GenerateTaggedDistFiles contains paths for the
+ // DefaultDistTag then that takes priority so delete any existing paths.
+ if _, ok := amod.distFiles[DefaultDistTag]; ok {
+ delete(availableTaggedDists, DefaultDistTag)
+ }
+
+ // Finally, merge the distFiles created by GenerateTaggedDistFiles.
+ availableTaggedDists = availableTaggedDists.merge(amod.distFiles)
+
+ if len(availableTaggedDists) == 0 {
// Nothing dist-able for this module.
return nil
}
+ // Collate the contributions this module makes to the dist.
+ distContributions := &distContributions{}
+
// Iterate over this module's dist structs, merged from the dist and dists properties.
for _, dist := range amod.Dists() {
// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
@@ -204,7 +342,7 @@
var tag string
if dist.Tag == nil {
// If the dist struct does not specify a tag, use the default output files tag.
- tag = ""
+ tag = DefaultDistTag
} else {
tag = *dist.Tag
}
@@ -218,15 +356,15 @@
}
if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
- errorMessage := "Cannot apply dest/suffix for more than one dist " +
- "file for %s goals in module %s. The list of dist files, " +
+ errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
+ "file for %q goals tag %q in module %s. The list of dist files, " +
"which should have a single element, is:\n%s"
- panic(fmt.Errorf(errorMessage, goals, name, tagPaths))
+ panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
}
- ret = append(ret, fmt.Sprintf(".PHONY: %s\n", goals))
+ copiesForGoals := distContributions.getCopiesForGoals(goals)
- // Create dist-for-goals calls for each path in the dist'd files.
+ // Iterate over each path adding a copy instruction to copiesForGoals
for _, path := range tagPaths {
// It's possible that the Path is nil from errant modules. Be defensive here.
if path == nil {
@@ -261,15 +399,43 @@
}
}
+ copiesForGoals.addCopyInstruction(path, dest)
+ }
+ }
+
+ return distContributions
+}
+
+// generateDistContributionsForMake generates make rules that will generate the
+// dist according to the instructions in the supplied distContribution.
+func generateDistContributionsForMake(distContributions *distContributions) []string {
+ var ret []string
+ for _, d := range distContributions.copiesForGoals {
+ ret = append(ret, fmt.Sprintf(".PHONY: %s\n", d.goals))
+ // Create dist-for-goals calls for each of the copy instructions.
+ for _, c := range d.copies {
ret = append(
ret,
- fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", goals, path.String(), dest))
+ fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", d.goals, c.from.String(), c.dest))
}
}
return ret
}
+// Compute the list of Make strings to declare phony goals and dist-for-goals
+// calls from the module's dist and dists properties.
+func (a *AndroidMkEntries) GetDistForGoals(mod blueprint.Module) []string {
+ distContributions := a.getDistContributions(mod)
+ if distContributions == nil {
+ return nil
+ }
+
+ 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()
@@ -333,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) {
@@ -386,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
@@ -410,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{}
}
@@ -417,7 +589,8 @@
type androidMkSingleton struct{}
func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
- if !ctx.Config().EmbeddedInMake() {
+ // Skip if Soong wasn't invoked from Make.
+ if !ctx.Config().KatiEnabled() {
return
}
@@ -427,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])
})
@@ -519,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 {
@@ -551,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 {
@@ -597,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
@@ -639,16 +820,19 @@
}
return !module.Enabled() ||
- module.commonProperties.SkipInstall ||
+ module.commonProperties.HideFromMake ||
// Make does not understand LinuxBionic
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/androidmk_test.go b/android/androidmk_test.go
index a558f45..347b92e 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -18,17 +18,77 @@
"fmt"
"io"
"reflect"
+ "strings"
"testing"
+
+ "github.com/google/blueprint/proptools"
)
type customModule struct {
ModuleBase
- data AndroidMkData
- distFiles TaggedDistFiles
+
+ properties struct {
+ Default_dist_files *string
+ Dist_output_file *bool
+ }
+
+ data AndroidMkData
+ distFiles TaggedDistFiles
+ outputFile OptionalPath
+
+ // The paths that will be used as the default dist paths if no tag is
+ // specified.
+ defaultDistPaths Paths
}
+const (
+ defaultDistFiles_None = "none"
+ defaultDistFiles_Default = "default"
+ defaultDistFiles_Tagged = "tagged"
+)
+
func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- m.distFiles = m.GenerateTaggedDistFiles(ctx)
+
+ // If the dist_output_file: true then create an output file that is stored in
+ // the OutputFile property of the AndroidMkEntry.
+ if proptools.BoolDefault(m.properties.Dist_output_file, true) {
+ path := PathForTesting("dist-output-file.out")
+ m.outputFile = OptionalPathForPath(path)
+
+ // Previous code would prioritize the DistFiles property over the OutputFile
+ // property in AndroidMkEntry when determining the default dist paths.
+ // Setting this first allows it to be overridden based on the
+ // default_dist_files setting replicating that previous behavior.
+ m.defaultDistPaths = Paths{path}
+ }
+
+ // Based on the setting of the default_dist_files property possibly create a
+ // TaggedDistFiles structure that will be stored in the DistFiles property of
+ // the AndroidMkEntry.
+ defaultDistFiles := proptools.StringDefault(m.properties.Default_dist_files, defaultDistFiles_Tagged)
+ switch defaultDistFiles {
+ case defaultDistFiles_None:
+ // Do nothing
+
+ case defaultDistFiles_Default:
+ path := PathForTesting("default-dist.out")
+ m.defaultDistPaths = Paths{path}
+ m.distFiles = MakeDefaultDistFiles(path)
+
+ case defaultDistFiles_Tagged:
+ // Module types that set AndroidMkEntry.DistFiles to the result of calling
+ // GenerateTaggedDistFiles(ctx) relied on no tag being treated as "" which
+ // meant that the default dist paths would be whatever was returned by
+ // OutputFiles(""). In order to preserve that behavior when treating no tag
+ // as being equal to DefaultDistTag this ensures that
+ // OutputFiles(DefaultDistTag) will return the same as OutputFiles("").
+ m.defaultDistPaths = PathsForTesting("one.out")
+
+ // This must be called after setting defaultDistPaths/outputFile as
+ // GenerateTaggedDistFiles calls into OutputFiles(tag) which may use those
+ // fields.
+ m.distFiles = m.GenerateTaggedDistFiles(ctx)
+ }
}
func (m *customModule) AndroidMk() AndroidMkData {
@@ -41,6 +101,12 @@
func (m *customModule) OutputFiles(tag string) (Paths, error) {
switch tag {
+ case DefaultDistTag:
+ if m.defaultDistPaths != nil {
+ return m.defaultDistPaths, nil
+ } else {
+ return nil, fmt.Errorf("default dist tag is not available")
+ }
case "":
return PathsForTesting("one.out"), nil
case ".multiple":
@@ -55,18 +121,43 @@
func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
return []AndroidMkEntries{
{
- Class: "CUSTOM_MODULE",
- DistFiles: m.distFiles,
+ Class: "CUSTOM_MODULE",
+ DistFiles: m.distFiles,
+ OutputFile: m.outputFile,
},
}
}
func customModuleFactory() Module {
module := &customModule{}
+
+ module.AddProperties(&module.properties)
+
InitAndroidModule(module)
return module
}
+// buildConfigAndCustomModuleFoo creates a config object, processes the supplied
+// bp module and then returns the config and the custom module called "foo".
+func buildConfigAndCustomModuleFoo(t *testing.T, bp string) (Config, *customModule) {
+ t.Helper()
+ config := TestConfig(buildDir, nil, bp, nil)
+ config.katiEnabled = true // Enable androidmk Singleton
+
+ ctx := NewTestContext(config)
+ ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ module := ctx.ModuleForTests("foo", "").Module().(*customModule)
+ return config, module
+}
+
func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
bp := `
custom {
@@ -77,20 +168,7 @@
}
`
- config := TestConfig(buildDir, nil, bp, nil)
- config.inMake = true // Enable androidmk Singleton
-
- ctx := NewTestContext()
- ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register(config)
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- m := ctx.ModuleForTests("foo", "").Module().(*customModule)
+ _, m := buildConfigAndCustomModuleFoo(t, bp)
assertEqual := func(expected interface{}, actual interface{}) {
if !reflect.DeepEqual(expected, actual) {
@@ -102,101 +180,29 @@
assertEqual([]string{"qux"}, m.data.Target_required)
}
-func TestGetDistForGoals(t *testing.T) {
- testCases := []struct {
- bp string
- expectedAndroidMkLines []string
- }{
- {
- bp: `
- custom {
- name: "foo",
- dist: {
- targets: ["my_goal"]
- }
- }
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_goal\n",
- "$(call dist-for-goals,my_goal,one.out:one.out)\n",
- },
- },
- {
- bp: `
- custom {
- name: "foo",
- dist: {
- targets: ["my_goal"],
- tag: ".another-tag",
- }
- }
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_goal\n",
- "$(call dist-for-goals,my_goal,another.out:another.out)\n",
- },
- },
- {
- bp: `
- custom {
- name: "foo",
- dists: [
- {
- targets: ["my_goal"],
- tag: ".another-tag",
- },
- ],
- }
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_goal\n",
- "$(call dist-for-goals,my_goal,another.out:another.out)\n",
- },
- },
- {
- bp: `
- custom {
- name: "foo",
- dists: [
- {
- targets: ["my_goal"],
- },
- {
- targets: ["my_second_goal", "my_third_goal"],
- },
- ],
- }
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_goal\n",
- "$(call dist-for-goals,my_goal,one.out:one.out)\n",
- ".PHONY: my_second_goal my_third_goal\n",
- "$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
- },
- },
- {
- bp: `
- custom {
- name: "foo",
- dist: {
- targets: ["my_goal"],
+func TestGenerateDistContributionsForMake(t *testing.T) {
+ dc := &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ distCopyForTest("two.out", "other.out"),
},
- dists: [
- {
- targets: ["my_second_goal", "my_third_goal"],
- },
- ],
- }
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_second_goal my_third_goal\n",
- "$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
- ".PHONY: my_goal\n",
- "$(call dist-for-goals,my_goal,one.out:one.out)\n",
},
},
- {
- bp: `
+ }
+
+ makeOutput := generateDistContributionsForMake(dc)
+
+ assertStringEquals(t, `.PHONY: my_goal
+$(call dist-for-goals,my_goal,one.out:one.out)
+$(call dist-for-goals,my_goal,two.out:other.out)
+`, strings.Join(makeOutput, ""))
+}
+
+func TestGetDistForGoals(t *testing.T) {
+ bp := `
custom {
name: "foo",
dist: {
@@ -228,64 +234,522 @@
},
],
}
- `,
- expectedAndroidMkLines: []string{
- ".PHONY: my_second_goal\n",
- "$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
- "$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
- ".PHONY: my_third_goal\n",
- "$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
- ".PHONY: my_fourth_goal\n",
- "$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
- ".PHONY: my_fifth_goal\n",
- "$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
- ".PHONY: my_sixth_goal\n",
- "$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
- ".PHONY: my_goal my_other_goal\n",
- "$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
- "$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
- },
- },
+ `
+
+ expectedAndroidMkLines := []string{
+ ".PHONY: my_second_goal\n",
+ "$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
+ "$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
+ ".PHONY: my_third_goal\n",
+ "$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
+ ".PHONY: my_fourth_goal\n",
+ "$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
+ ".PHONY: my_fifth_goal\n",
+ "$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
+ ".PHONY: my_sixth_goal\n",
+ "$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
+ ".PHONY: my_goal my_other_goal\n",
+ "$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
+ "$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
}
- for _, testCase := range testCases {
- config := TestConfig(buildDir, nil, testCase.bp, nil)
- config.inMake = true // Enable androidmk Singleton
+ config, module := buildConfigAndCustomModuleFoo(t, bp)
+ entries := AndroidMkEntriesForTest(t, config, "", module)
+ if len(entries) != 1 {
+ t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
+ }
+ androidMkLines := entries[0].GetDistForGoals(module)
- ctx := NewTestContext()
- ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register(config)
-
- _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
- FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- FailIfErrored(t, errs)
-
- module := ctx.ModuleForTests("foo", "").Module().(*customModule)
- entries := AndroidMkEntriesForTest(t, config, "", module)
- if len(entries) != 1 {
- t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
- }
- androidMkLines := entries[0].GetDistForGoals(module)
-
- if len(androidMkLines) != len(testCase.expectedAndroidMkLines) {
+ if len(androidMkLines) != len(expectedAndroidMkLines) {
+ t.Errorf(
+ "Expected %d AndroidMk lines, got %d:\n%v",
+ len(expectedAndroidMkLines),
+ len(androidMkLines),
+ androidMkLines,
+ )
+ }
+ for idx, line := range androidMkLines {
+ expectedLine := expectedAndroidMkLines[idx]
+ if line != expectedLine {
t.Errorf(
- "Expected %d AndroidMk lines, got %d:\n%v",
- len(testCase.expectedAndroidMkLines),
- len(androidMkLines),
- androidMkLines,
+ "Expected AndroidMk line to be '%s', got '%s'",
+ expectedLine,
+ line,
)
}
- for idx, line := range androidMkLines {
- expectedLine := testCase.expectedAndroidMkLines[idx]
- if line != expectedLine {
- t.Errorf(
- "Expected AndroidMk line to be '%s', got '%s'",
- line,
- expectedLine,
- )
+ }
+}
+
+func distCopyForTest(from, to string) distCopy {
+ return distCopy{PathForTesting(from), to}
+}
+
+func TestGetDistContributions(t *testing.T) {
+ compareContributions := func(d1 *distContributions, d2 *distContributions) error {
+ if d1 == nil || d2 == nil {
+ if d1 != d2 {
+ return fmt.Errorf("pointer mismatch, expected both to be nil but they were %p and %p", d1, d2)
+ } else {
+ return nil
}
}
+ if expected, actual := len(d1.copiesForGoals), len(d2.copiesForGoals); expected != actual {
+ return fmt.Errorf("length mismatch, expected %d found %d", expected, actual)
+ }
+
+ for i, copies1 := range d1.copiesForGoals {
+ copies2 := d2.copiesForGoals[i]
+ if expected, actual := copies1.goals, copies2.goals; expected != actual {
+ return fmt.Errorf("goals mismatch at position %d: expected %q found %q", i, expected, actual)
+ }
+
+ if expected, actual := len(copies1.copies), len(copies2.copies); expected != actual {
+ return fmt.Errorf("length mismatch in copy instructions at position %d, expected %d found %d", i, expected, actual)
+ }
+
+ for j, c1 := range copies1.copies {
+ c2 := copies2.copies[j]
+ if expected, actual := NormalizePathForTesting(c1.from), NormalizePathForTesting(c2.from); expected != actual {
+ return fmt.Errorf("paths mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
+ }
+
+ if expected, actual := c1.dest, c2.dest; expected != actual {
+ return fmt.Errorf("dest mismatch at position %d.%d: expected %q found %q", i, j, expected, actual)
+ }
+ }
+ }
+
+ return nil
}
+
+ formatContributions := func(d *distContributions) string {
+ buf := &strings.Builder{}
+ if d == nil {
+ fmt.Fprint(buf, "nil")
+ } else {
+ for _, copiesForGoals := range d.copiesForGoals {
+ fmt.Fprintf(buf, " Goals: %q {\n", copiesForGoals.goals)
+ for _, c := range copiesForGoals.copies {
+ fmt.Fprintf(buf, " %s -> %s\n", NormalizePathForTesting(c.from), c.dest)
+ }
+ fmt.Fprint(buf, " }\n")
+ }
+ }
+ return buf.String()
+ }
+
+ testHelper := func(t *testing.T, name, bp string, expectedContributions *distContributions) {
+ t.Helper()
+ t.Run(name, func(t *testing.T) {
+ t.Helper()
+
+ config, module := buildConfigAndCustomModuleFoo(t, bp)
+ entries := AndroidMkEntriesForTest(t, config, "", module)
+ if len(entries) != 1 {
+ t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
+ }
+ distContributions := entries[0].getDistContributions(module)
+
+ if err := compareContributions(expectedContributions, distContributions); err != nil {
+ t.Errorf("%s\nExpected Contributions\n%sActualContributions\n%s",
+ err,
+ formatContributions(expectedContributions),
+ formatContributions(distContributions))
+ }
+ })
+ }
+
+ testHelper(t, "dist-without-tag", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal"]
+ }
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "dist-with-tag", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal"],
+ tag: ".another-tag",
+ }
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("another.out", "another.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "dists-with-tag", `
+ custom {
+ name: "foo",
+ dists: [
+ {
+ targets: ["my_goal"],
+ tag: ".another-tag",
+ },
+ ],
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("another.out", "another.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "multiple-dists-with-and-without-tag", `
+ custom {
+ name: "foo",
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_second_goal", "my_third_goal"],
+ },
+ ],
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ {
+ goals: "my_second_goal my_third_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "dist-plus-dists-without-tags", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal"],
+ },
+ dists: [
+ {
+ targets: ["my_second_goal", "my_third_goal"],
+ },
+ ],
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_second_goal my_third_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "dist-plus-dists-with-tags", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal", "my_other_goal"],
+ tag: ".multiple",
+ },
+ dists: [
+ {
+ targets: ["my_second_goal"],
+ tag: ".multiple",
+ },
+ {
+ targets: ["my_third_goal"],
+ dir: "test/dir",
+ },
+ {
+ targets: ["my_fourth_goal"],
+ suffix: ".suffix",
+ },
+ {
+ targets: ["my_fifth_goal"],
+ dest: "new-name",
+ },
+ {
+ targets: ["my_sixth_goal"],
+ dest: "new-name",
+ dir: "some/dir",
+ suffix: ".suffix",
+ },
+ ],
+ }
+`,
+ &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_second_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ {
+ goals: "my_third_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "test/dir/one.out"),
+ },
+ },
+ {
+ goals: "my_fourth_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.suffix.out"),
+ },
+ },
+ {
+ goals: "my_fifth_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "new-name"),
+ },
+ },
+ {
+ goals: "my_sixth_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "some/dir/new-name.suffix"),
+ },
+ },
+ {
+ goals: "my_goal my_other_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ // The above test the default values of default_dist_files and use_output_file.
+
+ // The following tests explicitly test the different combinations of those settings.
+ testHelper(t, "tagged-dist-files-no-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "tagged",
+ dist_output_file: false,
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "default-dist-files-no-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "default",
+ dist_output_file: false,
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("default-dist.out", "default-dist.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "no-dist-files-no-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "none",
+ dist_output_file: false,
+ dists: [
+ // The following is silently ignored because there is not default file
+ // in either the dist files or the output file.
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "tagged-dist-files-default-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "tagged",
+ dist_output_file: true,
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "default-dist-files-default-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "default",
+ dist_output_file: true,
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("default-dist.out", "default-dist.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
+
+ testHelper(t, "no-dist-files-default-output", `
+ custom {
+ name: "foo",
+ default_dist_files: "none",
+ dist_output_file: true,
+ dists: [
+ {
+ targets: ["my_goal"],
+ },
+ {
+ targets: ["my_goal"],
+ tag: ".multiple",
+ },
+ ],
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("dist-output-file.out", "dist-output-file.out"),
+ },
+ },
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("two.out", "two.out"),
+ distCopyForTest("three/four.out", "four.out"),
+ },
+ },
+ },
+ })
}
diff --git a/android/apex.go b/android/apex.go
index e70ec4f..f6eca86 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -25,32 +25,54 @@
)
var (
+ // This is the sdk version when APEX was first introduced
SdkVersion_Android10 = uncheckedFinalApiLevel(29)
)
-// ApexInfo describes the metadata common to all modules in an apexBundle.
+// ApexInfo describes the metadata about one or more apexBundles that an apex variant of a module is
+// part of. When an apex variant is created, the variant is associated with one apexBundle. But
+// when multiple apex variants are merged for deduping (see mergeApexVariations), this holds the
+// information about the apexBundles that are merged together.
+// Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)`
type ApexInfo struct {
- // Name of the apex variation that this module is mutated into, or "" for
- // a platform variant. Note that a module can be included in multiple APEXes,
- // in which case, the module is mutated into one or more variants, each of
- // which is for one or more APEXes.
+ // Name of the apex variation that this module (i.e. the apex variant of the module) is
+ // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be
+ // included in multiple APEXes, in which case, the module is mutated into one or more
+ // variants, each of which is for an APEX. The variants then can later be deduped if they
+ // don't need to be compiled differently. This is an optimization done in
+ // mergeApexVariations.
ApexVariationName string
- // Serialized ApiLevel. Use via MinSdkVersion() method. Cannot be stored in
- // its struct form because this is cloned into properties structs, and
- // ApiLevel has private members.
+ // Serialized ApiLevel that this module has to support at minimum. Should be accessed via
+ // MinSdkVersion() method. Cannot be stored in its struct form because this is cloned into
+ // properties structs, and ApiLevel has private members.
MinSdkVersionStr string
- // True if the module comes from an updatable APEX.
- Updatable bool
+ // True if this module comes from an updatable apexBundle.
+ Updatable bool
+
+ // The list of SDK modules that the containing apexBundle depends on.
RequiredSdks SdkRefs
- InApexes []string
+ // List of apexBundles that this apex variant of the module is associated with. Initially,
+ // the size of this list is one because one apex variant is associated with one apexBundle.
+ // When multiple apex variants are merged in mergeApexVariations, ApexInfo struct of the
+ // merged variant holds the list of apexBundles that are merged together.
+ InApexes []string
+
+ // Pointers to the ApexContents struct each of which is for apexBundle modules that this
+ // module is part of. The ApexContents gives information about which modules the apexBundle
+ // has and whether a module became part of the apexBundle via a direct dependency or not.
ApexContents []*ApexContents
}
var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
+// mergedName gives the name of the alias variation that will be used when multiple apex variations
+// of a module can be deduped into one variation. For example, if libfoo is included in both apex.a
+// and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't
+// have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
+// are configured to have the same alias variation named apex29.
func (i ApexInfo) mergedName(ctx PathContext) string {
name := "apex" + strconv.Itoa(i.MinSdkVersion(ctx).FinalOrFutureInt())
for _, sdk := range i.RequiredSdks {
@@ -59,14 +81,19 @@
return name
}
-func (this *ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
- return ApiLevelOrPanic(ctx, this.MinSdkVersionStr)
+// MinSdkVersion gives the api level that this module has to support at minimum. This is from the
+// min_sdk_version property of the containing apexBundle.
+func (i ApexInfo) MinSdkVersion(ctx PathContext) ApiLevel {
+ return ApiLevelOrPanic(ctx, i.MinSdkVersionStr)
}
+// IsForPlatform tells whether this module is for the platform or not. If false is returned, it
+// means that this apex variant of the module is built for an APEX.
func (i ApexInfo) IsForPlatform() bool {
return i.ApexVariationName == ""
}
+// InApex tells whether this apex variant of the module is part of the given apexBundle or not.
func (i ApexInfo) InApex(apex string) bool {
for _, a := range i.InApexes {
if a == apex {
@@ -76,102 +103,110 @@
return false
}
-// ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has
-// access to APEX internals.
+// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
+// module is not part of the APEX - and thus has access to APEX internals.
type ApexTestForInfo struct {
ApexContents []*ApexContents
}
var ApexTestForInfoProvider = blueprint.NewMutatorProvider(ApexTestForInfo{}, "apex_test_for")
-// Extracted from ApexModule to make it easier to define custom subsets of the
-// ApexModule interface and improve code navigation within the IDE.
+// DepIsInSameApex defines an interface that should be used to determine whether a given dependency
+// should be considered as part of the same APEX as the current module or not. Note: this was
+// extracted from ApexModule to make it easier to define custom subsets of the ApexModule interface
+// and improve code navigation within the IDE.
type DepIsInSameApex interface {
- // DepIsInSameApex tests if the other module 'dep' is installed to the same
- // APEX as this module
+ // DepIsInSameApex tests if the other module 'dep' is considered as part of the same APEX as
+ // this module. For example, a static lib dependency usually returns true here, while a
+ // shared lib dependency to a stub library returns false.
DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
}
-// ApexModule is the interface that a module type is expected to implement if
-// the module has to be built differently depending on whether the module
-// is destined for an apex or not (installed to one of the regular partitions).
+// ApexModule is the interface that a module type is expected to implement if the module has to be
+// built differently depending on whether the module is destined for an APEX or not (i.e., installed
+// to one of the regular partitions).
//
-// Native shared libraries are one such module type; when it is built for an
-// APEX, it should depend only on stable interfaces such as NDK, stable AIDL,
-// or C APIs from other APEXs.
+// Native shared libraries are one such module type; when it is built for an APEX, it should depend
+// only on stable interfaces such as NDK, stable AIDL, or C APIs from other APEXes.
//
-// A module implementing this interface will be mutated into multiple
-// variations by apex.apexMutator if it is directly or indirectly included
-// in one or more APEXs. Specifically, if a module is included in apex.foo and
-// apex.bar then three apex variants are created: platform, apex.foo and
-// apex.bar. The platform variant is for the regular partitions
-// (e.g., /system or /vendor, etc.) while the other two are for the APEXs,
-// respectively.
+// A module implementing this interface will be mutated into multiple variations by apex.apexMutator
+// if it is directly or indirectly included in one or more APEXes. Specifically, if a module is
+// included in apex.foo and apex.bar then three apex variants are created: platform, apex.foo and
+// apex.bar. The platform variant is for the regular partitions (e.g., /system or /vendor, etc.)
+// while the other two are for the APEXs, respectively. The latter two variations can be merged (see
+// mergedName) when the two APEXes have the same min_sdk_version requirement.
type ApexModule interface {
Module
DepIsInSameApex
apexModuleBase() *ApexModuleBase
- // Marks that this module should be built for the specified APEX.
- // Call this before apex.apexMutator is run.
+ // Marks that this module should be built for the specified APEX. Call this BEFORE
+ // apex.apexMutator is run.
BuildForApex(apex ApexInfo)
- // Returns true if this module is present in any APEXes
- // directly or indirectly.
- // Call this after apex.apexMutator is run.
+ // Returns true if this module is present in any APEX either directly or indirectly. Call
+ // this after apex.apexMutator is run.
InAnyApex() bool
- // Returns true if this module is directly in any APEXes.
- // Call this after apex.apexMutator is run.
+ // Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is
+ // run.
DirectlyInAnyApex() bool
- // Returns true if any variant of this module is directly in any APEXes.
- // 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. APEX variants are
- // created only for the modules that returns true here. This is useful
- // for not creating APEX variants for certain types of shared libraries
- // such as NDK stubs.
+ // 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
+ // true here. This is useful for not creating APEX variants for certain types of shared
+ // libraries such as NDK stubs.
CanHaveApexVariants() bool
- // Tests if this module can be installed to APEX as a file. For example,
- // this would return true for shared libs while return false for static
- // libs.
+ // Tests if this module can be installed to APEX as a file. For example, this would return
+ // true for shared libs while return false for static libs because static libs are not
+ // installable module (but it can still be mutated for APEX)
IsInstallableToApex() bool
- // Tests if this module is available for the specified APEX or ":platform"
+ // Tests if this module is available for the specified APEX or ":platform". This is from the
+ // apex_available property of the module.
AvailableFor(what string) bool
- // Return true if this module is not available to platform (i.e. apex_available
- // property doesn't have "//apex_available:platform"), or shouldn't be available
- // to platform, which is the case when this module depends on other module that
- // isn't available to platform.
+ // Returns true if this module is not available to platform (i.e. apex_available property
+ // doesn't have "//apex_available:platform"), or shouldn't be available to platform, which
+ // is the case when this module depends on other module that isn't available to platform.
NotAvailableForPlatform() bool
- // Mark that this module is not available to platform. Set by the
+ // Marks that this module is not available to platform. Set by the
// check-platform-availability mutator in the apex package.
SetNotAvailableForPlatform()
- // 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.
+ // Returns the list of APEXes that this module is a test for. The module has access to the
+ // private part of the listed APEXes even when it is not included in the APEXes. This by
+ // default returns nil. A module type should override the default implementation. For
+ // example, cc_test module type returns the value of test_for here.
TestFor() []string
- // Returns nil if this module supports sdkVersion
- // Otherwise, returns error with reason
+ // Returns nil (success) if this module should support the given sdk version. Returns an
+ // error if not. No default implementation is provided for this method. A module type
+ // implementing this interface should provide an implementation. A module supports an sdk
+ // version when the module's min_sdk_version is equal to or less than the given sdk version.
ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion ApiLevel) error
- // Returns true if this module needs a unique variation per apex, for example if
- // use_apex_name_macro is set.
+ // Returns true if this module needs a unique variation per apex, effectively disabling the
+ // deduping. This is turned on when, for example if use_apex_name_macro is set so that each
+ // apex variant should be built with different macro definitions.
UniqueApexVariations() bool
}
+// Properties that are common to all module types implementing ApexModule interface.
type ApexProperties struct {
- // Availability of this module in APEXes. Only the listed APEXes can contain
- // this module. If the module has stubs then other APEXes and the platform may
- // access it through them (subject to visibility).
+ // Availability of this module in APEXes. Only the listed APEXes can contain this module. If
+ // the module has stubs then other APEXes and the platform may access it through them
+ // (subject to visibility).
//
// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
// "//apex_available:platform" refers to non-APEX partitions like "system.img".
@@ -179,29 +214,28 @@
// Default is ["//apex_available:platform"].
Apex_available []string
- // 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.
- AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"`
-
- // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the
- // platform) of this module is directly in any APEX.
- DirectlyInAnyApex bool `blueprint:"mutated"`
-
- // DirectlyInAnyApex is true if any APEX variant (including the "" variant used for the
- // platform) of this module is directly or indirectly in any APEX.
+ // See ApexModule.InAnyApex()
InAnyApex bool `blueprint:"mutated"`
+ // See ApexModule.DirectlyInAnyApex()
+ DirectlyInAnyApex bool `blueprint:"mutated"`
+
+ // 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()
NotAvailableForPlatform bool `blueprint:"mutated"`
+ // See ApexModule.UniqueApexVariants()
UniqueApexVariationsForDeps bool `blueprint:"mutated"`
}
-// Marker interface that identifies dependencies that are excluded from APEX
-// contents.
+// Marker interface that identifies dependencies that are excluded from APEX contents.
type ExcludeFromApexContentsTag interface {
blueprint.DependencyTag
@@ -209,85 +243,122 @@
ExcludeFromApexContents()
}
-// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex
-// state from the parent to the child. For example, stubs libraries are marked as
-// DirectlyInAnyApex if their implementation is in an apex.
+// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
+// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
+// their implementation is in an apex.
type CopyDirectlyInAnyApexTag interface {
blueprint.DependencyTag
+ // Method that differentiates this interface from others.
CopyDirectlyInAnyApex()
}
-// Provides default implementation for the ApexModule interface. APEX-aware
+// ApexModuleBase provides the default implementation for the ApexModule interface. APEX-aware
// modules are expected to include this struct and call InitApexModule().
type ApexModuleBase struct {
ApexProperties ApexProperties
canHaveApexVariants bool
- apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator
- apexVariations []ApexInfo
+ apexInfos []ApexInfo
+ apexInfosLock sync.Mutex // protects apexInfos during parallel apexInfoMutator
}
+// Initializes ApexModuleBase struct. Not calling this (even when inheriting from ApexModuleBase)
+// prevents the module from being mutated for apexBundle.
+func InitApexModule(m ApexModule) {
+ base := m.apexModuleBase()
+ base.canHaveApexVariants = true
+
+ m.AddProperties(&base.ApexProperties)
+}
+
+// Implements ApexModule
func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase {
return m
}
+// Implements ApexModule
func (m *ApexModuleBase) ApexAvailable() []string {
return m.ApexProperties.Apex_available
}
-func (m *ApexModuleBase) TestFor() []string {
- // To be implemented by concrete types inheriting ApexModuleBase
- return nil
-}
-
-func (m *ApexModuleBase) UniqueApexVariations() bool {
- return false
-}
-
+// Implements ApexModule
func (m *ApexModuleBase) BuildForApex(apex ApexInfo) {
- m.apexVariationsLock.Lock()
- defer m.apexVariationsLock.Unlock()
- for _, v := range m.apexVariations {
+ m.apexInfosLock.Lock()
+ defer m.apexInfosLock.Unlock()
+ for _, v := range m.apexInfos {
if v.ApexVariationName == apex.ApexVariationName {
return
}
}
- m.apexVariations = append(m.apexVariations, apex)
+ m.apexInfos = append(m.apexInfos, apex)
}
-func (m *ApexModuleBase) DirectlyInAnyApex() bool {
- return m.ApexProperties.DirectlyInAnyApex
-}
-
-func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool {
- return m.ApexProperties.AnyVariantDirectlyInAnyApex
-}
-
+// Implements ApexModule
func (m *ApexModuleBase) InAnyApex() bool {
return m.ApexProperties.InAnyApex
}
+// Implements ApexModule
+func (m *ApexModuleBase) DirectlyInAnyApex() bool {
+ return m.ApexProperties.DirectlyInAnyApex
+}
+
+// Implements ApexModule
+func (m *ApexModuleBase) NotInPlatform() bool {
+ return m.ApexProperties.AnyVariantDirectlyInAnyApex || !m.AvailableFor(AvailableToPlatform)
+}
+
+// Implements ApexModule
func (m *ApexModuleBase) CanHaveApexVariants() bool {
return m.canHaveApexVariants
}
+// Implements ApexModule
func (m *ApexModuleBase) IsInstallableToApex() bool {
- // should be overriden if needed
+ // If needed, this will bel overridden by concrete types inheriting
+ // ApexModuleBase
return false
}
+// Implements ApexModule
+func (m *ApexModuleBase) TestFor() []string {
+ // If needed, this will be overridden by concrete types inheriting
+ // ApexModuleBase
+ return nil
+}
+
+// Implements ApexModule
+func (m *ApexModuleBase) UniqueApexVariations() bool {
+ // If needed, this will bel overridden by concrete types inheriting
+ // ApexModuleBase
+ return false
+}
+
+// Implements ApexModule
+func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool {
+ // By default, if there is a dependency from A to B, we try to include both in the same
+ // APEX, unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning
+ // true. This is overridden by some module types like apex.ApexBundle, cc.Module,
+ // java.Module, etc.
+ return true
+}
+
const (
AvailableToPlatform = "//apex_available:platform"
AvailableToAnyApex = "//apex_available:anyapex"
AvailableToGkiApex = "com.android.gki.*"
)
+// CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
+// availability is empty, it defaults to ["//apex_available:platform"] which means "available to the
+// platform but not available to any APEX". When the list is not empty, `what` is matched against
+// the list. If there is any matching element in the list, thus function returns true. The special
+// availability "//apex_available:anyapex" matches with anything except for
+// "//apex_available:platform".
func CheckAvailableForApex(what string, apex_available []string) bool {
if len(apex_available) == 0 {
- // apex_available defaults to ["//apex_available:platform"],
- // which means 'available to the platform but no apexes'.
return what == AvailableToPlatform
}
return InList(what, apex_available) ||
@@ -295,25 +366,22 @@
(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available))
}
+// Implements ApexModule
func (m *ApexModuleBase) AvailableFor(what string) bool {
return CheckAvailableForApex(what, m.ApexProperties.Apex_available)
}
+// Implements ApexModule
func (m *ApexModuleBase) NotAvailableForPlatform() bool {
return m.ApexProperties.NotAvailableForPlatform
}
+// Implements ApexModule
func (m *ApexModuleBase) SetNotAvailableForPlatform() {
m.ApexProperties.NotAvailableForPlatform = true
}
-func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool {
- // By default, if there is a dependency from A to B, we try to include both in the same APEX,
- // unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true.
- // This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc.
- return true
-}
-
+// This function makes sure that the apex_available property is valid
func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
for _, n := range m.ApexProperties.Apex_available {
if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
@@ -331,22 +399,23 @@
func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byApexName) Less(i, j int) bool { return a[i].ApexVariationName < a[j].ApexVariationName }
-// mergeApexVariations deduplicates APEX variations that would build identically into a common
-// variation. It returns the reduced list of variations and a list of aliases from the original
+// mergeApexVariations deduplicates apex variations that would build identically into a common
+// variation. It returns the reduced list of variations and a list of aliases from the original
// variation names to the new variation names.
-func mergeApexVariations(ctx PathContext, apexVariations []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
- sort.Sort(byApexName(apexVariations))
+func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2]string) {
+ sort.Sort(byApexName(apexInfos))
seen := make(map[string]int)
- for _, apexInfo := range apexVariations {
+ for _, apexInfo := range apexInfos {
apexName := apexInfo.ApexVariationName
mergedName := apexInfo.mergedName(ctx)
if index, exists := seen[mergedName]; exists {
+ // Variants having the same mergedName are deduped
merged[index].InApexes = append(merged[index].InApexes, apexName)
merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
} else {
seen[mergedName] = len(merged)
- apexInfo.ApexVariationName = apexInfo.mergedName(ctx)
+ apexInfo.ApexVariationName = mergedName
apexInfo.InApexes = CopyOf(apexInfo.InApexes)
apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
merged = append(merged, apexInfo)
@@ -356,75 +425,78 @@
return merged, aliases
}
+// CreateApexVariations mutates a given module into multiple apex variants each of which is for an
+// apexBundle (and/or the platform) where the module is part of.
func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Module {
base := module.apexModuleBase()
- if len(base.apexVariations) > 0 {
- base.checkApexAvailableProperty(mctx)
- var apexVariations []ApexInfo
- var aliases [][2]string
- if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
- apexVariations, aliases = mergeApexVariations(mctx, base.apexVariations)
- } else {
- apexVariations = base.apexVariations
- }
- // base.apexVariations is only needed to propagate the list of apexes from
- // apexDepsMutator 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.apexVariations = nil
-
- sort.Sort(byApexName(apexVariations))
- variations := []string{}
- variations = append(variations, "") // Original variation for platform
- for _, apex := range apexVariations {
- variations = append(variations, apex.ApexVariationName)
- }
-
- defaultVariation := ""
- mctx.SetDefaultDependencyVariation(&defaultVariation)
-
- var inApex ApexMembership
- for _, a := range apexVariations {
- for _, apexContents := range a.ApexContents {
- inApex = inApex.merge(apexContents.contents[mctx.ModuleName()])
- }
- }
-
- base.ApexProperties.InAnyApex = true
- base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
-
- modules := mctx.CreateVariations(variations...)
- for i, mod := range modules {
- platformVariation := i == 0
- if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
- // Do not install the module for platform, but still allow it to output
- // uninstallable AndroidMk entries in certain cases when they have
- // side effects.
- mod.MakeUninstallable()
- }
- if !platformVariation {
- mctx.SetVariationProvider(mod, ApexInfoProvider, apexVariations[i-1])
- }
- }
-
- for _, alias := range aliases {
- mctx.CreateAliasVariation(alias[0], alias[1])
- }
-
- return modules
+ // Shortcut
+ if len(base.apexInfos) == 0 {
+ return nil
}
- return nil
+
+ // Do some validity checks.
+ // TODO(jiyong): is this the right place?
+ base.checkApexAvailableProperty(mctx)
+
+ var apexInfos []ApexInfo
+ var aliases [][2]string
+ if !mctx.Module().(ApexModule).UniqueApexVariations() && !base.ApexProperties.UniqueApexVariationsForDeps {
+ apexInfos, aliases = mergeApexVariations(mctx, base.apexInfos)
+ } else {
+ apexInfos = base.apexInfos
+ }
+ // 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
+ sort.Sort(byApexName(apexInfos))
+
+ var inApex ApexMembership
+ for _, a := range apexInfos {
+ for _, apexContents := range a.ApexContents {
+ inApex = inApex.merge(apexContents.contents[mctx.ModuleName()])
+ }
+ }
+ base.ApexProperties.InAnyApex = true
+ base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
+
+ defaultVariation := ""
+ mctx.SetDefaultDependencyVariation(&defaultVariation)
+
+ variations := []string{defaultVariation}
+ for _, a := range apexInfos {
+ variations = append(variations, a.ApexVariationName)
+ }
+ modules := mctx.CreateVariations(variations...)
+ for i, mod := range modules {
+ platformVariation := i == 0
+ if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) {
+ // Do not install the module for platform, but still allow it to output
+ // uninstallable AndroidMk entries in certain cases when they have side
+ // effects. TODO(jiyong): move this routine to somewhere else
+ mod.MakeUninstallable()
+ }
+ if !platformVariation {
+ mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1])
+ }
+ }
+
+ for _, alias := range aliases {
+ mctx.CreateAliasVariation(alias[0], alias[1])
+ }
+
+ return modules
}
-// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies
-// that are in the same APEX have unique APEX variations so that the module can link against
-// the right variant.
+// UpdateUniqueApexVariationsForDeps sets UniqueApexVariationsForDeps if any dependencies that are
+// in the same APEX have unique APEX variations so that the module can link against the right
+// variant.
func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) {
- // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes list
- // in common. It is used instead of DepIsInSameApex because it needs to determine if the dep
- // is in the same APEX due to being directly included, not only if it is included _because_ it
- // is a dependency.
+ // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes
+ // list in common. It is used instead of DepIsInSameApex because it needs to determine if
+ // the dep is in the same APEX due to being directly included, not only if it is included
+ // _because_ it is a dependency.
anyInSameApex := func(a, b []ApexInfo) bool {
collectApexes := func(infos []ApexInfo) []string {
var ret []string
@@ -446,9 +518,10 @@
return false
}
+ // If any of the dependencies requires unique apex variations, so does this module.
mctx.VisitDirectDeps(func(dep Module) {
if depApexModule, ok := dep.(ApexModule); ok {
- if anyInSameApex(depApexModule.apexModuleBase().apexVariations, am.apexModuleBase().apexVariations) &&
+ if anyInSameApex(depApexModule.apexModuleBase().apexInfos, am.apexModuleBase().apexInfos) &&
(depApexModule.UniqueApexVariations() ||
depApexModule.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps) {
am.apexModuleBase().ApexProperties.UniqueApexVariationsForDeps = true
@@ -457,10 +530,10 @@
})
}
-// UpdateDirectlyInAnyApex uses the final module to store if any variant of this
-// module is directly in any APEX, and then copies the final value to all the modules.
-// It also copies the DirectlyInAnyApex value to any direct dependencies with a
-// CopyDirectlyInAnyApexTag dependency tag.
+// UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
+// in any APEX, and then copies the final value to all the modules. It also copies the
+// DirectlyInAnyApex value to any direct dependencies with a CopyDirectlyInAnyApexTag dependency
+// tag.
func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
base := am.apexModuleBase()
// Copy DirectlyInAnyApex and InAnyApex from any direct dependencies with a
@@ -475,14 +548,13 @@
if base.ApexProperties.DirectlyInAnyApex {
// Variants of a module are always visited sequentially in order, so it is safe to
- // write to another variant of this module.
- // For a BottomUpMutator the PrimaryModule() is visited first and FinalModule() is
- // visited last.
+ // write to another variant of this module. For a BottomUpMutator the
+ // PrimaryModule() is visited first and FinalModule() is visited last.
mctx.FinalModule().(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = true
}
- // If this is the FinalModule (last visited module) copy AnyVariantDirectlyInAnyApex to
- // all the other variants
+ // If this is the FinalModule (last visited module) copy
+ // AnyVariantDirectlyInAnyApex to all the other variants
if am == mctx.FinalModule().(ApexModule) {
mctx.VisitAllModuleVariants(func(variant Module) {
variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex =
@@ -491,6 +563,7 @@
}
}
+// ApexMembership tells how a module became part of an APEX.
type ApexMembership int
const (
@@ -499,20 +572,21 @@
directlyInApex
)
-// Each apexBundle has an apexContents, and modules in that apex have a provider containing the
-// apexContents of each apexBundle they are part of.
+// ApexContents gives an information about member modules of an apexBundle. Each apexBundle has an
+// apexContents, and modules in that apex have a provider containing the apexContents of each
+// apexBundle they are part of.
type ApexContents struct {
- ApexName string
+ // map from a module name to its membership to this apexBUndle
contents map[string]ApexMembership
}
-func NewApexContents(name string, contents map[string]ApexMembership) *ApexContents {
+func NewApexContents(contents map[string]ApexMembership) *ApexContents {
return &ApexContents{
- ApexName: name,
contents: contents,
}
}
+// Updates an existing membership by adding a new direct (or indirect) membership
func (i ApexMembership) Add(direct bool) ApexMembership {
if direct || i == directlyInApex {
return directlyInApex
@@ -520,6 +594,10 @@
return indirectlyInApex
}
+// Merges two membership into one. Merging is needed because a module can be a part of an apexBundle
+// in many different paths. For example, it could be dependend on by the apexBundle directly, but at
+// the same time, there might be an indirect dependency to the module. In that case, the more
+// specific dependency (the direct one) is chosen.
func (i ApexMembership) merge(other ApexMembership) ApexMembership {
if other == directlyInApex || i == directlyInApex {
return directlyInApex
@@ -531,16 +609,19 @@
return notInApex
}
-func (ac *ApexContents) DirectlyInApex(name string) bool {
- return ac.contents[name] == directlyInApex
+// Tests whether a module named moduleName is directly included in the apexBundle where this
+// ApexContents is tagged.
+func (ac *ApexContents) DirectlyInApex(moduleName string) bool {
+ return ac.contents[moduleName] == directlyInApex
}
-func (ac *ApexContents) InApex(name string) bool {
- return ac.contents[name] != notInApex
+// Tests whether a module named moduleName is included in the apexBundle where this ApexContent is
+// tagged.
+func (ac *ApexContents) InApex(moduleName string) bool {
+ return ac.contents[moduleName] != notInApex
}
-// Tests whether a module named moduleName is directly depended on by all APEXes
-// in an ApexInfo.
+// Tests whether a module named moduleName is directly depended on by all APEXes in an ApexInfo.
func DirectlyInAllApexes(apexInfo ApexInfo, moduleName string) bool {
for _, contents := range apexInfo.ApexContents {
if !contents.DirectlyInApex(moduleName) {
@@ -550,12 +631,15 @@
return true
}
-func InitApexModule(m ApexModule) {
- base := m.apexModuleBase()
- base.canHaveApexVariants = true
-
- m.AddProperties(&base.ApexProperties)
-}
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//Below are routines for extra safety checks.
+//
+// BuildDepsInfoLists is to flatten the dependency graph for an apexBundle into a text file
+// (actually two in slightly different formats). The files are mostly for debugging, for example to
+// see why a certain module is included in an APEX via which dependency path.
+//
+// CheckMinSdkVersion is to make sure that all modules in an apexBundle satisfy the min_sdk_version
+// requirement of the apexBundle.
// A dependency info for a single ApexModule, either direct or transitive.
type ApexModuleDepInfo struct {
@@ -598,36 +682,22 @@
var fullContent strings.Builder
var flatContent strings.Builder
- fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion)
+ fmt.Fprintf(&fullContent, "%s(minSdkVersion:%s):\n", ctx.ModuleName(), minSdkVersion)
for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
info := depInfos[key]
toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion)
if info.IsExternal {
toName = toName + " (external)"
}
- fmt.Fprintf(&fullContent, " %s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
- fmt.Fprintf(&flatContent, "%s\\n", toName)
+ fmt.Fprintf(&fullContent, " %s <- %s\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
+ fmt.Fprintf(&flatContent, "%s\n", toName)
}
d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
- ctx.Build(pctx, BuildParams{
- Rule: WriteFile,
- Description: "Full Dependency Info",
- Output: d.fullListPath,
- Args: map[string]string{
- "content": fullContent.String(),
- },
- })
+ WriteFileRule(ctx, d.fullListPath, fullContent.String())
d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath
- ctx.Build(pctx, BuildParams{
- Rule: WriteFile,
- Description: "Flat Dependency Info",
- Output: d.flatListPath,
- Args: map[string]string{
- "content": flatContent.String(),
- },
- })
+ WriteFileRule(ctx, d.flatListPath, flatContent.String())
}
// TODO(b/158059172): remove minSdkVersion allowlist
@@ -719,7 +789,8 @@
WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
}
-// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly
+// CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version
+// accordingly
func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
// do not enforce min_sdk_version for host
if ctx.Host() {
@@ -739,8 +810,9 @@
m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
if externalDep {
- // external deps are outside the payload boundary, which is "stable" interface.
- // We don't have to check min_sdk_version for external dependencies.
+ // external deps are outside the payload boundary, which is "stable"
+ // interface. We don't have to check min_sdk_version for external
+ // dependencies.
return false
}
if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
diff --git a/android/api_levels.go b/android/api_levels.go
index bace3d4..08328e1 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -225,14 +225,7 @@
ctx.Errorf(err.Error())
}
- ctx.Build(pctx, BuildParams{
- Rule: WriteFile,
- Description: "generate " + file.Base(),
- Output: file,
- Args: map[string]string{
- "content": string(jsonStr[:]),
- },
- })
+ WriteFileRule(ctx, file, string(jsonStr))
}
func GetApiLevelsJson(ctx PathContext) WritablePath {
diff --git a/android/arch.go b/android/arch.go
index 98ff07a..34f9b29 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -27,28 +27,6 @@
"github.com/google/blueprint/proptools"
)
-const COMMON_VARIANT = "common"
-
-var (
- archTypeList []ArchType
-
- Arm = newArch("arm", "lib32")
- Arm64 = newArch("arm64", "lib64")
- X86 = newArch("x86", "lib32")
- X86_64 = newArch("x86_64", "lib64")
-
- Common = ArchType{
- Name: COMMON_VARIANT,
- }
-)
-
-var archTypeMap = map[string]ArchType{
- "arm": Arm,
- "arm64": Arm64,
- "x86": X86,
- "x86_64": X86_64,
-}
-
/*
Example blueprints file containing all variant property groups, with comment listing what type
of variants get properties in that group:
@@ -111,405 +89,26 @@
}
*/
-var archVariants = map[ArchType][]string{
- Arm: {
- "armv7-a",
- "armv7-a-neon",
- "armv8-a",
- "armv8-2a",
- "cortex-a7",
- "cortex-a8",
- "cortex-a9",
- "cortex-a15",
- "cortex-a53",
- "cortex-a53-a57",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "krait",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
- },
- Arm64: {
- "armv8_a",
- "armv8_2a",
- "armv8-2a-dotprod",
- "cortex-a53",
- "cortex-a55",
- "cortex-a72",
- "cortex-a73",
- "cortex-a75",
- "cortex-a76",
- "kryo",
- "kryo385",
- "exynos-m1",
- "exynos-m2",
- },
- X86: {
- "amberlake",
- "atom",
- "broadwell",
- "haswell",
- "icelake",
- "ivybridge",
- "kabylake",
- "sandybridge",
- "silvermont",
- "skylake",
- "stoneyridge",
- "tigerlake",
- "whiskeylake",
- "x86_64",
- },
- X86_64: {
- "amberlake",
- "broadwell",
- "haswell",
- "icelake",
- "ivybridge",
- "kabylake",
- "sandybridge",
- "silvermont",
- "skylake",
- "stoneyridge",
- "tigerlake",
- "whiskeylake",
- },
-}
-
-var archFeatures = map[ArchType][]string{
- Arm: {
- "neon",
- },
- Arm64: {
- "dotprod",
- },
- X86: {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "avx512",
- "popcnt",
- "movbe",
- },
- X86_64: {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "avx512",
- "popcnt",
- },
-}
-
-var archFeatureMap = map[ArchType]map[string][]string{
- Arm: {
- "armv7-a-neon": {
- "neon",
- },
- "armv8-a": {
- "neon",
- },
- "armv8-2a": {
- "neon",
- },
- },
- Arm64: {
- "armv8-2a-dotprod": {
- "dotprod",
- },
- },
- X86: {
- "amberlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "atom": {
- "ssse3",
- "movbe",
- },
- "broadwell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "haswell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- "movbe",
- },
- "icelake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "ivybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "kabylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "sandybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- "silvermont": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "popcnt",
- "movbe",
- },
- "skylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "stoneyridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "popcnt",
- "movbe",
- },
- "tigerlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "whiskeylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "x86_64": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- },
- X86_64: {
- "amberlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "broadwell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "haswell": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "icelake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "ivybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "popcnt",
- },
- "kabylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "aes_ni",
- "popcnt",
- },
- "sandybridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "popcnt",
- },
- "silvermont": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "popcnt",
- },
- "skylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "stoneyridge": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "aes_ni",
- "avx",
- "avx2",
- "popcnt",
- },
- "tigerlake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- "whiskeylake": {
- "ssse3",
- "sse4",
- "sse4_1",
- "sse4_2",
- "avx",
- "avx2",
- "avx512",
- "aes_ni",
- "popcnt",
- },
- },
-}
-
-var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
-
-func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
- checkCalledFromInit()
-
- for _, feature := range features {
- if !InList(feature, archFeatures[arch]) {
- panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
- }
- }
-
- if defaultArchFeatureMap[os] == nil {
- defaultArchFeatureMap[os] = make(map[ArchType][]string)
- }
- defaultArchFeatureMap[os][arch] = features
-}
-
// An Arch indicates a single CPU architecture.
type Arch struct {
- ArchType ArchType
- ArchVariant string
- CpuVariant string
- Abi []string
+ // The type of the architecture (arm, arm64, x86, or x86_64).
+ ArchType ArchType
+
+ // The variant of the architecture, for example "armv7-a" or "armv7-a-neon" for arm.
+ ArchVariant string
+
+ // The variant of the CPU, for example "cortex-a53" for arm64.
+ CpuVariant string
+
+ // The list of Android app ABIs supported by the CPU architecture, for example "arm64-v8a".
+ Abi []string
+
+ // The list of arch-specific features supported by the CPU architecture, for example "neon".
ArchFeatures []string
}
+// String returns the Arch as a string. The value is used as the name of the variant created
+// by archMutator.
func (a Arch) String() string {
s := a.ArchType.String()
if a.ArchVariant != "" {
@@ -521,12 +120,42 @@
return s
}
+// ArchType is used to define the 4 supported architecture types (arm, arm64, x86, x86_64), as
+// well as the "common" architecture used for modules that support multiple architectures, for
+// example Java modules.
type ArchType struct {
- Name string
- Field string
+ // Name is the name of the architecture type, "arm", "arm64", "x86", or "x86_64".
+ Name string
+
+ // Field is the name of the field used in properties that refer to the architecture, e.g. "Arm64".
+ Field string
+
+ // Multilib is either "lib32" or "lib64" for 32-bit or 64-bit architectures.
Multilib string
}
+// String returns the name of the ArchType.
+func (a ArchType) String() string {
+ return a.Name
+}
+
+const COMMON_VARIANT = "common"
+
+var (
+ archTypeList []ArchType
+
+ Arm = newArch("arm", "lib32")
+ Arm64 = newArch("arm64", "lib64")
+ X86 = newArch("x86", "lib32")
+ X86_64 = newArch("x86_64", "lib64")
+
+ Common = ArchType{
+ Name: COMMON_VARIANT,
+ }
+)
+
+var archTypeMap = map[string]ArchType{}
+
func newArch(name, multilib string) ArchType {
archType := ArchType{
Name: name,
@@ -534,25 +163,25 @@
Multilib: multilib,
}
archTypeList = append(archTypeList, archType)
+ archTypeMap[name] = archType
return archType
}
+// ArchTypeList returns the 4 supported ArchTypes for arm, arm64, x86 and x86_64.
func ArchTypeList() []ArchType {
return append([]ArchType(nil), archTypeList...)
}
-func (a ArchType) String() string {
- return a.Name
-}
-
-var _ encoding.TextMarshaler = ArchType{}
-
+// MarshalText allows an ArchType to be serialized through any encoder that supports
+// encoding.TextMarshaler.
func (a ArchType) MarshalText() ([]byte, error) {
return []byte(strconv.Quote(a.String())), nil
}
-var _ encoding.TextUnmarshaler = &ArchType{}
+var _ encoding.TextMarshaler = ArchType{}
+// UnmarshalText allows an ArchType to be deserialized through any decoder that supports
+// encoding.TextUnmarshaler.
func (a *ArchType) UnmarshalText(text []byte) error {
if u, ok := archTypeMap[string(text)]; ok {
*a = u
@@ -562,67 +191,22 @@
return fmt.Errorf("unknown ArchType %q", text)
}
-var BuildOs = func() OsType {
- switch runtime.GOOS {
- case "linux":
- return Linux
- case "darwin":
- return Darwin
- default:
- panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
- }
-}()
+var _ encoding.TextUnmarshaler = &ArchType{}
-var BuildArch = func() ArchType {
- switch runtime.GOARCH {
- case "amd64":
- return X86_64
- default:
- panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
- }
-}()
-
-var (
- OsTypeList []OsType
- commonTargetMap = make(map[string]Target)
-
- NoOsType OsType
- Linux = NewOsType("linux_glibc", Host, false)
- Darwin = NewOsType("darwin", Host, false)
- LinuxBionic = NewOsType("linux_bionic", Host, false)
- Windows = NewOsType("windows", Host, true)
- Android = NewOsType("android", Device, false)
- Fuchsia = NewOsType("fuchsia", Device, false)
-
- // A pseudo OSType for a common os variant, which is OSType agnostic and which
- // has dependencies on all the OS variants.
- CommonOS = NewOsType("common_os", Generic, false)
-
- osArchTypeMap = map[OsType][]ArchType{
- Linux: []ArchType{X86, X86_64},
- LinuxBionic: []ArchType{Arm64, X86_64},
- Darwin: []ArchType{X86_64},
- Windows: []ArchType{X86, X86_64},
- Android: []ArchType{Arm, Arm64, X86, X86_64},
- Fuchsia: []ArchType{Arm64, X86_64},
- }
-)
-
-type OsType struct {
- Name, Field string
- Class OsClass
-
- DefaultDisabled bool
-}
-
+// OsClass is an enum that describes whether a variant of a module runs on the host, on the device,
+// or is generic.
type OsClass int
const (
+ // Generic is used for variants of modules that are not OS-specific.
Generic OsClass = iota
+ // Device is used for variants of modules that run on the device.
Device
+ // Host is used for variants of modules that run on the host.
Host
)
+// String returns the OsClass as a string.
func (class OsClass) String() string {
switch class {
case Generic:
@@ -636,22 +220,48 @@
}
}
+// OsType describes an OS variant of a module.
+type OsType struct {
+ // Name is the name of the OS. It is also used as the name of the property in Android.bp
+ // files.
+ Name string
+
+ // Field is the name of the OS converted to an exported field name, i.e. with the first
+ // character capitalized.
+ Field string
+
+ // Class is the OsClass of the OS.
+ Class OsClass
+
+ // DefaultDisabled is set when the module variants for the OS should not be created unless
+ // the module explicitly requests them. This is used to limit Windows cross compilation to
+ // only modules that need it.
+ DefaultDisabled bool
+}
+
+// String returns the name of the OsType.
func (os OsType) String() string {
return os.Name
}
+// Bionic returns true if the OS uses the Bionic libc runtime, i.e. if the OS is Android or
+// is Linux with Bionic.
func (os OsType) Bionic() bool {
return os == Android || os == LinuxBionic
}
+// Linux returns true if the OS uses the Linux kernel, i.e. if the OS is Android or is Linux
+// with or without the Bionic libc runtime.
func (os OsType) Linux() bool {
return os == Android || os == Linux || os == LinuxBionic
}
-func NewOsType(name string, class OsClass, defDisabled bool) OsType {
+// newOsType constructs an OsType and adds it to the global lists.
+func newOsType(name string, class OsClass, defDisabled bool, archTypes ...ArchType) OsType {
+ checkCalledFromInit()
os := OsType{
Name: name,
- Field: strings.Title(name),
+ Field: proptools.FieldNameForProperty(name),
Class: class,
DefaultDisabled: defDisabled,
@@ -661,12 +271,14 @@
if _, found := commonTargetMap[name]; found {
panic(fmt.Errorf("Found Os type duplicate during OsType registration: %q", name))
} else {
- commonTargetMap[name] = Target{Os: os, Arch: Arch{ArchType: Common}}
+ commonTargetMap[name] = Target{Os: os, Arch: CommonArch}
}
+ osArchTypeMap[os] = archTypes
return os
}
+// osByName returns the OsType that has the given name, or NoOsType if none match.
func osByName(name string) OsType {
for _, os := range OsTypeList {
if os.Name == name {
@@ -677,18 +289,78 @@
return NoOsType
}
-type NativeBridgeSupport bool
+// BuildOs returns the OsType for the OS that the build is running on.
+var BuildOs = func() OsType {
+ switch runtime.GOOS {
+ case "linux":
+ return Linux
+ case "darwin":
+ return Darwin
+ default:
+ panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
+ }
+}()
-const (
- NativeBridgeDisabled NativeBridgeSupport = false
- NativeBridgeEnabled NativeBridgeSupport = true
+// BuildArch returns the ArchType for the CPU that the build is running on.
+var BuildArch = func() ArchType {
+ switch runtime.GOARCH {
+ case "amd64":
+ return X86_64
+ default:
+ panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
+ }
+}()
+
+var (
+ // OsTypeList contains a list of all the supported OsTypes, including ones not supported
+ // by the current build host or the target device.
+ OsTypeList []OsType
+ // commonTargetMap maps names of OsTypes to the corresponding common Target, i.e. the
+ // Target with the same OsType and the common ArchType.
+ commonTargetMap = make(map[string]Target)
+ // osArchTypeMap maps OsTypes to the list of supported ArchTypes for that OS.
+ osArchTypeMap = map[OsType][]ArchType{}
+
+ // NoOsType is a placeholder for when no OS is needed.
+ NoOsType OsType
+ // Linux is the OS for the Linux kernel plus the glibc runtime.
+ Linux = newOsType("linux_glibc", Host, false, X86, X86_64)
+ // Darwin is the OS for MacOS/Darwin host machines.
+ Darwin = newOsType("darwin", Host, false, X86_64)
+ // LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
+ // rest of Android.
+ LinuxBionic = newOsType("linux_bionic", Host, false, Arm64, X86_64)
+ // Windows the OS for Windows host machines.
+ Windows = newOsType("windows", Host, true, X86, X86_64)
+ // Android is the OS for target devices that run all of Android, including the Linux kernel
+ // and the Bionic libc runtime.
+ Android = newOsType("android", Device, false, Arm, Arm64, X86, X86_64)
+ // Fuchsia is the OS for target devices that run Fuchsia.
+ Fuchsia = newOsType("fuchsia", Device, false, Arm64, X86_64)
+
+ // CommonOS is a pseudo OSType for a common OS variant, which is OsType agnostic and which
+ // has dependencies on all the OS variants.
+ CommonOS = newOsType("common_os", Generic, false)
+
+ // CommonArch is the Arch for all modules that are os-specific but not arch specific,
+ // for example most Java modules.
+ CommonArch = Arch{ArchType: Common}
)
+// Target specifies the OS and architecture that a module is being compiled for.
type Target struct {
- Os OsType
- Arch Arch
- NativeBridge NativeBridgeSupport
+ // Os the OS that the module is being compiled for (e.g. "linux_glibc", "android").
+ Os OsType
+ // Arch is the architecture that the module is being compiled for.
+ Arch Arch
+ // NativeBridge is NativeBridgeEnabled if the architecture is supported using NativeBridge
+ // (i.e. arm on x86) for this device.
+ NativeBridge NativeBridgeSupport
+ // NativeBridgeHostArchName is the name of the real architecture that is used to implement
+ // the NativeBridge architecture. For example, for arm on x86 this would be "x86".
NativeBridgeHostArchName string
+ // NativeBridgeRelativePath is the name of the subdirectory that will contain NativeBridge
+ // libraries and binaries.
NativeBridgeRelativePath string
// HostCross is true when the target cannot run natively on the current build host.
@@ -697,14 +369,25 @@
HostCross bool
}
+// NativeBridgeSupport is an enum that specifies if a Target supports NativeBridge.
+type NativeBridgeSupport bool
+
+const (
+ NativeBridgeDisabled NativeBridgeSupport = false
+ NativeBridgeEnabled NativeBridgeSupport = true
+)
+
+// String returns the OS and arch variations used for the Target.
func (target Target) String() string {
return target.OsVariation() + "_" + target.ArchVariation()
}
+// OsVariation returns the name of the variation used by the osMutator for the Target.
func (target Target) OsVariation() string {
return target.Os.String()
}
+// ArchVariation returns the name of the variation used by the archMutator for the Target.
func (target Target) ArchVariation() string {
var variation string
if target.NativeBridge {
@@ -715,6 +398,8 @@
return variation
}
+// Variations returns a list of blueprint.Variations for the osMutator and archMutator for the
+// Target.
func (target Target) Variations() []blueprint.Variation {
return []blueprint.Variation{
{Mutator: "os", Variation: target.OsVariation()},
@@ -722,10 +407,16 @@
}
}
+// osMutator splits an arch-specific module into a variant for each OS that is enabled for the
+// module. It uses the HostOrDevice value passed to InitAndroidArchModule and the
+// device_supported and host_supported properties to determine which OsTypes are enabled for this
+// module, then searches through the Targets to determine which have enabled Targets for this
+// module.
func osMutator(bpctx blueprint.BottomUpMutatorContext) {
var module Module
var ok bool
if module, ok = bpctx.Module().(Module); !ok {
+ // The module is not a Soong module, it is a Blueprint module.
if bootstrap.IsBootstrapModule(bpctx.Module()) {
// Bootstrap Go modules are always the build OS or linux bionic.
config := bpctx.Config().(Config)
@@ -749,35 +440,38 @@
base := module.base()
+ // Nothing to do for modules that are not architecture specific (e.g. a genrule).
if !base.ArchSpecific() {
return
}
+ // Collect a list of OSTypes supported by this module based on the HostOrDevice value
+ // passed to InitAndroidArchModule and the device_supported and host_supported properties.
var moduleOSList []OsType
-
for _, os := range OsTypeList {
for _, t := range mctx.Config().Targets[os] {
- if base.supportsTarget(t, mctx.Config()) {
+ if base.supportsTarget(t) {
moduleOSList = append(moduleOSList, os)
break
}
}
}
+ // If there are no supported OSes then disable the module.
if len(moduleOSList) == 0 {
base.Disable()
return
}
+ // Convert the list of supported OsTypes to the variation names.
osNames := make([]string, len(moduleOSList))
-
for i, os := range moduleOSList {
osNames[i] = os.String()
}
createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
if createCommonOSVariant {
- // A CommonOS variant was requested so add it to the list of OS's variants to
+ // A CommonOS variant was requested so add it to the list of OS variants to
// create. It needs to be added to the end because it needs to depend on the
// the other variants in the list returned by CreateVariations(...) and inter
// variant dependencies can only be created from a later variant in that list to
@@ -787,6 +481,8 @@
moduleOSList = append(moduleOSList, CommonOS)
}
+ // Create the variations, annotate each one with which OS it was created for, and
+ // squash the appropriate OS-specific properties into the top level properties.
modules := mctx.CreateVariations(osNames...)
for i, m := range modules {
m.base().commonProperties.CompileOS = moduleOSList[i]
@@ -819,9 +515,6 @@
// Identifies the dependency from CommonOS variant to the os specific variants.
var commonOsToOsSpecificVariantTag = archDepTag{name: "common os to os specific"}
-// Identifies the dependency from arch variant to the common variant for a "common_first" multilib.
-var firstArchToCommonArchDepTag = archDepTag{name: "first arch to common arch"}
-
// Get the OsType specific variants for the current CommonOS variant.
//
// The returned list will only contain enabled OsType specific variants of the
@@ -841,7 +534,7 @@
}
// archMutator splits a module into a variant for each Target requested by the module. Target selection
-// for a module is in three levels, OsClass, mulitlib, and then Target.
+// for a module is in three levels, OsClass, multilib, and then Target.
// OsClass selection is determined by:
// - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
// whether the module type can compile for host, device or both.
@@ -858,7 +551,11 @@
// but may be arm for a 32-bit only build.
// "32": compile for only a single 32-bit Target supported by the OsClass.
// "64": compile for only a single 64-bit Target supported by the OsClass.
-// "common": compile a for a single Target that will work on all Targets suported by the OsClass (for example Java).
+// "common": compile a for a single Target that will work on all Targets supported by the OsClass (for example Java).
+// "common_first": compile a for a Target that will work on all Targets supported by the OsClass
+// (same as "common"), plus a second Target for the preferred Target supported by the OsClass
+// (same as "first"). This is used for java_binary that produces a common .jar and a wrapper
+// executable script.
//
// Once the list of Targets is determined, the module is split into a variant for each Target.
//
@@ -899,8 +596,8 @@
osTargets := mctx.Config().Targets[os]
image := base.commonProperties.ImageVariation
- // Filter NativeBridge targets unless they are explicitly supported
- // Skip creating native bridge variants for vendor modules
+ // Filter NativeBridge targets unless they are explicitly supported.
+ // Skip creating native bridge variants for non-core modules.
if os == Android &&
!(Bool(base.commonProperties.Native_bridge_supported) && image == CoreVariation) {
@@ -919,17 +616,24 @@
osTargets = []Target{osTargets[0]}
}
+ // Some modules want compile_multilib: "first" to mean 32-bit, not 64-bit.
+ // This is used for Windows support and for HOST_PREFER_32_BIT=true support for Art modules.
prefer32 := false
if base.prefer32 != nil {
prefer32 = base.prefer32(mctx, base, os)
}
+ // Determine the multilib selection for this module.
multilib, extraMultilib := decodeMultilib(base, os.Class)
+
+ // Convert the multilib selection into a list of Targets.
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
}
+ // If the module is using extraMultilib, decode the extraMultilib selection into
+ // a separate list of Targets.
var multiTargets []Target
if extraMultilib != "" {
multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
@@ -938,52 +642,62 @@
}
}
+ // Recovery is always the primary architecture, filter out any other architectures.
if image == RecoveryVariation {
primaryArch := mctx.Config().DevicePrimaryArchType()
targets = filterToArch(targets, primaryArch)
multiTargets = filterToArch(multiTargets, primaryArch)
}
+ // If there are no supported targets disable the module.
if len(targets) == 0 {
base.Disable()
return
}
+ // Convert the targets into a list of arch variation names.
targetNames := make([]string, len(targets))
-
for i, target := range targets {
targetNames[i] = target.ArchVariation()
}
+ // Create the variations, annotate each one with which Target it was created for, and
+ // squash the appropriate arch-specific properties into the top level properties.
modules := mctx.CreateVariations(targetNames...)
for i, m := range modules {
addTargetProperties(m, targets[i], multiTargets, i == 0)
m.base().setArchProperties(mctx)
}
-
- if multilib == "common_first" && len(modules) >= 2 {
- for i := range modules[1:] {
- mctx.AddInterVariantDependency(firstArchToCommonArchDepTag, modules[i+1], modules[0])
- }
- }
}
+// addTargetProperties annotates a variant with the Target is is being compiled for, the list
+// of additional Targets it is supporting (if any), and whether it is the primary Target for
+// the module.
func addTargetProperties(m Module, target Target, multiTargets []Target, primaryTarget bool) {
m.base().commonProperties.CompileTarget = target
m.base().commonProperties.CompileMultiTargets = multiTargets
m.base().commonProperties.CompilePrimary = primaryTarget
}
+// decodeMultilib returns the appropriate compile_multilib property for the module, or the default
+// multilib from the factory's call to InitAndroidArchModule if none was set. For modules that
+// called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
+// the actual multilib in extraMultilib.
func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
+ // First check the "android.compile_multilib" or "host.compile_multilib" properties.
switch class {
case Device:
multilib = String(base.commonProperties.Target.Android.Compile_multilib)
case Host:
multilib = String(base.commonProperties.Target.Host.Compile_multilib)
}
+
+ // If those aren't set, try the "compile_multilib" property.
if multilib == "" {
multilib = String(base.commonProperties.Compile_multilib)
}
+
+ // If that wasn't set, use the default multilib set by the factory.
if multilib == "" {
multilib = base.commonProperties.Default_multilib
}
@@ -1000,6 +714,8 @@
}
}
+// filterToArch takes a list of Targets and an ArchType, and returns a modified list that contains
+// only Targets that have the specified ArchType.
func filterToArch(targets []Target, arch ArchType) []Target {
for i := 0; i < len(targets); i++ {
if targets[i].Arch.ArchType != arch {
@@ -1010,17 +726,27 @@
return targets
}
-type archPropTypeDesc struct {
- arch, multilib, target reflect.Type
-}
-
+// archPropRoot is a struct type used as the top level of the arch-specific properties. It
+// contains the "arch", "multilib", and "target" property structs. It is used to split up the
+// property structs to limit how much is allocated when a single arch-specific property group is
+// used. The types are interface{} because they will hold instances of runtime-created types.
type archPropRoot struct {
Arch, Multilib, Target interface{}
}
+// archPropTypeDesc holds the runtime-created types for the property structs to instantiate to
+// create an archPropRoot property struct.
+type archPropTypeDesc struct {
+ arch, multilib, target reflect.Type
+}
+
// createArchPropTypeDesc takes a reflect.Type that is either a struct or a pointer to a struct, and
// returns lists of reflect.Types that contains the arch-variant properties inside structs for each
// arch, multilib and target property.
+//
+// This is a relatively expensive operation, so the results are cached in the global
+// archPropTypeMap. It is constructed entirely based on compile-time data, so there is no need
+// to isolate the results between multiple tests running in parallel.
func createArchPropTypeDesc(props reflect.Type) []archPropTypeDesc {
// Each property struct shard will be nested many times under the runtime generated arch struct,
// which can hit the limit of 64kB for the name of runtime generated structs. They are nested
@@ -1029,7 +755,12 @@
// could cause problems if a single deeply nested property no longer fits in the name.
const maxArchTypeNameSize = 500
+ // Convert the type to a new set of types that contains only the arch-specific properties
+ // (those that are tagged with `android:"arch_specific"`), and sharded into multiple types
+ // to keep the runtime-generated names under the limit.
propShards, _ := proptools.FilterPropertyStructSharded(props, maxArchTypeNameSize, filterArchStruct)
+
+ // If the type has no arch-specific properties there is nothing to do.
if len(propShards) == 0 {
return nil
}
@@ -1037,6 +768,8 @@
var ret []archPropTypeDesc
for _, props := range propShards {
+ // variantFields takes a list of variant property field names and returns a list the
+ // StructFields with the names and the type of the current shard.
variantFields := func(names []string) []reflect.StructField {
ret := make([]reflect.StructField, len(names))
@@ -1048,9 +781,11 @@
return ret
}
+ // Create a type that contains the properties in this shard repeated for each
+ // architecture, architecture variant, and architecture feature.
archFields := make([]reflect.StructField, len(archTypeList))
for i, arch := range archTypeList {
- variants := []string{}
+ var variants []string
for _, archVariant := range archVariants[arch] {
archVariant := variantReplacer.Replace(archVariant)
@@ -1061,8 +796,13 @@
variants = append(variants, proptools.FieldNameForProperty(feature))
}
+ // Create the StructFields for each architecture variant architecture feature
+ // (e.g. "arch.arm.cortex-a53" or "arch.arm.neon").
fields := variantFields(variants)
+ // Create the StructField for the architecture itself (e.g. "arch.arm"). The special
+ // "BlueprintEmbed" name is used by Blueprint to put the properties in the
+ // parent struct.
fields = append([]reflect.StructField{{
Name: "BlueprintEmbed",
Type: props,
@@ -1074,10 +814,15 @@
Type: reflect.StructOf(fields),
}
}
+
+ // Create the type of the "arch" property struct for this shard.
archType := reflect.StructOf(archFields)
+ // Create the type for the "multilib" property struct for this shard, containing the
+ // "multilib.lib32" and "multilib.lib64" property structs.
multilibType := reflect.StructOf(variantFields([]string{"Lib32", "Lib64"}))
+ // Start with a list of the special targets
targets := []string{
"Host",
"Android64",
@@ -1090,11 +835,14 @@
"Native_bridge",
}
for _, os := range OsTypeList {
+ // Add all the OSes.
targets = append(targets, os.Field)
+ // Add the OS/Arch combinations, e.g. "android_arm64".
for _, archType := range osArchTypeMap[os] {
targets = append(targets, os.Field+"_"+archType.Name)
+ // Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
if os.Linux() {
target := "Linux_" + archType.Name
if !InList(target, targets) {
@@ -1110,8 +858,10 @@
}
}
+ // Create the type for the "target" property struct for this shard.
targetType := reflect.StructOf(variantFields(targets))
+ // Return a descriptor of the 3 runtime-created types.
ret = append(ret, archPropTypeDesc{
arch: reflect.PtrTo(archType),
multilib: reflect.PtrTo(multilibType),
@@ -1121,6 +871,11 @@
return ret
}
+// variantReplacer converts architecture variant or architecture feature names into names that
+// are valid for an Android.bp file.
+var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
+
+// filterArchStruct returns true if the given field is an architecture specific property.
func filterArchStruct(field reflect.StructField, prefix string) (bool, reflect.StructField) {
if proptools.HasTag(field, "android", "arch_variant") {
// The arch_variant field isn't necessary past this point
@@ -1147,12 +902,17 @@
return false, field
}
+// archPropTypeMap contains a cache of the results of createArchPropTypeDesc for each type. It is
+// shared across all Contexts, but is constructed based only on compile-time information so there
+// is no risk of contaminating one Context with data from another.
var archPropTypeMap OncePer
-func InitArchModule(m Module) {
+// initArchModule adds the architecture-specific property structs to a Module.
+func initArchModule(m Module) {
base := m.base()
+ // Store the original list of top level property structs
base.generalProperties = m.GetProperties()
for _, properties := range base.generalProperties {
@@ -1169,10 +929,13 @@
propertiesValue.Interface()))
}
+ // Get or create the arch-specific property struct types for this property struct type.
archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
return createArchPropTypeDesc(t)
}).([]archPropTypeDesc)
+ // Instantiate one of each arch-specific property struct type and add it to the
+ // properties for the Module.
var archProperties []interface{}
for _, t := range archPropTypes {
archProperties = append(archProperties, &archPropRoot{
@@ -1185,14 +948,18 @@
m.AddProperties(archProperties...)
}
+ // Update the list of properties that can be set by a defaults module or a call to
+ // AppendMatchingProperties or PrependMatchingProperties.
base.customizableProperties = m.GetProperties()
}
-var variantReplacer = strings.NewReplacer("-", "_", ".", "_")
-
+// appendProperties squashes properties from the given field of the given src property struct
+// into the dst property struct. Returns the reflect.Value of the field in the src property
+// struct to be used for further appendProperties calls on fields of that property struct.
func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
+ // Step into non-nil pointers to structs in the src value.
if src.Kind() == reflect.Ptr {
if src.IsNil() {
return src
@@ -1200,18 +967,25 @@
src = src.Elem()
}
+ // Find the requested field in the src struct.
src = src.FieldByName(field)
if !src.IsValid() {
ctx.ModuleErrorf("field %q does not exist", srcPrefix)
return src
}
+ // Save the value of the field in the src struct to return.
ret := src
+ // If the value of the field is a struct (as opposed to a pointer to a struct) then step
+ // into the BlueprintEmbed field.
if src.Kind() == reflect.Struct {
src = src.FieldByName("BlueprintEmbed")
}
+ // order checks the `android:"variant_prepend"` tag to handle properties where the
+ // arch-specific value needs to come before the generic value, for example for lists of
+ // include directories.
order := func(property string,
dstField, srcField reflect.StructField,
dstValue, srcValue interface{}) (proptools.Order, error) {
@@ -1222,6 +996,7 @@
}
}
+ // Squash the located property struct into the destination property struct.
err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, order)
if err != nil {
if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
@@ -1234,7 +1009,8 @@
return ret
}
-// Rewrite the module's properties structs to contain os-specific values.
+// Squash the appropriate OS-specific property structs into the matching top level property structs
+// based on the CompileOS value that was annotated on the variant.
func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
os := m.commonProperties.CompileOS
@@ -1328,7 +1104,8 @@
}
}
-// Rewrite the module's properties structs to contain arch-specific values.
+// Squash the appropriate arch-specific property structs into the matching top level property
+// structs based on the CompileTarget value that was annotated on the variant.
func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
arch := m.Arch()
os := m.Os()
@@ -1480,22 +1257,7 @@
}
}
-func forEachInterface(v reflect.Value, f func(reflect.Value)) {
- switch v.Kind() {
- case reflect.Interface:
- f(v)
- case reflect.Struct:
- for i := 0; i < v.NumField(); i++ {
- forEachInterface(v.Field(i), f)
- }
- case reflect.Ptr:
- forEachInterface(v.Elem(), f)
- default:
- panic(fmt.Errorf("Unsupported kind %s", v.Kind()))
- }
-}
-
-// Convert the arch product variables into a list of targets for each os class structs
+// Convert the arch product variables into a list of targets for each OsType.
func decodeTargetProductVariables(config *config) (map[OsType][]Target, error) {
variables := config.productVariables
@@ -1567,19 +1329,15 @@
return nil, fmt.Errorf("No host primary architecture set")
}
+ // The primary host target, which must always exist.
addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ // An optional secondary host target.
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
- if Bool(config.Host_bionic) {
- addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
- }
- 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)
if crossHostOs == NoOsType {
@@ -1590,28 +1348,34 @@
return nil, fmt.Errorf("No cross-host primary architecture set")
}
+ // The primary cross-compiled host target.
addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+ // An optional secondary cross-compiled host target.
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
}
+ // Optional device targets
if variables.DeviceArch != nil && *variables.DeviceArch != "" {
var target = Android
if Bool(variables.Fuchsia) {
target = Fuchsia
}
+ // The primary device target.
addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
+ // An optional secondary device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
addTarget(Android, *variables.DeviceSecondaryArch,
variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
}
+ // An optional NativeBridge device target.
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
addTarget(Android, *variables.NativeBridgeArch,
variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
@@ -1619,6 +1383,7 @@
variables.NativeBridgeRelativePath)
}
+ // An optional secondary NativeBridge device target.
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
variables.NativeBridgeSecondaryArch != nil && *variables.NativeBridgeSecondaryArch != "" {
addTarget(Android, *variables.NativeBridgeSecondaryArch,
@@ -1653,6 +1418,7 @@
return false
}
+// archConfig describes a built-in configuration.
type archConfig struct {
arch string
archVariant string
@@ -1660,52 +1426,7 @@
abi []string
}
-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{
{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
@@ -1715,6 +1436,7 @@
}
}
+// getAmlAbisConfig returns a list of archConfigs for the ABIs supported by mainline modules.
func getAmlAbisConfig() []archConfig {
return []archConfig{
{"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
@@ -1724,6 +1446,7 @@
}
}
+// decodeArchSettings converts a list of archConfigs into a list of Targets for the given OsType.
func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
var ret []Target
@@ -1743,15 +1466,9 @@
return ret, nil
}
-// Convert a set of strings from product variables into a single Arch struct
+// decodeArch converts a set of strings from product variables into an Arch struct.
func decodeArch(os OsType, arch string, archVariant, cpuVariant *string, abi []string) (Arch, error) {
- stringPtr := func(p *string) string {
- if p != nil {
- return *p
- }
- return ""
- }
-
+ // Verify the arch is valid
archType, ok := archTypeMap[arch]
if !ok {
return Arch{}, fmt.Errorf("unknown arch %q", arch)
@@ -1759,19 +1476,22 @@
a := Arch{
ArchType: archType,
- ArchVariant: stringPtr(archVariant),
- CpuVariant: stringPtr(cpuVariant),
+ ArchVariant: String(archVariant),
+ CpuVariant: String(cpuVariant),
Abi: abi,
}
+ // Convert generic arch variants into the empty string.
if a.ArchVariant == a.ArchType.Name || a.ArchVariant == "generic" {
a.ArchVariant = ""
}
+ // Convert generic CPU variants into the empty string.
if a.CpuVariant == a.ArchType.Name || a.CpuVariant == "generic" {
a.CpuVariant = ""
}
+ // Filter empty ABIs out of the list.
for i := 0; i < len(a.Abi); i++ {
if a.Abi[i] == "" {
a.Abi = append(a.Abi[:i], a.Abi[i+1:]...)
@@ -1780,10 +1500,12 @@
}
if a.ArchVariant == "" {
+ // Set ArchFeatures from the default arch features.
if featureMap, ok := defaultArchFeatureMap[os]; ok {
a.ArchFeatures = featureMap[archType]
}
} else {
+ // Set ArchFeatures from the arch type.
if featureMap, ok := archFeatureMap[archType]; ok {
a.ArchFeatures = featureMap[a.ArchVariant]
}
@@ -1792,6 +1514,8 @@
return a, nil
}
+// filterMultilibTargets takes a list of Targets and a multilib value and returns a new list of
+// Targets containing only those that have the given multilib value.
func filterMultilibTargets(targets []Target, multilib string) []Target {
var ret []Target
for _, t := range targets {
@@ -1802,8 +1526,8 @@
return ret
}
-// Return the set of Os specific common architecture targets for each Os in a list of
-// targets.
+// getCommonTargets returns the set of Os specific common architecture targets for each Os in a list
+// of targets.
func getCommonTargets(targets []Target) []Target {
var ret []Target
set := make(map[string]bool)
@@ -1818,6 +1542,9 @@
return ret
}
+// firstTarget takes a list of Targets and a list of multilib values and returns a list of Targets
+// that contains zero or one Target for each OsType, selecting the one that matches the earliest
+// filter.
func firstTarget(targets []Target, filters ...string) []Target {
// find the first target from each OS
var ret []Target
@@ -1837,9 +1564,10 @@
return ret
}
-// Use the module multilib setting to select one or more targets from a target list
+// decodeMultilibTargets uses the module's multilib setting to select one or more targets from a
+// list of Targets.
func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([]Target, error) {
- buildTargets := []Target{}
+ var buildTargets []Target
switch multilib {
case "common":
diff --git a/android/arch_list.go b/android/arch_list.go
new file mode 100644
index 0000000..0c33b9d
--- /dev/null
+++ b/android/arch_list.go
@@ -0,0 +1,410 @@
+// 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"
+
+var archVariants = map[ArchType][]string{
+ Arm: {
+ "armv7-a",
+ "armv7-a-neon",
+ "armv8-a",
+ "armv8-2a",
+ "cortex-a7",
+ "cortex-a8",
+ "cortex-a9",
+ "cortex-a15",
+ "cortex-a53",
+ "cortex-a53-a57",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "krait",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ Arm64: {
+ "armv8_a",
+ "armv8_2a",
+ "armv8-2a-dotprod",
+ "cortex-a53",
+ "cortex-a55",
+ "cortex-a72",
+ "cortex-a73",
+ "cortex-a75",
+ "cortex-a76",
+ "kryo",
+ "kryo385",
+ "exynos-m1",
+ "exynos-m2",
+ },
+ X86: {
+ "amberlake",
+ "atom",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ "x86_64",
+ },
+ X86_64: {
+ "amberlake",
+ "broadwell",
+ "haswell",
+ "icelake",
+ "ivybridge",
+ "kabylake",
+ "sandybridge",
+ "silvermont",
+ "skylake",
+ "stoneyridge",
+ "tigerlake",
+ "whiskeylake",
+ },
+}
+
+var archFeatures = map[ArchType][]string{
+ Arm: {
+ "neon",
+ },
+ Arm64: {
+ "dotprod",
+ },
+ X86: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ "movbe",
+ },
+ X86_64: {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "avx512",
+ "popcnt",
+ },
+}
+
+var archFeatureMap = map[ArchType]map[string][]string{
+ Arm: {
+ "armv7-a-neon": {
+ "neon",
+ },
+ "armv8-a": {
+ "neon",
+ },
+ "armv8-2a": {
+ "neon",
+ },
+ },
+ Arm64: {
+ "armv8-2a-dotprod": {
+ "dotprod",
+ },
+ },
+ X86: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "atom": {
+ "ssse3",
+ "movbe",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ "movbe",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ "movbe",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ "movbe",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "x86_64": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ },
+ X86_64: {
+ "amberlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "broadwell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "haswell": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "icelake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "ivybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "popcnt",
+ },
+ "kabylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "aes_ni",
+ "popcnt",
+ },
+ "sandybridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "popcnt",
+ },
+ "silvermont": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "popcnt",
+ },
+ "skylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "stoneyridge": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "aes_ni",
+ "avx",
+ "avx2",
+ "popcnt",
+ },
+ "tigerlake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ "whiskeylake": {
+ "ssse3",
+ "sse4",
+ "sse4_1",
+ "sse4_2",
+ "avx",
+ "avx2",
+ "avx512",
+ "aes_ni",
+ "popcnt",
+ },
+ },
+}
+
+var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
+
+// RegisterDefaultArchVariantFeatures is called by files that define Toolchains to specify the
+// arch features that are available for the default arch variant. It must be called from an
+// init() function.
+func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
+ checkCalledFromInit()
+
+ for _, feature := range features {
+ if !InList(feature, archFeatures[arch]) {
+ panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
+ }
+ }
+
+ if defaultArchFeatureMap[os] == nil {
+ defaultArchFeatureMap[os] = make(map[ArchType][]string)
+ }
+ defaultArchFeatureMap[os][arch] = features
+}
diff --git a/android/arch_test.go b/android/arch_test.go
index 8525b03..4cef4c8 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -353,9 +353,9 @@
t.Run(tt.name, func(t *testing.T) {
config := TestArchConfig(buildDir, nil, bp, nil)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
ctx.RegisterModuleType("module", archTestModuleFactory)
- ctx.Register(config)
+ ctx.Register()
if tt.config != nil {
tt.config(config)
}
@@ -442,9 +442,9 @@
t.Run(tt.name, func(t *testing.T) {
config := TestArchConfigNativeBridge(buildDir, nil, bp, nil)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
ctx.RegisterModuleType("module", archTestModuleFactory)
- ctx.Register(config)
+ ctx.Register()
if tt.config != nil {
tt.config(config)
}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index c87a945..81ca475 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -27,6 +27,9 @@
"sync"
"github.com/google/blueprint/bootstrap"
+
+ "android/soong/bazel"
+ "android/soong/shared"
)
type CqueryRequestType int
@@ -58,6 +61,12 @@
// Returns true if bazel is enabled for the given configuration.
BazelEnabled() bool
+
+ // Returns the bazel output base (the root directory for all bazel intermediate outputs).
+ OutputBase() string
+
+ // Returns build statements which should get registered to reflect Bazel's outputs.
+ BuildStatementsToRegister() []bazel.BuildStatement
}
// A context object which tracks queued requests that need to be made to Bazel,
@@ -68,11 +77,15 @@
outputBase string
workspaceDir string
buildDir string
+ metricsDir string
requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
requestMutex sync.Mutex // requests can be written in parallel
results map[cqueryKey]string // Results of cquery requests after Bazel invocations
+
+ // Build statements which should get registered to reflect Bazel's outputs.
+ buildStatements []bazel.BuildStatement
}
var _ BazelContext = &bazelContext{}
@@ -100,6 +113,14 @@
return true
}
+func (m MockBazelContext) OutputBase() string {
+ return "outputbase"
+}
+
+func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
var _ BazelContext = MockBazelContext{}
func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
@@ -120,10 +141,18 @@
panic("unimplemented")
}
+func (m noopBazelContext) OutputBase() string {
+ return ""
+}
+
func (n noopBazelContext) BazelEnabled() bool {
return false
}
+func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return []bazel.BuildStatement{}
+}
+
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
@@ -153,6 +182,11 @@
} else {
missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
}
+ if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
+ bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
+ } else {
+ missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
+ }
if len(missingEnvVars) > 0 {
return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
} else {
@@ -160,6 +194,10 @@
}
}
+func (context *bazelContext) BazelMetricsDir() string {
+ return context.metricsDir
+}
+
func (context *bazelContext) BazelEnabled() bool {
return true
}
@@ -189,12 +227,13 @@
return ""
}
-func (context *bazelContext) issueBazelCommand(command string, labels []string,
+func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
extraFlags ...string) (string, error) {
- cmdFlags := []string{"--bazelrc=build/bazel/common.bazelrc",
- "--output_base=" + context.outputBase, command}
+ 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...)
@@ -211,32 +250,81 @@
}
}
+// Returns the string contents of a workspace file that should be output
+// adjacent to the main bzl file and build file.
+// This workspace file allows, via local_repository rule, sourcetree-level
+// BUILD targets to be referenced via @sourceroot.
+func (context *bazelContext) workspaceFileContents() []byte {
+ formatString := `
+# This file is generated by soong_build. Do not edit.
+local_repository(
+ name = "sourceroot",
+ path = "%s",
+)
+`
+ return []byte(fmt.Sprintf(formatString, context.workspaceDir))
+}
+
func (context *bazelContext) mainBzlFileContents() []byte {
contents := `
+#####################################################
# This file is generated by soong_build. Do not edit.
+#####################################################
+
def _mixed_build_root_impl(ctx):
return [DefaultInfo(files = depset(ctx.files.deps))]
+# Rule representing the root of the build, to depend on all Bazel targets that
+# are required for the build. Building this target will build the entire Bazel
+# build tree.
mixed_build_root = rule(
implementation = _mixed_build_root_impl,
attrs = {"deps" : attr.label_list()},
)
+
+def _phony_root_impl(ctx):
+ return []
+
+# Rule to depend on other targets but build nothing.
+# This is useful as follows: building a target of this rule will generate
+# symlink forests for all dependencies of the target, without executing any
+# actions of the build.
+phony_root = rule(
+ implementation = _phony_root_impl,
+ attrs = {"deps" : attr.label_list()},
+)
`
return []byte(contents)
}
+// Returns a "canonicalized" corresponding to the given sourcetree-level label.
+// This abstraction is required because a sourcetree label such as //foo/bar:baz
+// must be referenced via the local repository prefix, such as
+// @sourceroot//foo/bar:baz.
+func canonicalizeLabel(label string) string {
+ if strings.HasPrefix(label, "//") {
+ return "@sourceroot" + label
+ } else {
+ return "@sourceroot//" + label
+ }
+}
+
func (context *bazelContext) mainBuildFileContents() []byte {
formatString := `
# This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root")
+load(":main.bzl", "mixed_build_root", "phony_root")
mixed_build_root(name = "buildroot",
deps = [%s],
)
+
+phony_root(name = "phonyroot",
+ deps = [":buildroot"],
+)
`
var buildRootDeps []string = nil
for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", val.label))
+ buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
}
buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
@@ -262,13 +350,19 @@
// TODO(cparsons): Sort by request type instead of assuming all requests
// are of GetAllFiles type.
for val, _ := range context.requests {
- buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", val.label))
+ buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
}
buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
return []byte(fmt.Sprintf(formatString, buildRootDepsString))
}
+// Returns a workspace-relative path containing build-related metadata required
+// for interfacing with Bazel. Example: out/soong/bazel.
+func (context *bazelContext) intermediatesDir() string {
+ return filepath.Join(context.buildDir, "bazel")
+}
+
// Issues commands to Bazel to receive results for all cquery requests
// queued in the BazelContext.
func (context *bazelContext) InvokeBazel() error {
@@ -276,27 +370,39 @@
var cqueryOutput string
var err error
+
+ err = os.Mkdir(absolutePath(context.intermediatesDir()), 0777)
+ if err != nil {
+ return err
+ }
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.buildDir, "main.bzl")),
+ absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
context.mainBzlFileContents(), 0666)
if err != nil {
return err
}
err = ioutil.WriteFile(
- absolutePath(filepath.Join(context.buildDir, "BUILD.bazel")),
+ absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
context.mainBuildFileContents(), 0666)
if err != nil {
return err
}
- cquery_file_relpath := filepath.Join(context.buildDir, "buildroot.cquery")
+ cquery_file_relpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
err = ioutil.WriteFile(
absolutePath(cquery_file_relpath),
context.cqueryStarlarkFileContents(), 0666)
if err != nil {
return err
}
- buildroot_label := fmt.Sprintf("//%s:buildroot", context.buildDir)
- cqueryOutput, err = context.issueBazelCommand("cquery",
+ workspace_file_relpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
+ err = ioutil.WriteFile(
+ absolutePath(workspace_file_relpath),
+ context.workspaceFileContents(), 0666)
+ if err != nil {
+ return err
+ }
+ buildroot_label := "//:buildroot"
+ cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
[]string{fmt.Sprintf("deps(%s)", buildroot_label)},
"--output=starlark",
"--starlark:file="+cquery_file_relpath)
@@ -314,29 +420,53 @@
}
for val, _ := range context.requests {
- if cqueryResult, ok := cqueryResults[val.label]; ok {
+ if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
context.results[val] = string(cqueryResult)
} else {
return fmt.Errorf("missing result for bazel target %s", val.label)
}
}
- // Issue a build command.
- // TODO(cparsons): Invoking bazel execution during soong_build should be avoided;
- // bazel actions should either be added to the Ninja file and executed later,
- // or bazel should handle execution.
+ // Issue an aquery command to retrieve action information about the bazel build tree.
+ //
// TODO(cparsons): Use --target_pattern_file to avoid command line limits.
- _, err = context.issueBazelCommand("build", []string{buildroot_label})
+ var aqueryOutput string
+ aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
+ []string{fmt.Sprintf("deps(%s)", buildroot_label),
+ // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
+ // proto sources, which would add a number of unnecessary dependencies.
+ "--output=jsonproto"})
if err != nil {
return err
}
+ context.buildStatements = bazel.AqueryBuildStatements([]byte(aqueryOutput))
+
+ // Issue a build command of the phony root to generate symlink forests for dependencies of the
+ // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
+ // but some of symlinks may be required to resolve source dependencies of the build.
+ _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
+ []string{"//:phonyroot"})
+
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Build statements %s", context.buildStatements)
// Clear requests.
context.requests = map[cqueryKey]bool{}
return nil
}
+func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
+ return context.buildStatements
+}
+
+func (context *bazelContext) OutputBase() string {
+ return context.outputBase
+}
+
// Singleton used for registering BUILD file ninja dependencies (needed
// for correctness of builds which use Bazel.
func BazelSingleton() Singleton {
@@ -346,18 +476,45 @@
type bazelSingleton struct{}
func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
- if ctx.Config().BazelContext.BazelEnabled() {
- bazelBuildList := absolutePath(filepath.Join(
- filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
- ctx.AddNinjaFileDeps(bazelBuildList)
+ // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
+ if !ctx.Config().BazelContext.BazelEnabled() {
+ return
+ }
- data, err := ioutil.ReadFile(bazelBuildList)
- if err != nil {
- ctx.Errorf(err.Error())
+ // Add ninja file dependencies for files which all bazel invocations require.
+ bazelBuildList := absolutePath(filepath.Join(
+ filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
+ ctx.AddNinjaFileDeps(bazelBuildList)
+
+ data, err := ioutil.ReadFile(bazelBuildList)
+ if err != nil {
+ ctx.Errorf(err.Error())
+ }
+ files := strings.Split(strings.TrimSpace(string(data)), "\n")
+ for _, file := range files {
+ ctx.AddNinjaFileDeps(file)
+ }
+
+ // Register bazel-owned build statements (obtained from the aquery invocation).
+ for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
+ rule := NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command()
+ cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
+ ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+ for _, outputPath := range buildStatement.OutputPaths {
+ cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
}
- files := strings.Split(strings.TrimSpace(string(data)), "\n")
- for _, file := range files {
- ctx.AddNinjaFileDeps(file)
+ for _, inputPath := range buildStatement.InputPaths {
+ cmd.Implicit(PathForBazelOut(ctx, inputPath))
}
+
+ // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
+ // some Bazel builtins (such as files in the bazel_tools directory) have far-future
+ // timestamps. Without restat, Ninja would emit warnings that the input files of a
+ // build statement have later timestamps than the outputs.
+ rule.Restat()
+
+ rule.Build(fmt.Sprintf("bazel %s", index), buildStatement.Mnemonic)
}
}
diff --git a/android/bazel_overlay.go b/android/bazel_overlay.go
deleted file mode 100644
index a034282..0000000
--- a/android/bazel_overlay.go
+++ /dev/null
@@ -1,76 +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"
- "os"
- "strings"
-
- "github.com/google/blueprint"
-)
-
-// The Bazel Overlay singleton is responsible for generating the Ninja actions
-// for calling the soong_build primary builder in the main build.ninja file.
-func init() {
- RegisterSingletonType("bazel_overlay", BazelOverlaySingleton)
-}
-
-func BazelOverlaySingleton() Singleton {
- return &bazelOverlaySingleton{}
-}
-
-type bazelOverlaySingleton struct{}
-
-func (c *bazelOverlaySingleton) GenerateBuildActions(ctx SingletonContext) {
- // Create a build and rule statement, using the Bazel overlay's WORKSPACE
- // file as the output file marker.
- var deps Paths
- moduleListFilePath := pathForBuildToolDep(ctx, ctx.Config().moduleListFile)
- deps = append(deps, moduleListFilePath)
- deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
-
- bazelOverlayDirectory := PathForOutput(ctx, "bazel_overlay")
- bazelOverlayWorkspaceFile := bazelOverlayDirectory.Join(ctx, "WORKSPACE")
- primaryBuilder := primaryBuilderPath(ctx)
- bazelOverlay := ctx.Rule(pctx, "bazelOverlay",
- blueprint.RuleParams{
- Command: fmt.Sprintf(
- "rm -rf ${outDir}/* && %s --bazel_overlay_dir ${outDir} %s && echo WORKSPACE: `cat %s` > ${outDir}/.overlay-depfile.d",
- 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(
- "Creating the Bazel overlay workspace with %s at $outDir",
- primaryBuilder.Base()),
- Deps: blueprint.DepsGCC,
- Depfile: "${outDir}/.overlay-depfile.d",
- },
- "outDir")
-
- ctx.Build(pctx, BuildParams{
- Rule: bazelOverlay,
- Output: bazelOverlayWorkspaceFile,
- Inputs: deps,
- Args: map[string]string{
- "outDir": bazelOverlayDirectory.String(),
- },
- })
-
- // Add a phony target for building the bazel overlay
- ctx.Phony("bazel_overlay", bazelOverlayWorkspaceFile)
-}
diff --git a/android/config.go b/android/config.go
index e87a4ac..89467d8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -14,6 +14,9 @@
package android
+// This is the primary location to write and read all configuration values and
+// product variables necessary for soong_build's operation.
+
import (
"encoding/json"
"fmt"
@@ -32,63 +35,61 @@
"android/soong/android/soongconfig"
)
+// Bool re-exports proptools.Bool for the android package.
var Bool = proptools.Bool
+
+// String re-exports proptools.String for the android package.
var String = proptools.String
+
+// StringDefault re-exports proptools.StringDefault for the android package.
var StringDefault = proptools.StringDefault
+// FutureApiLevelInt is a placeholder constant for unreleased API levels.
const FutureApiLevelInt = 10000
+// FutureApiLevel represents unreleased API levels.
var FutureApiLevel = ApiLevel{
value: "current",
number: FutureApiLevelInt,
isPreview: true,
}
-// The configuration file name
-const configFileName = "soong.config"
+// 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"`
-}
-
-func (f *FileConfigurableOptions) SetDefaultConfig() {
- *f = FileConfigurableOptions{}
-}
-
// A Config object represents the entire build configuration for Android.
type Config struct {
*config
}
+// BuildDir returns the build output directory for the configuration.
func (c Config) BuildDir() string {
return c.buildDir
}
-// A DeviceConfig object represents the configuration for a particular device being built. For
-// now there will only be one of these, but in the future there may be multiple devices being
-// built
+// A DeviceConfig object represents the configuration for a particular device
+// being built. For now there will only be one of these, but in the future there
+// may be multiple devices being built.
type DeviceConfig struct {
*deviceConfig
}
+// VendorConfig represents the configuration for vendor-specific behavior.
type VendorConfig soongconfig.SoongConfig
+// Definition of general build configuration for soong_build. Some of these
+// product configuration values are read from Kati-generated soong.variables.
type config struct {
- FileConfigurableOptions
+ // Options configurable with soong.variables
productVariables productVariables
// Only available on configs created by TestConfig
TestProductVariables *productVariables
+ // A specialized context object for Bazel/Soong mixed builds and migration
+ // purposes.
BazelContext BazelContext
- PrimaryBuilder string
- ConfigFileName string
ProductVariablesFileName string
Targets map[OsType][]Target
@@ -97,8 +98,8 @@
AndroidCommonTarget Target // the Target for common modules for the Android device
AndroidFirstDeviceTarget Target // the first Target for modules for the Android device
- // multilibConflicts for an ArchType is true if there is earlier configured device architecture with the same
- // multilib value.
+ // multilibConflicts for an ArchType is true if there is earlier configured
+ // device architecture with the same multilib value.
multilibConflicts map[ArchType]bool
deviceConfig *deviceConfig
@@ -112,7 +113,9 @@
envDeps map[string]string
envFrozen bool
- inMake bool
+ // Changes behavior based on whether Kati runs after soong_build, or if soong_build
+ // runs standalone.
+ katiEnabled bool
captureBuild bool // true for tests, saves build parameters for each module
ignoreEnvironment bool // true for tests, returns empty from all Getenv calls
@@ -126,6 +129,10 @@
// in tests when a path doesn't exist.
testAllowNonExistentPaths bool
+ // The list of files that when changed, must invalidate soong_build to
+ // regenerate build.ninja.
+ ninjaFileDepsSet sync.Map
+
OncePer
}
@@ -139,15 +146,11 @@
}
func loadConfig(config *config) error {
- err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
- if err != nil {
- return err
- }
-
return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
-// loads configuration options from a JSON file in the cwd.
+// loadFromConfigFile loads and decodes configuration options from a JSON file
+// in the current working directory.
func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
// Try to open the file
configFileReader, err := os.Open(filename)
@@ -187,7 +190,7 @@
f, err := ioutil.TempFile(filepath.Dir(filename), "config")
if err != nil {
- return fmt.Errorf("cannot create empty config file %s: %s\n", filename, err.Error())
+ return fmt.Errorf("cannot create empty config file %s: %s", filename, err.Error())
}
defer os.Remove(f.Name())
defer f.Close()
@@ -219,14 +222,15 @@
}
}
-// TestConfig returns a Config object suitable for using for tests
+// TestConfig returns a Config object for testing.
func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
envCopy := make(map[string]string)
for k, v := range env {
envCopy[k] = v
}
- // Copy the real PATH value to the test environment, it's needed by HostSystemTool() used in x86_darwin_host.go
+ // Copy the real PATH value to the test environment, it's needed by
+ // NonHermeticHostSystemTool() used in x86_darwin_host.go
envCopy["PATH"] = originalEnv["PATH"]
config := &config{
@@ -261,13 +265,12 @@
config.mockFileSystem(bp, fs)
- if err := config.fromEnv(); err != nil {
- panic(err)
- }
-
return Config{config}
}
+// TestArchConfigNativeBridge returns a Config object suitable for using
+// for tests that need to run the arch mutator for native bridge supported
+// archs.
func TestArchConfigNativeBridge(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
testConfig := TestArchConfig(buildDir, env, bp, fs)
config := testConfig.config
@@ -282,6 +285,8 @@
return testConfig
}
+// TestArchConfigFuchsia returns a Config object suitable for using for
+// tests that need to run the arch mutator for the Fuchsia arch.
func TestArchConfigFuchsia(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
testConfig := TestConfig(buildDir, env, bp, fs)
config := testConfig.config
@@ -298,7 +303,8 @@
return testConfig
}
-// TestConfig returns a Config object suitable for using for tests that need to run the arch mutator
+// TestArchConfig returns a Config object suitable for using for tests that
+// need to run the arch mutator.
func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
testConfig := TestConfig(buildDir, env, bp, fs)
config := testConfig.config
@@ -330,10 +336,10 @@
return testConfig
}
-// Returns a config object which is "reset" for another bootstrap run.
-// Only per-run data is reset. Data which needs to persist across multiple
-// runs in the same program execution is carried over (such as Bazel context
-// or environment deps).
+// ConfigForAdditionalRun is a config object which is "reset" for another
+// bootstrap run. Only per-run data is reset. Data which needs to persist across
+// multiple runs in the same program execution is carried over (such as Bazel
+// context or environment deps).
func ConfigForAdditionalRun(c Config) (Config, error) {
newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile)
if err != nil {
@@ -344,12 +350,11 @@
return newConfig, nil
}
-// New creates a new Config object. The srcDir argument specifies the path to
-// the root source directory. It also loads the config file, if found.
+// NewConfig creates a new Config object. The srcDir argument specifies the path
+// to the root source directory. It also loads the config file, if found.
func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
- // Make a config with default options
+ // Make a config with default options.
config := &config{
- ConfigFileName: filepath.Join(buildDir, configFileName),
ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
env: originalEnv,
@@ -388,11 +393,13 @@
return Config{}, err
}
- inMakeFile := filepath.Join(buildDir, ".soong.in_make")
- if _, err := os.Stat(absolutePath(inMakeFile)); err == nil {
- config.inMake = true
+ KatiEnabledMarkerFile := filepath.Join(buildDir, ".soong.kati_enabled")
+ if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
+ config.katiEnabled = true
}
+ // Sets up the map of target OSes to the finer grained compilation targets
+ // that are configured from the product variables.
targets, err := decodeTargetProductVariables(config)
if err != nil {
return Config{}, err
@@ -402,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()
@@ -426,18 +431,19 @@
multilib[target.Arch.ArchType.Multilib] = true
}
+ // Map of OS to compilation targets.
config.Targets = targets
+
+ // Compilation targets for host tools.
config.BuildOSTarget = config.Targets[BuildOs][0]
config.BuildOSCommonTarget = getCommonTargets(config.Targets[BuildOs])[0]
+
+ // Compilation targets for Android.
if len(config.Targets[Android]) > 0 {
config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0]
}
- if err := config.fromEnv(); err != nil {
- return Config{}, err
- }
-
if Bool(config.productVariables.GcovCoverage) && Bool(config.productVariables.ClangCoverage) {
return Config{}, fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set")
}
@@ -447,13 +453,9 @@
Bool(config.productVariables.ClangCoverage))
config.BazelContext, err = NewBazelContext(config)
- if err != nil {
- return Config{}, err
- }
- return Config{config}, nil
-}
-var TestConfigOsFs = map[string][]byte{}
+ return Config{config}, err
+}
// mockFileSystem replaces all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
@@ -485,27 +487,19 @@
c.mockBpList = blueprint.MockModuleListFile
}
-func (c *config) fromEnv() error {
- switch c.Getenv("EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9") {
- case "", "true":
- // Do nothing
- default:
- return fmt.Errorf("The environment variable EXPERIMENTAL_JAVA_LANGUAGE_LEVEL_9 is no longer supported. Java language level 9 is now the global default.")
- }
-
- return nil
-}
-
func (c *config) StopBefore() bootstrap.StopBefore {
return c.stopBefore
}
+// SetStopBefore configures soong_build to exit earlier at a specific point.
func (c *config) SetStopBefore(stopBefore bootstrap.StopBefore) {
c.stopBefore = stopBefore
}
var _ bootstrap.ConfigStopBefore = (*config)(nil)
+// BlueprintToolLocation returns the directory containing build system tools
+// from Blueprint, like soong_zip and merge_zips.
func (c *config) BlueprintToolLocation() string {
return filepath.Join(c.buildDir, "host", c.PrebuiltOS(), "bin")
}
@@ -528,9 +522,12 @@
return PathForOutput(ctx, "host", c.PrebuiltOS(), "framework", path)
}
-// HostSystemTool looks for non-hermetic tools from the system we're running on.
-// Generally shouldn't be used, but useful to find the XCode SDK, etc.
-func (c *config) HostSystemTool(name string) string {
+// NonHermeticHostSystemTool looks for non-hermetic tools from the system we're
+// running on. These tools are not checked-in to AOSP, and therefore could lead
+// to reproducibility problems. Should not be used for other than finding the
+// XCode SDK (xcrun, sw_vers), etc. See ui/build/paths/config.go for the
+// allowlist of host system tools.
+func (c *config) NonHermeticHostSystemTool(name string) string {
for _, dir := range filepath.SplitList(c.Getenv("PATH")) {
path := filepath.Join(dir, name)
if s, err := os.Stat(path); err != nil {
@@ -539,10 +536,13 @@
return path
}
}
- return name
+ panic(fmt.Errorf(
+ "Unable to use '%s' as a host system tool for build system "+
+ "hermeticity reasons. See build/soong/ui/build/paths/config.go "+
+ "for the full list of allowed host tools on your system.", name))
}
-// PrebuiltOS returns the name of the host OS used in prebuilts directories
+// PrebuiltOS returns the name of the host OS used in prebuilts directories.
func (c *config) PrebuiltOS() string {
switch runtime.GOOS {
case "linux":
@@ -559,10 +559,14 @@
return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
}
+// PrebuiltBuildTool returns the path to a tool in the prebuilts directory containing
+// checked-in tools, like Kati, Ninja or Toybox, for the current host OS.
func (c *config) PrebuiltBuildTool(ctx PathContext, tool string) Path {
return PathForSource(ctx, "prebuilts/build-tools", c.PrebuiltOS(), "bin", tool)
}
+// CpPreserveSymlinksFlags returns the host-specific flag for the cp(1) command
+// to preserve symlinks.
func (c *config) CpPreserveSymlinksFlags() string {
switch runtime.GOOS {
case "darwin":
@@ -610,6 +614,8 @@
return value == "0" || value == "n" || value == "no" || value == "off" || value == "false"
}
+// EnvDeps returns the environment variables this build depends on. The first
+// call to this function blocks future reads from the environment.
func (c *config) EnvDeps() map[string]string {
c.envLock.Lock()
defer c.envLock.Unlock()
@@ -617,19 +623,26 @@
return c.envDeps
}
-func (c *config) EmbeddedInMake() bool {
- return c.inMake
+func (c *config) KatiEnabled() bool {
+ return c.katiEnabled
}
func (c *config) BuildId() string {
return String(c.productVariables.BuildId)
}
+// BuildNumberFile returns the path to a text file containing metadata
+// representing the current build's number.
+//
+// Rules that want to reference the build number should read from this file
+// without depending on it. They will run whenever their other dependencies
+// require them to run and get the current build number. This ensures they don't
+// rebuild on every incremental build when the build number changes.
func (c *config) BuildNumberFile(ctx PathContext) Path {
return PathForOutput(ctx, String(c.productVariables.BuildNumberFile))
}
-// DeviceName returns the name of the current device target
+// DeviceName returns the name of the current device target.
// TODO: take an AndroidModuleContext to select the device name for multi-device builds
func (c *config) DeviceName() string {
return *c.productVariables.DeviceName
@@ -701,19 +714,20 @@
return append(levels, c.PreviewApiLevels()...)
}
+// DefaultAppTargetSdk returns the API level that platform apps are targeting.
+// This converts a codename to the exact ApiLevel it represents.
func (c *config) DefaultAppTargetSdk(ctx EarlyModuleContext) ApiLevel {
if Bool(c.productVariables.Platform_sdk_final) {
return c.PlatformSdkVersion()
- } else {
- codename := c.PlatformSdkCodename()
- if codename == "" {
- return NoneApiLevel
- }
- if codename == "REL" {
- panic("Platform_sdk_codename should not be REL when Platform_sdk_final is true")
- }
- return ApiLevelOrPanic(ctx, codename)
}
+ codename := c.PlatformSdkCodename()
+ if codename == "" {
+ return NoneApiLevel
+ }
+ if codename == "REL" {
+ panic("Platform_sdk_codename should not be REL when Platform_sdk_final is true")
+ }
+ return ApiLevelOrPanic(ctx, codename)
}
func (c *config) AppsDefaultVersionName() string {
@@ -745,19 +759,17 @@
defaultCert := String(c.productVariables.DefaultAppCertificate)
if defaultCert != "" {
return PathForSource(ctx, filepath.Dir(defaultCert))
- } else {
- return PathForSource(ctx, "build/make/target/product/security")
}
+ return PathForSource(ctx, "build/make/target/product/security")
}
func (c *config) DefaultAppCertificate(ctx PathContext) (pem, key SourcePath) {
defaultCert := String(c.productVariables.DefaultAppCertificate)
if defaultCert != "" {
return PathForSource(ctx, defaultCert+".x509.pem"), PathForSource(ctx, defaultCert+".pk8")
- } else {
- defaultDir := c.DefaultAppCertificateDir(ctx)
- return defaultDir.Join(ctx, "testkey.x509.pem"), defaultDir.Join(ctx, "testkey.pk8")
}
+ defaultDir := c.DefaultAppCertificateDir(ctx)
+ return defaultDir.Join(ctx, "testkey.x509.pem"), defaultDir.Join(ctx, "testkey.pk8")
}
func (c *config) ApexKeyDir(ctx ModuleContext) SourcePath {
@@ -767,12 +779,14 @@
// When defaultCert is unset or is set to the testkeys path, use the APEX keys
// that is under the module dir
return pathForModuleSrc(ctx)
- } else {
- // If not, APEX keys are under the specified directory
- return PathForSource(ctx, filepath.Dir(defaultCert))
}
+ // If not, APEX keys are under the specified directory
+ return PathForSource(ctx, filepath.Dir(defaultCert))
}
+// AllowMissingDependencies configures Blueprint/Soong to not fail when modules
+// are configured to depend on non-existent modules. Note that this does not
+// affect missing input dependencies at the Ninja level.
func (c *config) AllowMissingDependencies() bool {
return Bool(c.productVariables.Allow_missing_dependencies)
}
@@ -818,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...)
}
@@ -842,9 +851,8 @@
func (c *config) EnableCFI() bool {
if c.productVariables.EnableCFI == nil {
return true
- } else {
- return *c.productVariables.EnableCFI
}
+ return *c.productVariables.EnableCFI
}
func (c *config) DisableScudo() bool {
@@ -889,11 +897,13 @@
return c.IsEnvTrue("RUN_ERROR_PRONE")
}
+// XrefCorpusName returns the Kythe cross-reference corpus name.
func (c *config) XrefCorpusName() string {
return c.Getenv("XREF_CORPUS")
}
-// Returns Compilation Unit encoding to use. Can be 'json' (default), 'proto' or 'all'.
+// XrefCuEncoding returns the compilation unit encoding to use for Kythe code
+// xrefs. Can be 'json' (default), 'proto' or 'all'.
func (c *config) XrefCuEncoding() string {
if enc := c.Getenv("KYTHE_KZIP_ENCODING"); enc != "" {
return enc
@@ -928,6 +938,10 @@
return Bool(c.productVariables.ArtUseReadBarrier)
}
+// Enforce Runtime Resource Overlays for a module. RROs supersede static RROs,
+// but some modules still depend on it.
+//
+// More info: https://source.android.com/devices/architecture/rros
func (c *config) EnforceRROForModule(name string) bool {
enforceList := c.productVariables.EnforceRROTargets
// TODO(b/150820813) Some modules depend on static overlay, remove this after eliminating the dependency.
@@ -974,6 +988,9 @@
return c.productVariables.ModulesLoadedByPrivilegedModules
}
+// DexpreoptGlobalConfigPath returns the path to the dexpreopt.config file in
+// the output directory, if it was created during the product configuration
+// phase by Kati.
func (c *config) DexpreoptGlobalConfigPath(ctx PathContext) OptionalPath {
if c.productVariables.DexpreoptGlobalConfig == nil {
return OptionalPathForPath(nil)
@@ -982,6 +999,12 @@
pathForBuildToolDep(ctx, *c.productVariables.DexpreoptGlobalConfig))
}
+// DexpreoptGlobalConfig returns the raw byte contents of the dexpreopt global
+// configuration. Since the configuration file was created by Kati during
+// product configuration (externally of soong_build), it's not tracked, so we
+// also manually add a Ninja file dependency on the configuration file to the
+// rule that creates the main build.ninja file. This ensures that build.ninja is
+// regenerated correctly if dexpreopt.config changes.
func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
path := c.DexpreoptGlobalConfigPath(ctx)
if !path.Valid() {
@@ -1249,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)
}
@@ -1261,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)
}
@@ -1289,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
}
@@ -1340,26 +1371,31 @@
// - "system_ext:foo"
//
type ConfiguredJarList struct {
- apexes []string // A list of apex components.
- jars []string // A list of jar components.
+ // A list of apex components, which can be an apex name,
+ // or special names like "platform" or "system_ext".
+ apexes []string
+
+ // A list of jar module name components.
+ jars []string
}
-// The length of the list.
+// Len returns the length of the list of jars.
func (l *ConfiguredJarList) Len() int {
return len(l.jars)
}
-// Jar component of idx-th pair on the list.
+// Jar returns the idx-th jar component of (apex, jar) pairs.
func (l *ConfiguredJarList) Jar(idx int) string {
return l.jars[idx]
}
-// Apex component of idx-th pair on the list.
+// Apex returns the idx-th apex component of (apex, jar) pairs.
func (l *ConfiguredJarList) Apex(idx int) string {
return l.apexes[idx]
}
-// If the list contains a pair with the given jar.
+// ContainsJar returns true if the (apex, jar) pairs contains a pair with the
+// given jar module name.
func (l *ConfiguredJarList) ContainsJar(jar string) bool {
return InList(jar, l.jars)
}
@@ -1374,7 +1410,8 @@
return false
}
-// Index of the first pair with the given jar on the list, or -1 if none.
+// IndexOfJar returns the first pair with the given jar name on the list, or -1
+// if not found.
func (l *ConfiguredJarList) IndexOfJar(jar string) int {
return IndexList(jar, l.jars)
}
@@ -1402,7 +1439,7 @@
return ConfiguredJarList{apexes, jars}
}
-// Filter out sublist.
+// RemoveList filters out a list of (apex, jar) pairs from the receiving list of pairs.
func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList {
apexes := make([]string, 0, l.Len())
jars := make([]string, 0, l.Len())
@@ -1418,12 +1455,14 @@
return ConfiguredJarList{apexes, jars}
}
-// A copy of the list of strings containing jar components.
+// CopyOfJars returns a copy of the list of strings containing jar module name
+// components.
func (l *ConfiguredJarList) CopyOfJars() []string {
return CopyOf(l.jars)
}
-// A copy of the list of strings with colon-separated (apex, jar) pairs.
+// CopyOfApexJarPairs returns a copy of the list of strings with colon-separated
+// (apex, jar) pairs.
func (l *ConfiguredJarList) CopyOfApexJarPairs() []string {
pairs := make([]string, 0, l.Len())
@@ -1435,7 +1474,7 @@
return pairs
}
-// A list of build paths based on the given directory prefix.
+// BuildPaths returns a list of build paths based on the given directory prefix.
func (l *ConfiguredJarList) BuildPaths(ctx PathContext, dir OutputPath) WritablePaths {
paths := make(WritablePaths, l.Len())
for i, jar := range l.jars {
@@ -1444,7 +1483,8 @@
return paths
}
-// Called when loading configuration from JSON into a configuration structure.
+// UnmarshalJSON converts JSON configuration from raw bytes into a
+// ConfiguredJarList structure.
func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
// Try and unmarshal into a []string each item of which contains a pair
// <apex>:<jar>.
@@ -1464,16 +1504,19 @@
return nil
}
+// ModuleStem hardcodes the stem of framework-minus-apex to return "framework".
+//
+// TODO(b/139391334): hard coded until we find a good way to query the stem of a
+// module before any other mutators are run.
func ModuleStem(module string) string {
- // b/139391334: the stem of framework-minus-apex is framework. This is hard coded here until we
- // find a good way to query the stem of a module before any other mutators are run.
if module == "framework-minus-apex" {
return "framework"
}
return module
}
-// A list of on-device paths.
+// DevicePaths computes the on-device paths for the list of (apex, jar) pairs,
+// based on the operating system.
func (l *ConfiguredJarList) DevicePaths(cfg Config, ostype OsType) []string {
paths := make([]string, l.Len())
for i, jar := range l.jars {
@@ -1534,6 +1577,8 @@
}
}
+// CreateTestConfiguredJarList is a function to create ConfiguredJarList for
+// tests.
func CreateTestConfiguredJarList(list []string) ConfiguredJarList {
apexes, jars, err := splitListOfPairsIntoPairOfLists(list)
if err != nil {
@@ -1543,6 +1588,7 @@
return ConfiguredJarList{apexes, jars}
}
+// EmptyConfiguredJarList returns an empty jar list.
func EmptyConfiguredJarList() ConfiguredJarList {
return ConfiguredJarList{}
}
@@ -1552,8 +1598,7 @@
func (c *config) BootJars() []string {
return c.Once(earlyBootJarsKey, func() interface{} {
list := c.productVariables.BootJars.CopyOfJars()
- list = append(list, c.productVariables.UpdatableBootJars.CopyOfJars()...)
- return list
+ return append(list, c.productVariables.UpdatableBootJars.CopyOfJars()...)
}).([]string)
}
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 15c518a..bf24d98 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -14,11 +14,6 @@
package android
-import (
- "fmt"
- "io"
-)
-
func init() {
RegisterModuleType("csuite_config", CSuiteConfigFactory)
}
@@ -38,22 +33,21 @@
me.OutputFilePath = PathForModuleOut(ctx, me.BaseModuleName()).OutputPath
}
-func (me *CSuiteConfig) AndroidMk() AndroidMkData {
- androidMkData := AndroidMkData{
+func (me *CSuiteConfig) AndroidMkEntries() []AndroidMkEntries {
+ androidMkEntries := AndroidMkEntries{
Class: "FAKE",
Include: "$(BUILD_SYSTEM)/suite_host_config.mk",
OutputFile: OptionalPathForPath(me.OutputFilePath),
}
- androidMkData.Extra = []AndroidMkExtraFunc{
- func(w io.Writer, outputFile Path) {
+ androidMkEntries.ExtraEntries = []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
if me.properties.Test_config != nil {
- fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n",
- *me.properties.Test_config)
+ entries.SetString("LOCAL_TEST_CONFIG", *me.properties.Test_config)
}
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := csuite")
+ entries.AddCompatibilityTestSuites("csuite")
},
}
- return androidMkData
+ return []AndroidMkEntries{androidMkEntries}
}
func InitCSuiteConfigModule(me *CSuiteConfig) {
diff --git a/android/csuite_config_test.go b/android/csuite_config_test.go
index bf1a19a..9ac959e 100644
--- a/android/csuite_config_test.go
+++ b/android/csuite_config_test.go
@@ -21,9 +21,9 @@
func testCSuiteConfig(test *testing.T, bpFileContents string) *TestContext {
config := TestArchConfig(buildDir, nil, bpFileContents, nil)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
ctx.RegisterModuleType("csuite_config", CSuiteConfigFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(test, errs)
_, errs = ctx.PrepareBuildActions(config)
diff --git a/android/defaults.go b/android/defaults.go
index eb013d7..44753ce 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -183,7 +183,7 @@
initAndroidModuleBase(module)
initProductVariableModule(module)
- InitArchModule(module)
+ initArchModule(module)
InitDefaultableModule(module)
// Add properties that will not have defaults applied to them.
diff --git a/android/defaults_test.go b/android/defaults_test.go
index d096b2f..2689d86 100644
--- a/android/defaults_test.go
+++ b/android/defaults_test.go
@@ -80,14 +80,14 @@
config := TestConfig(buildDir, nil, bp, nil)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.RegisterModuleType("test", defaultsTestModuleFactory)
ctx.RegisterModuleType("defaults", defaultsTestDefaultsFactory)
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
@@ -125,7 +125,7 @@
config := TestConfig(buildDir, nil, bp, nil)
config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.SetAllowMissingDependencies(true)
ctx.RegisterModuleType("test", defaultsTestModuleFactory)
@@ -133,7 +133,7 @@
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
diff --git a/android/defs.go b/android/defs.go
index 2b1bd85..38ecb05 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -15,8 +15,13 @@
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"
)
var (
@@ -91,9 +96,9 @@
// ubuntu 14.04 offcially use dash for /bin/sh, and its builtin echo command
// doesn't support -e option. Therefore we force to use /bin/bash when writing out
// content to file.
- WriteFile = pctx.AndroidStaticRule("WriteFile",
+ 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")
@@ -111,3 +116,92 @@
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
}
+
+var (
+ // echoEscaper escapes a string such that passing it to "echo -e" will produce the input value.
+ echoEscaper = strings.NewReplacer(
+ `\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`.
+ "\n", `\n`, // Then replace newlines with \n
+ )
+
+ // echoEscaper reverses echoEscaper.
+ echoUnescaper = strings.NewReplacer(
+ `\n`, "\n",
+ `\\`, `\`,
+ )
+
+ // shellUnescaper reverses the replacer in proptools.ShellEscape
+ shellUnescaper = strings.NewReplacer(`'\''`, `'`)
+)
+
+func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+ content = echoEscaper.Replace(content)
+ content = proptools.ShellEscape(content)
+ if content == "" {
+ content = "''"
+ }
+ ctx.Build(pctx, BuildParams{
+ Rule: writeFile,
+ Output: outputFile,
+ Description: "write " + outputFile.Base(),
+ Args: map[string]string{
+ "content": content,
+ },
+ })
+}
+
+// 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
+ if len(s) >= 2 && s[0] == '\'' {
+ s = s[1 : len(s)-1]
+ }
+ s = shellUnescaper.Replace(s)
+ return s
+}
+
+// ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
+// in tests.
+func ContentFromFileRuleForTests(t *testing.T, params TestingBuildParams) string {
+ t.Helper()
+ if g, w := params.Rule, writeFile; g != w {
+ t.Errorf("expected params.Rule to be %q, was %q", w, g)
+ return ""
+ }
+
+ content := params.Args["content"]
+ content = shellUnescape(content)
+ content = echoUnescaper.Replace(content)
+
+ 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/deptag.go b/android/deptag.go
new file mode 100644
index 0000000..be5c35c
--- /dev/null
+++ b/android/deptag.go
@@ -0,0 +1,45 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import "github.com/google/blueprint"
+
+// Dependency tags can implement this interface and return true from InstallDepNeeded to annotate
+// that the installed files of the parent should depend on the installed files of the child.
+type InstallNeededDependencyTag interface {
+ // If InstallDepNeeded returns true then the installed files of the parent will depend on the
+ // installed files of the child.
+ InstallDepNeeded() bool
+}
+
+// Dependency tags can embed this struct to annotate that the installed files of the parent should
+// depend on the installed files of the child.
+type InstallAlwaysNeededDependencyTag struct{}
+
+func (i InstallAlwaysNeededDependencyTag) InstallDepNeeded() bool {
+ return true
+}
+
+var _ InstallNeededDependencyTag = InstallAlwaysNeededDependencyTag{}
+
+// IsInstallDepNeeded returns true if the dependency tag implements the InstallNeededDependencyTag
+// interface and the InstallDepNeeded returns true, meaning that the installed files of the parent
+// should depend on the installed files of the child.
+func IsInstallDepNeeded(tag blueprint.DependencyTag) bool {
+ if i, ok := tag.(InstallNeededDependencyTag); ok {
+ return i.InstallDepNeeded()
+ }
+ return false
+}
diff --git a/android/deptag_test.go b/android/deptag_test.go
new file mode 100644
index 0000000..bdd449e
--- /dev/null
+++ b/android/deptag_test.go
@@ -0,0 +1,135 @@
+// 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 (
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+type testInstallDependencyTagModule struct {
+ ModuleBase
+ Properties struct {
+ Install_deps []string
+ Deps []string
+ }
+}
+
+func (t *testInstallDependencyTagModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ outputFile := PathForModuleOut(ctx, "out")
+ ctx.Build(pctx, BuildParams{
+ Rule: Touch,
+ Output: outputFile,
+ })
+ ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
+}
+
+var testInstallDependencyTagAlwaysDepTag = struct {
+ blueprint.DependencyTag
+ InstallAlwaysNeededDependencyTag
+}{}
+
+var testInstallDependencyTagNeverDepTag = struct {
+ blueprint.DependencyTag
+}{}
+
+func (t *testInstallDependencyTagModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddVariationDependencies(nil, testInstallDependencyTagAlwaysDepTag, t.Properties.Install_deps...)
+ ctx.AddVariationDependencies(nil, testInstallDependencyTagNeverDepTag, t.Properties.Deps...)
+}
+
+func testInstallDependencyTagModuleFactory() Module {
+ module := &testInstallDependencyTagModule{}
+ InitAndroidArchModule(module, HostAndDeviceDefault, MultilibCommon)
+ module.AddProperties(&module.Properties)
+ return module
+}
+
+func TestInstallDependencyTag(t *testing.T) {
+ bp := `
+ test_module {
+ name: "foo",
+ deps: ["dep"],
+ install_deps: ["install_dep"],
+ }
+
+ test_module {
+ name: "install_dep",
+ install_deps: ["transitive"],
+ }
+
+ test_module {
+ name: "transitive",
+ }
+
+ test_module {
+ name: "dep",
+ }
+ `
+
+ config := TestArchConfig(buildDir, nil, bp, nil)
+ ctx := NewTestArchContext(config)
+
+ ctx.RegisterModuleType("test_module", testInstallDependencyTagModuleFactory)
+
+ ctx.Register()
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ hostFoo := ctx.ModuleForTests("foo", config.BuildOSCommonTarget.String()).Description("install")
+ hostInstallDep := ctx.ModuleForTests("install_dep", config.BuildOSCommonTarget.String()).Description("install")
+ hostTransitive := ctx.ModuleForTests("transitive", config.BuildOSCommonTarget.String()).Description("install")
+ hostDep := ctx.ModuleForTests("dep", config.BuildOSCommonTarget.String()).Description("install")
+
+ if g, w := hostFoo.Implicits.Strings(), hostInstallDep.Output.String(); !InList(w, g) {
+ t.Errorf("expected host dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostFoo.Implicits.Strings(), hostTransitive.Output.String(); !InList(w, g) {
+ t.Errorf("expected host dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostInstallDep.Implicits.Strings(), hostTransitive.Output.String(); !InList(w, g) {
+ t.Errorf("expected host dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostFoo.Implicits.Strings(), hostDep.Output.String(); InList(w, g) {
+ t.Errorf("expected no host dependency %q, got %q", w, g)
+ }
+
+ deviceFoo := ctx.ModuleForTests("foo", "android_common").Description("install")
+ deviceInstallDep := ctx.ModuleForTests("install_dep", "android_common").Description("install")
+ deviceTransitive := ctx.ModuleForTests("transitive", "android_common").Description("install")
+ deviceDep := ctx.ModuleForTests("dep", "android_common").Description("install")
+
+ if g, w := deviceFoo.OrderOnly.Strings(), deviceInstallDep.Output.String(); !InList(w, g) {
+ t.Errorf("expected device dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceFoo.OrderOnly.Strings(), deviceTransitive.Output.String(); !InList(w, g) {
+ t.Errorf("expected device dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceInstallDep.OrderOnly.Strings(), deviceTransitive.Output.String(); !InList(w, g) {
+ t.Errorf("expected device dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceFoo.OrderOnly.Strings(), deviceDep.Output.String(); InList(w, g) {
+ t.Errorf("expected no device dependency %q, got %q", w, g)
+ }
+}
diff --git a/android/env.go b/android/env.go
index c7c96d5..c2a09aa 100644
--- a/android/env.go
+++ b/android/env.go
@@ -41,7 +41,10 @@
// access originalEnv with dependencies. Store the value where soong_build can find it, it will manually
// ensure the dependencies are created.
soongDelveListen = os.Getenv("SOONG_DELVE")
- soongDelvePath, _ = exec.LookPath("dlv")
+ soongDelvePath = os.Getenv("SOONG_DELVE_PATH")
+ if soongDelvePath == "" {
+ soongDelvePath, _ = exec.LookPath("dlv")
+ }
originalEnv = make(map[string]string)
soongDelveEnv = []string{}
@@ -49,7 +52,7 @@
idx := strings.IndexRune(env, '=')
if idx != -1 {
originalEnv[env[:idx]] = env[idx+1:]
- if env[:idx] != "SOONG_DELVE" {
+ if env[:idx] != "SOONG_DELVE" && env[:idx] != "SOONG_DELVE_PATH" {
soongDelveEnv = append(soongDelveEnv, env)
}
}
diff --git a/android/filegroup.go b/android/filegroup.go
index 68311e3..9425616 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,6 +15,7 @@
package android
import (
+ "android/soong/bazel"
"strings"
)
@@ -37,6 +38,9 @@
// Create a make variable with the specified name that contains the list of files in the
// filegroup, relative to the root of the source tree.
Export_to_make_var *string
+
+ // Properties for Bazel migration purposes.
+ bazel.Properties
}
type fileGroup struct {
diff --git a/android/makefile_goal.go b/android/makefile_goal.go
index b5d9d69..07354a6 100644
--- a/android/makefile_goal.go
+++ b/android/makefile_goal.go
@@ -80,7 +80,7 @@
Class: "ETC",
OutputFile: OptionalPathForPath(p.outputFilePath),
ExtraFooters: []AndroidMkExtraFootersFunc{
- func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
// Can't use Cp because inputPath() is not a valid Path.
fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath)
},
diff --git a/android/makevars.go b/android/makevars.go
index 3ca7792..5101436 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -139,15 +139,24 @@
MakeVars(ctx MakeVarsContext)
}
-// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to the list of
-// MakeVarsProviders to run.
-func registerSingletonMakeVarsProvider(singleton SingletonMakeVarsProvider) {
- singletonMakeVarsProviders = append(singletonMakeVarsProviders,
- makeVarsProvider{pctx, SingletonmakeVarsProviderAdapter(singleton)})
+var singletonMakeVarsProvidersKey = NewOnceKey("singletonMakeVarsProvidersKey")
+
+// registerSingletonMakeVarsProvider adds a singleton that implements SingletonMakeVarsProvider to
+// the list of MakeVarsProviders to run.
+func registerSingletonMakeVarsProvider(config Config, singleton SingletonMakeVarsProvider) {
+ // Singletons are registered on the Context and may be different between different Contexts,
+ // for example when running multiple tests. Store the SingletonMakeVarsProviders in the
+ // Config so they are attached to the Context.
+ singletonMakeVarsProviders := config.Once(singletonMakeVarsProvidersKey, func() interface{} {
+ return &[]makeVarsProvider{}
+ }).(*[]makeVarsProvider)
+
+ *singletonMakeVarsProviders = append(*singletonMakeVarsProviders,
+ makeVarsProvider{pctx, singletonMakeVarsProviderAdapter(singleton)})
}
-// SingletonmakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
-func SingletonmakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
+// singletonMakeVarsProviderAdapter converts a SingletonMakeVarsProvider to a MakeVarsProvider.
+func singletonMakeVarsProviderAdapter(singleton SingletonMakeVarsProvider) MakeVarsProvider {
return func(ctx MakeVarsContext) { singleton.MakeVars(ctx) }
}
@@ -175,9 +184,6 @@
// Collection of makevars providers that are registered in init() methods.
var makeVarsInitProviders []makeVarsProvider
-// Collection of singleton makevars providers that are not registered as part of init() methods.
-var singletonMakeVarsProviders []makeVarsProvider
-
type makeVarsContext struct {
SingletonContext
config Config
@@ -207,7 +213,7 @@
}
func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
- if !ctx.Config().EmbeddedInMake() {
+ if !ctx.Config().KatiEnabled() {
return
}
@@ -224,7 +230,11 @@
var vars []makeVarsVariable
var dists []dist
var phonies []phony
- for _, provider := range append(makeVarsInitProviders) {
+
+ providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
+ providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
+
+ for _, provider := range providers {
mctx := &makeVarsContext{
SingletonContext: ctx,
pctx: provider.pctx,
@@ -237,25 +247,6 @@
dists = append(dists, mctx.dists...)
}
- for _, provider := range append(singletonMakeVarsProviders) {
- mctx := &makeVarsContext{
- SingletonContext: ctx,
- pctx: provider.pctx,
- }
-
- provider.call(mctx)
-
- vars = append(vars, mctx.vars...)
- phonies = append(phonies, mctx.phonies...)
- dists = append(dists, mctx.dists...)
- }
-
- // Clear singleton makevars providers after use. Since these are in-memory
- // singletons, this ensures state is reset if the build tree is processed
- // multiple times.
- // TODO(cparsons): Clean up makeVarsProviders to be part of the context.
- singletonMakeVarsProviders = nil
-
ctx.VisitAllModules(func(m Module) {
if provider, ok := m.(ModuleMakeVarsProvider); ok && m.Enabled() {
mctx := &makeVarsContext{
diff --git a/android/module.go b/android/module.go
index d677406..cfb32c1 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
@@ -409,8 +452,8 @@
InstallInRoot() bool
InstallBypassMake() bool
InstallForceOS() (*OsType, *ArchType)
- SkipInstall()
- IsSkipInstall() bool
+ HideFromMake()
+ IsHideFromMake() bool
MakeUninstallable()
ReplacedByPrebuilt()
IsReplacedByPrebuilt() bool
@@ -440,6 +483,11 @@
TargetRequiredModuleNames() []string
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
@@ -502,8 +550,12 @@
// A suffix to add to the artifact file name (before any extension).
Suffix *string `android:"arch_variant"`
- // A string tag to select the OutputFiles associated with the tag. Defaults to the
- // the empty "" string.
+ // A string tag to select the OutputFiles associated with the tag.
+ //
+ // If no tag is specified then it will select the default dist paths provided
+ // by the module type. If a tag of "" is specified then it will return the
+ // default output files provided by the modules, i.e. the result of calling
+ // OutputFiles("").
Tag *string `android:"arch_variant"`
}
@@ -699,6 +751,13 @@
// Set by osMutator.
CommonOSVariant bool `blueprint:"mutated"`
+ // When HideFromMake is set to true, no entry for this variant will be emitted in the
+ // generated Android.mk file.
+ HideFromMake bool `blueprint:"mutated"`
+
+ // When SkipInstall is set to true, calls to ctx.InstallFile, ctx.InstallExecutable,
+ // ctx.InstallSymlink and ctx.InstallAbsoluteSymlink act like calls to ctx.PackageFile
+ // and don't create a rule to install the file.
SkipInstall bool `blueprint:"mutated"`
// Whether the module has been replaced by a prebuilt
@@ -716,7 +775,9 @@
DebugMutators []string `blueprint:"mutated"`
DebugVariations []string `blueprint:"mutated"`
- // set by ImageMutator
+ // ImageVariation is set by ImageMutator to specify which image this variation is for,
+ // for example "" for core or "recovery" for recovery. It will often be set to one of the
+ // constants in image.go, but can also be set to a custom value by individual module types.
ImageVariation string `blueprint:"mutated"`
}
@@ -730,9 +791,45 @@
Dists []Dist `android:"arch_variant"`
}
+// The key to use in TaggedDistFiles when a Dist structure does not specify a
+// tag property. This intentionally does not use "" as the default because that
+// would mean that an empty tag would have a different meaning when used in a dist
+// structure that when used to reference a specific set of output paths using the
+// :module{tag} syntax, which passes tag to the OutputFiles(tag) method.
+const DefaultDistTag = "<default-dist-tag>"
+
// A map of OutputFile tag keys to Paths, for disting purposes.
type TaggedDistFiles map[string]Paths
+// addPathsForTag adds a mapping from the tag to the paths. If the map is nil
+// then it will create a map, update it and then return it. If a mapping already
+// exists for the tag then the paths are appended to the end of the current list
+// of paths, ignoring any duplicates.
+func (t TaggedDistFiles) addPathsForTag(tag string, paths ...Path) TaggedDistFiles {
+ if t == nil {
+ t = make(TaggedDistFiles)
+ }
+
+ for _, distFile := range paths {
+ if distFile != nil && !t[tag].containsPath(distFile) {
+ t[tag] = append(t[tag], distFile)
+ }
+ }
+
+ return t
+}
+
+// merge merges the entries from the other TaggedDistFiles object into this one.
+// If the TaggedDistFiles is nil then it will create a new instance, merge the
+// other into it, and then return it.
+func (t TaggedDistFiles) merge(other TaggedDistFiles) TaggedDistFiles {
+ for tag, paths := range other {
+ t = t.addPathsForTag(tag, paths...)
+ }
+
+ return t
+}
+
func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
for _, path := range paths {
if path == nil {
@@ -741,7 +838,7 @@
}
// The default OutputFile tag is the empty "" string.
- return TaggedDistFiles{"": paths}
+ return TaggedDistFiles{DefaultDistTag: paths}
}
type hostAndDeviceProperties struct {
@@ -765,27 +862,32 @@
type HostOrDeviceSupported int
const (
- _ HostOrDeviceSupported = iota
+ hostSupported = 1 << iota
+ hostCrossSupported
+ deviceSupported
+ hostDefault
+ deviceDefault
// Host and HostCross are built by default. Device is not supported.
- HostSupported
+ HostSupported = hostSupported | hostCrossSupported | hostDefault
// Host is built by default. HostCross and Device are not supported.
- HostSupportedNoCross
+ HostSupportedNoCross = hostSupported | hostDefault
// Device is built by default. Host and HostCross are not supported.
- DeviceSupported
+ DeviceSupported = deviceSupported | deviceDefault
// Device is built by default. Host and HostCross are supported.
- HostAndDeviceSupported
+ HostAndDeviceSupported = hostSupported | hostCrossSupported | deviceSupported | deviceDefault
// Host, HostCross, and Device are built by default.
- HostAndDeviceDefault
+ HostAndDeviceDefault = hostSupported | hostCrossSupported | hostDefault |
+ deviceSupported | deviceDefault
// Nothing is supported. This is not exposed to the user, but used to mark a
// host only module as unsupported when the module type is not supported on
// the host OS. E.g. benchmarks are supported on Linux but not Darwin.
- NeitherHostNorDeviceSupported
+ NeitherHostNorDeviceSupported = 0
)
type moduleKind int
@@ -819,6 +921,8 @@
m.base().module = m
}
+// InitAndroidModule initializes the Module as an Android module that is not architecture-specific.
+// It adds the common properties, for example "name" and "enabled".
func InitAndroidModule(m Module) {
initAndroidModuleBase(m)
base := m.base()
@@ -838,6 +942,12 @@
setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
}
+// InitAndroidArchModule initializes the Module as an Android module that is architecture-specific.
+// It adds the common properties, for example "name" and "enabled", as well as runtime generated
+// property structs for architecture-specific versions of generic properties tagged with
+// `android:"arch_variant"`.
+//
+// InitAndroidModule should not be called if InitAndroidArchModule was called.
func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidModule(m)
@@ -847,21 +957,37 @@
base.commonProperties.ArchSpecific = true
base.commonProperties.UseTargetVariants = true
- switch hod {
- case HostAndDeviceSupported, HostAndDeviceDefault:
+ if hod&hostSupported != 0 && hod&deviceSupported != 0 {
m.AddProperties(&base.hostAndDeviceProperties)
}
- InitArchModule(m)
+ initArchModule(m)
}
+// InitAndroidMultiTargetsArchModule initializes the Module as an Android module that is
+// architecture-specific, but will only have a single variant per OS that handles all the
+// architectures simultaneously. The list of Targets that it must handle will be available from
+// ModuleContext.MultiTargets. It adds the common properties, for example "name" and "enabled", as
+// well as runtime generated property structs for architecture-specific versions of generic
+// properties tagged with `android:"arch_variant"`.
+//
+// InitAndroidModule or InitAndroidArchModule should not be called if
+// InitAndroidMultiTargetsArchModule was called.
func InitAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidArchModule(m, hod, defaultMultilib)
m.base().commonProperties.UseTargetVariants = false
}
-// As InitAndroidMultiTargetsArchModule except it creates an additional CommonOS variant that
-// has dependencies on all the OsType specific variants.
+// InitCommonOSAndroidMultiTargetsArchModule initializes the Module as an Android module that is
+// architecture-specific, but will only have a single variant per OS that handles all the
+// architectures simultaneously, and will also have an additional CommonOS variant that has
+// dependencies on all the OS-specific variants. The list of Targets that it must handle will be
+// available from ModuleContext.MultiTargets. It adds the common properties, for example "name" and
+// "enabled", as well as runtime generated property structs for architecture-specific versions of
+// generic properties tagged with `android:"arch_variant"`.
+//
+// InitAndroidModule, InitAndroidArchModule or InitAndroidMultiTargetsArchModule should not be
+// called if InitCommonOSAndroidMultiTargetsArchModule was called.
func InitCommonOSAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
InitAndroidArchModule(m, hod, defaultMultilib)
m.base().commonProperties.UseTargetVariants = false
@@ -931,11 +1057,17 @@
// The primary visibility property, may be nil, that controls access to the module.
primaryVisibilityProperty visibilityProperty
- noAddressSanitizer bool
- installFiles InstallPaths
- checkbuildFiles Paths
- 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
// Used by buildTargetSingleton to create checkbuild and per-directory build targets
// Only set on the final variant of each module
@@ -1038,23 +1170,30 @@
}
func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFiles {
- distFiles := make(TaggedDistFiles)
+ var distFiles TaggedDistFiles
for _, dist := range m.Dists() {
- var tag string
- var distFilesForTag Paths
- if dist.Tag == nil {
- tag = ""
- } else {
- tag = *dist.Tag
- }
- distFilesForTag, err := m.base().module.(OutputFileProducer).OutputFiles(tag)
- if err != nil {
- ctx.PropertyErrorf("dist.tag", "%s", err.Error())
- }
- for _, distFile := range distFilesForTag {
- if distFile != nil && !distFiles[tag].containsPath(distFile) {
- distFiles[tag] = append(distFiles[tag], distFile)
+ // If no tag is specified then it means to use the default dist paths so use
+ // the special tag name which represents that.
+ tag := proptools.StringDefault(dist.Tag, DefaultDistTag)
+
+ if outputFileProducer, ok := m.module.(OutputFileProducer); ok {
+ // Call the OutputFiles(tag) method to get the paths associated with the tag.
+ distFilesForTag, err := outputFileProducer.OutputFiles(tag)
+
+ // If the tag was not supported and is not DefaultDistTag then it is an error.
+ // Failing to find paths for DefaultDistTag is not an error. It just means
+ // that the module type requires the legacy behavior.
+ if err != nil && tag != DefaultDistTag {
+ ctx.PropertyErrorf("dist.tag", "%s", err.Error())
}
+
+ distFiles = distFiles.addPathsForTag(tag, distFilesForTag...)
+ } else if tag != DefaultDistTag {
+ // If the tag was specified then it is an error if the module does not
+ // implement OutputFileProducer because there is no other way of accessing
+ // the paths for the specified tag.
+ ctx.PropertyErrorf("dist.tag",
+ "tag %s not supported because the module does not implement OutputFileProducer", tag)
}
}
@@ -1098,43 +1237,54 @@
return m.commonProperties.CommonOSVariant
}
-func (m *ModuleBase) supportsTarget(target Target, config Config) bool {
- switch m.commonProperties.HostOrDeviceSupported {
- case HostSupported:
- return target.Os.Class == Host
- case HostSupportedNoCross:
- return target.Os.Class == Host && !target.HostCross
- case DeviceSupported:
- return target.Os.Class == Device
- case HostAndDeviceSupported, HostAndDeviceDefault:
- supported := false
- if Bool(m.hostAndDeviceProperties.Host_supported) ||
- (m.commonProperties.HostOrDeviceSupported == HostAndDeviceDefault &&
- m.hostAndDeviceProperties.Host_supported == nil) {
- supported = supported || target.Os.Class == Host
+// supportsTarget returns true if the given Target is supported by the current module.
+func (m *ModuleBase) supportsTarget(target Target) bool {
+ switch target.Os.Class {
+ case Host:
+ if target.HostCross {
+ return m.HostCrossSupported()
+ } else {
+ return m.HostSupported()
}
- if m.hostAndDeviceProperties.Device_supported == nil ||
- *m.hostAndDeviceProperties.Device_supported {
- supported = supported || target.Os.Class == Device
- }
- return supported
+ case Device:
+ return m.DeviceSupported()
default:
return false
}
}
+// DeviceSupported returns true if the current module is supported and enabled for device targets,
+// i.e. the factory method set the HostOrDeviceSupported value to include device support and
+// the device support is enabled by default or enabled by the device_supported property.
func (m *ModuleBase) DeviceSupported() bool {
- return m.commonProperties.HostOrDeviceSupported == DeviceSupported ||
- m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
- (m.hostAndDeviceProperties.Device_supported == nil ||
- *m.hostAndDeviceProperties.Device_supported)
+ hod := m.commonProperties.HostOrDeviceSupported
+ // deviceEnabled is true if the device_supported property is true or the HostOrDeviceSupported
+ // value has the deviceDefault bit set.
+ deviceEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Device_supported, hod&deviceDefault != 0)
+ return hod&deviceSupported != 0 && deviceEnabled
}
+// HostSupported returns true if the current module is supported and enabled for host targets,
+// i.e. the factory method set the HostOrDeviceSupported value to include host support and
+// the host support is enabled by default or enabled by the host_supported property.
func (m *ModuleBase) HostSupported() bool {
- return m.commonProperties.HostOrDeviceSupported == HostSupported ||
- m.commonProperties.HostOrDeviceSupported == HostAndDeviceSupported &&
- (m.hostAndDeviceProperties.Host_supported != nil &&
- *m.hostAndDeviceProperties.Host_supported)
+ hod := m.commonProperties.HostOrDeviceSupported
+ // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported
+ // value has the hostDefault bit set.
+ hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0)
+ return hod&hostSupported != 0 && hostEnabled
+}
+
+// HostCrossSupported returns true if the current module is supported and enabled for host cross
+// targets, i.e. the factory method set the HostOrDeviceSupported value to include host cross
+// support and the host cross support is enabled by default or enabled by the
+// host_supported property.
+func (m *ModuleBase) HostCrossSupported() bool {
+ hod := m.commonProperties.HostOrDeviceSupported
+ // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported
+ // value has the hostDefault bit set.
+ hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0)
+ return hod&hostCrossSupported != 0 && hostEnabled
}
func (m *ModuleBase) Platform() bool {
@@ -1212,26 +1362,33 @@
m.commonProperties.ForcedDisabled = true
}
+// HideFromMake marks this variant so that it is not emitted in the generated Android.mk file.
+func (m *ModuleBase) HideFromMake() {
+ m.commonProperties.HideFromMake = true
+}
+
+// IsHideFromMake returns true if HideFromMake was previously called.
+func (m *ModuleBase) IsHideFromMake() bool {
+ return m.commonProperties.HideFromMake == true
+}
+
+// SkipInstall marks this variant to not create install rules when ctx.Install* are called.
func (m *ModuleBase) SkipInstall() {
m.commonProperties.SkipInstall = true
}
-func (m *ModuleBase) IsSkipInstall() bool {
- return m.commonProperties.SkipInstall == true
-}
-
-// Similar to SkipInstall, but if the AndroidMk entry would set
+// Similar to HideFromMake, but if the AndroidMk entry would set
// LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
// rather than leaving it out altogether. That happens in cases where it would
// have other side effects, in particular when it adds a NOTICE file target,
// which other install targets might depend on.
func (m *ModuleBase) MakeUninstallable() {
- m.SkipInstall()
+ m.HideFromMake()
}
func (m *ModuleBase) ReplacedByPrebuilt() {
m.commonProperties.ReplacedByPrebuilt = true
- m.SkipInstall()
+ m.HideFromMake()
}
func (m *ModuleBase) IsReplacedByPrebuilt() bool {
@@ -1242,23 +1399,33 @@
return m.commonProperties.NamespaceExportedToMake
}
-func (m *ModuleBase) computeInstallDeps(ctx blueprint.ModuleContext) InstallPaths {
-
- var result InstallPaths
- // TODO(ccross): we need to use WalkDeps and have some way to know which dependencies require installation
- ctx.VisitDepsDepthFirst(func(m blueprint.Module) {
- if a, ok := m.(Module); ok {
- result = append(result, a.FilesToInstall()...)
+// 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 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 result
+ return installDeps, packagingSpecs
}
func (m *ModuleBase) FilesToInstall() InstallPaths {
return m.installFiles
}
+func (m *ModuleBase) PackagingSpecs() []PackagingSpec {
+ return m.packagingSpecs
+}
+
+func (m *ModuleBase) TransitivePackagingSpecs() []PackagingSpec {
+ return m.packagingSpecsDepSet.ToList()
+}
+
func (m *ModuleBase) NoAddressSanitizer() bool {
return m.noAddressSanitizer
}
@@ -1392,7 +1559,7 @@
if len(deps) > 0 {
suffix := ""
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
suffix = "-soong"
}
@@ -1485,11 +1652,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
@@ -1527,22 +1698,9 @@
ctx.Variable(pctx, "moduleDescSuffix", s)
// Some common property checks for properties that will be used later in androidmk.go
- if m.distProperties.Dist.Dest != nil {
- _, err := validateSafePath(*m.distProperties.Dist.Dest)
- if err != nil {
- ctx.PropertyErrorf("dist.dest", "%s", err.Error())
- }
- }
- if m.distProperties.Dist.Dir != nil {
- _, err := validateSafePath(*m.distProperties.Dist.Dir)
- if err != nil {
- ctx.PropertyErrorf("dist.dir", "%s", err.Error())
- }
- }
- if m.distProperties.Dist.Suffix != nil {
- if strings.Contains(*m.distProperties.Dist.Suffix, "/") {
- ctx.PropertyErrorf("dist.suffix", "Suffix may not contain a '/' character.")
- }
+ checkDistProperties(ctx, "dist", &m.distProperties.Dist)
+ for i, _ := range m.distProperties.Dists {
+ checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i])
}
if m.Enabled() {
@@ -1579,8 +1737,18 @@
return
}
+ // Create the set of tagged dist files after calling GenerateAndroidBuildActions
+ // as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the
+ // output paths being set which must be done before or during
+ // GenerateAndroidBuildActions.
+ m.distFiles = m.GenerateTaggedDistFiles(ctx)
+ if ctx.Failed() {
+ return
+ }
+
m.installFiles = append(m.installFiles, ctx.installFiles...)
m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
+ m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc)
m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments)
for k, v := range ctx.phonies {
@@ -1601,11 +1769,40 @@
}
}
+ m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
+ m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
+
m.buildParams = ctx.buildParams
m.ruleParams = ctx.ruleParams
m.variables = ctx.variables
}
+// Check the supplied dist structure to make sure that it is valid.
+//
+// property - the base property, e.g. dist or dists[1], which is combined with the
+// name of the nested property to produce the full property, e.g. dist.dest or
+// dists[1].dir.
+func checkDistProperties(ctx *moduleContext, property string, dist *Dist) {
+ if dist.Dest != nil {
+ _, err := validateSafePath(*dist.Dest)
+ if err != nil {
+ ctx.PropertyErrorf(property+".dest", "%s", err.Error())
+ }
+ }
+ if dist.Dir != nil {
+ _, err := validateSafePath(*dist.Dir)
+ if err != nil {
+ ctx.PropertyErrorf(property+".dir", "%s", err.Error())
+ }
+ }
+ if dist.Suffix != nil {
+ if strings.Contains(*dist.Suffix, "/") {
+ ctx.PropertyErrorf(property+".suffix", "Suffix may not contain a '/' character.")
+ }
+ }
+
+}
+
type earlyModuleContext struct {
blueprint.EarlyModuleContext
@@ -1748,7 +1945,7 @@
type moduleContext struct {
bp blueprint.ModuleContext
baseModuleContext
- installDeps InstallPaths
+ packagingSpecs []PackagingSpec
installFiles InstallPaths
checkbuildFiles Paths
module Module
@@ -2257,11 +2454,15 @@
return m.module.InstallForceOS()
}
-func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
+func (m *moduleContext) skipInstall() bool {
if m.module.base().commonProperties.SkipInstall {
return true
}
+ if m.module.base().commonProperties.HideFromMake {
+ return true
+ }
+
// We'll need a solution for choosing which of modules with the same name in different
// namespaces to install. For now, reuse the list of namespaces exported to Make as the
// list of namespaces to install in a Soong-only build.
@@ -2270,11 +2471,7 @@
}
if m.Device() {
- if m.Config().EmbeddedInMake() && !m.InstallBypassMake() {
- return true
- }
-
- if m.Config().SkipMegaDeviceInstall(fullInstallPath.String()) {
+ if m.Config().KatiEnabled() && !m.InstallBypassMake() {
return true
}
}
@@ -2284,23 +2481,37 @@
func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
deps ...Path) InstallPath {
- return m.installFile(installPath, name, srcPath, Cp, deps)
+ return m.installFile(installPath, name, srcPath, deps, false)
}
func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
deps ...Path) InstallPath {
- return m.installFile(installPath, name, srcPath, CpExecutable, deps)
+ return m.installFile(installPath, name, srcPath, deps, true)
}
-func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path,
- rule blueprint.Rule, deps []Path) InstallPath {
+func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec {
+ fullInstallPath := installPath.Join(m, name)
+ return m.packageFile(fullInstallPath, srcPath, false)
+}
+
+func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
+ spec := PackagingSpec{
+ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+ srcPath: srcPath,
+ symlinkTarget: "",
+ executable: executable,
+ }
+ m.packagingSpecs = append(m.packagingSpecs, spec)
+ return spec
+}
+
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, executable bool) InstallPath {
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
- if !m.skipInstall(fullInstallPath) {
-
- deps = append(deps, m.installDeps.Paths()...)
+ if !m.skipInstall() {
+ deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
var implicitDeps, orderOnlyDeps Paths
@@ -2312,6 +2523,11 @@
orderOnlyDeps = deps
}
+ rule := Cp
+ if executable {
+ rule = CpExecutable
+ }
+
m.Build(pctx, BuildParams{
Rule: rule,
Description: "install " + fullInstallPath.Base(),
@@ -2319,12 +2535,16 @@
Input: srcPath,
Implicits: implicitDeps,
OrderOnly: orderOnlyDeps,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
})
m.installFiles = append(m.installFiles, fullInstallPath)
}
+
+ m.packageFile(fullInstallPath, srcPath, executable)
+
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+
return fullInstallPath
}
@@ -2332,18 +2552,18 @@
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true)
- if !m.skipInstall(fullInstallPath) {
+ relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
+ if err != nil {
+ panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
+ }
+ if !m.skipInstall() {
- relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String())
- if err != nil {
- panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
- }
m.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base(),
Output: fullInstallPath,
Input: srcPath,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
Args: map[string]string{
"fromPath": relPath,
},
@@ -2352,6 +2572,14 @@
m.installFiles = append(m.installFiles, fullInstallPath)
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
+
+ m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
+ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+ srcPath: nil,
+ symlinkTarget: relPath,
+ executable: false,
+ })
+
return fullInstallPath
}
@@ -2361,12 +2589,12 @@
fullInstallPath := installPath.Join(m, name)
m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
- if !m.skipInstall(fullInstallPath) {
+ if !m.skipInstall() {
m.Build(pctx, BuildParams{
Rule: Symlink,
Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
Output: fullInstallPath,
- Default: !m.Config().EmbeddedInMake(),
+ Default: !m.Config().KatiEnabled(),
Args: map[string]string{
"fromPath": absPath,
},
@@ -2374,6 +2602,14 @@
m.installFiles = append(m.installFiles, fullInstallPath)
}
+
+ m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
+ relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
+ srcPath: nil,
+ symlinkTarget: absPath,
+ executable: false,
+ })
+
return fullInstallPath
}
@@ -2381,6 +2617,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) {
@@ -2500,12 +2740,26 @@
return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
}
return paths, nil
+ } else if sourceFileProducer, ok := module.(SourceFileProducer); ok {
+ if tag != "" {
+ return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag)
+ }
+ paths := sourceFileProducer.Srcs()
+ if len(paths) == 0 {
+ return nil, fmt.Errorf("failed to get output files from module %q", pathContextName(ctx, module))
+ }
+ return paths, nil
} else {
return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module))
}
}
+// 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
}
@@ -2587,7 +2841,7 @@
})
suffix := ""
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
suffix = "-soong"
}
@@ -2595,7 +2849,7 @@
ctx.Phony("checkbuild"+suffix, checkbuildDeps...)
// Make will generate the MODULES-IN-* targets
- if ctx.Config().EmbeddedInMake() {
+ if ctx.Config().KatiEnabled() {
return
}
@@ -2699,3 +2953,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/module_test.go b/android/module_test.go
index 3a039e2..e3cc613 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -164,9 +164,6 @@
}
func TestErrorDependsOnDisabledModule(t *testing.T) {
- ctx := NewTestContext()
- ctx.RegisterModuleType("deps", depsModuleFactory)
-
bp := `
deps {
name: "foo",
@@ -180,7 +177,9 @@
config := TestConfig(buildDir, nil, bp, nil)
- ctx.Register(config)
+ ctx := NewTestContext(config)
+ ctx.RegisterModuleType("deps", depsModuleFactory)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
@@ -233,3 +232,51 @@
t.Errorf("Expected build params to fail validation: %+v", bparams)
}
}
+
+func TestDistErrorChecking(t *testing.T) {
+ bp := `
+ deps {
+ name: "foo",
+ dist: {
+ dest: "../invalid-dest",
+ dir: "../invalid-dir",
+ suffix: "invalid/suffix",
+ },
+ dists: [
+ {
+ dest: "../invalid-dest0",
+ dir: "../invalid-dir0",
+ suffix: "invalid/suffix0",
+ },
+ {
+ dest: "../invalid-dest1",
+ dir: "../invalid-dir1",
+ suffix: "invalid/suffix1",
+ },
+ ],
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+
+ ctx := NewTestContext(config)
+ ctx.RegisterModuleType("deps", depsModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+
+ expectedErrs := []string{
+ "\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E",
+ "\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E",
+ "\\QAndroid.bp:7:15: module \"foo\": dist.suffix: Suffix may not contain a '/' character.\\E",
+ "\\QAndroid.bp:11:15: module \"foo\": dists[0].dest: Path is outside directory: ../invalid-dest0\\E",
+ "\\QAndroid.bp:12:14: module \"foo\": dists[0].dir: Path is outside directory: ../invalid-dir0\\E",
+ "\\QAndroid.bp:13:17: module \"foo\": dists[0].suffix: Suffix may not contain a '/' character.\\E",
+ "\\QAndroid.bp:16:15: module \"foo\": dists[1].dest: Path is outside directory: ../invalid-dest1\\E",
+ "\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E",
+ "\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
+ }
+ CheckErrorsAgainstExpectations(t, errs, expectedErrs)
+}
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/mutator_test.go b/android/mutator_test.go
index 191b535..1c395c7 100644
--- a/android/mutator_test.go
+++ b/android/mutator_test.go
@@ -70,7 +70,7 @@
config := TestConfig(buildDir, nil, bp, nil)
config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.SetAllowMissingDependencies(true)
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
@@ -78,7 +78,7 @@
ctx.TopDown("add_missing_dependencies", addMissingDependenciesMutator)
})
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
@@ -92,7 +92,15 @@
}
func TestModuleString(t *testing.T) {
- ctx := NewTestContext()
+ bp := `
+ test {
+ name: "foo",
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+
+ ctx := NewTestContext(config)
var moduleStrings []string
@@ -130,15 +138,7 @@
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
- bp := `
- test {
- name: "foo",
- }
- `
-
- config := TestConfig(buildDir, nil, bp, nil)
-
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
@@ -190,7 +190,21 @@
}
func TestFinalDepsPhase(t *testing.T) {
- ctx := NewTestContext()
+ bp := `
+ test {
+ name: "common_dep_1",
+ }
+ test {
+ name: "common_dep_2",
+ }
+ test {
+ name: "foo",
+ }
+ `
+
+ config := TestConfig(buildDir, nil, bp, nil)
+
+ ctx := NewTestContext(config)
finalGot := map[string]int{}
@@ -228,20 +242,7 @@
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
- bp := `
- test {
- name: "common_dep_1",
- }
- test {
- name: "common_dep_2",
- }
- test {
- name: "foo",
- }
- `
-
- config := TestConfig(buildDir, nil, bp, nil)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
@@ -267,7 +268,8 @@
}
func TestNoCreateVariationsInFinalDeps(t *testing.T) {
- ctx := NewTestContext()
+ config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
+ ctx := NewTestContext(config)
checkErr := func() {
if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
@@ -287,8 +289,7 @@
})
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
- config := TestConfig(buildDir, nil, `test {name: "foo"}`, nil)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 66c0d89..45e2cdb 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -635,7 +635,7 @@
func setupTestFromFiles(bps map[string][]byte) (ctx *TestContext, errs []error) {
config := TestConfig(buildDir, nil, "", bps)
- ctx = NewTestContext()
+ ctx = NewTestContext(config)
ctx.RegisterModuleType("test_module", newTestModule)
ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
@@ -643,7 +643,7 @@
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("rename", renameMutator)
})
- ctx.Register(config)
+ ctx.Register()
_, errs = ctx.ParseBlueprintsFiles("Android.bp")
if len(errs) > 0 {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 56a07dc..1d454e5 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -359,14 +359,14 @@
}
func testNeverallow(config Config) (*TestContext, []error) {
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.RegisterModuleType("cc_library", newMockCcLibraryModule)
ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
ctx.PostDepsMutators(RegisterNeverallowMutator)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
if len(errs) > 0 {
diff --git a/android/ninja_deps.go b/android/ninja_deps.go
new file mode 100644
index 0000000..2f442d5
--- /dev/null
+++ b/android/ninja_deps.go
@@ -0,0 +1,43 @@
+// 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 "sort"
+
+func (c *config) addNinjaFileDeps(deps ...string) {
+ for _, dep := range deps {
+ c.ninjaFileDepsSet.Store(dep, true)
+ }
+}
+
+func (c *config) ninjaFileDeps() []string {
+ var deps []string
+ c.ninjaFileDepsSet.Range(func(key, value interface{}) bool {
+ deps = append(deps, key.(string))
+ return true
+ })
+ sort.Strings(deps)
+ return deps
+}
+
+func ninjaDepsSingletonFactory() Singleton {
+ return &ninjaDepsSingleton{}
+}
+
+type ninjaDepsSingleton struct{}
+
+func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
+ ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...)
+}
diff --git a/android/ninja_deps_test.go b/android/ninja_deps_test.go
new file mode 100644
index 0000000..d3775ed
--- /dev/null
+++ b/android/ninja_deps_test.go
@@ -0,0 +1,75 @@
+// 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 (
+ "testing"
+)
+
+func init() {
+ // This variable uses ExistentPathForSource on a PackageVarContext, which is a PathContext
+ // that is not a PathGlobContext. That requires the deps to be stored in the Config.
+ pctx.VariableFunc("test_ninja_deps_variable", func(ctx PackageVarContext) string {
+ // Using ExistentPathForSource to look for a file that does not exist in a directory that
+ // does exist (test_ninja_deps) from a PackageVarContext adds a dependency from build.ninja
+ // to the directory.
+ if ExistentPathForSource(ctx, "test_ninja_deps/does_not_exist").Valid() {
+ return "true"
+ } else {
+ return "false"
+ }
+ })
+}
+
+func testNinjaDepsSingletonFactory() Singleton {
+ return testNinjaDepsSingleton{}
+}
+
+type testNinjaDepsSingleton struct{}
+
+func (testNinjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
+ // Reference the test_ninja_deps_variable in a build statement so Blueprint is forced to
+ // evaluate it.
+ ctx.Build(pctx, BuildParams{
+ Rule: Cp,
+ Input: PathForTesting("foo"),
+ Output: PathForOutput(ctx, "test_ninja_deps_out"),
+ Args: map[string]string{
+ "cpFlags": "${test_ninja_deps_variable}",
+ },
+ })
+}
+
+func TestNinjaDeps(t *testing.T) {
+ fs := map[string][]byte{
+ "test_ninja_deps/exists": nil,
+ }
+ config := TestConfig(buildDir, nil, "", fs)
+
+ ctx := NewTestContext(config)
+ ctx.RegisterSingletonType("test_ninja_deps_singleton", testNinjaDepsSingletonFactory)
+ ctx.RegisterSingletonType("ninja_deps_singleton", ninjaDepsSingletonFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ ninjaDeps, errs := ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ // Verify that the ninja file has a dependency on the test_ninja_deps directory.
+ if g, w := ninjaDeps, "test_ninja_deps"; !InList(w, g) {
+ t.Errorf("expected %q in %q", w, g)
+ }
+}
diff --git a/android/override_module.go b/android/override_module.go
index f8342d5..fa08566 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -235,7 +235,7 @@
return
}
// See if there's a prebuilt module that overrides this override module with prefer flag,
- // in which case we call SkipInstall on the corresponding variant later.
+ // in which case we call HideFromMake on the corresponding variant later.
ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
prebuilt, ok := dep.(PrebuiltInterface)
if !ok {
@@ -284,7 +284,7 @@
mods[i+1].(OverridableModule).override(ctx, o)
if o.getOverriddenByPrebuilt() {
// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
- mods[i+1].SkipInstall()
+ mods[i+1].HideFromMake()
}
}
} else if o, ok := ctx.Module().(OverrideModule); ok {
diff --git a/android/package_ctx.go b/android/package_ctx.go
index 0de356e..6d0fcb3 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -56,7 +56,7 @@
e.errors = append(e.errors, fmt.Errorf(format, args...))
}
func (e *configErrorWrapper) AddNinjaFileDeps(deps ...string) {
- e.pctx.AddNinjaFileDeps(deps...)
+ e.config.addNinjaFileDeps(deps...)
}
type PackageVarContext interface {
diff --git a/android/package_test.go b/android/package_test.go
index 04dfc08..ade95d4 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -86,9 +86,9 @@
// Create a new config per test as visibility information is stored in the config.
config := TestArchConfig(buildDir, nil, "", fs)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
RegisterPackageBuildComponents(ctx)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles(".")
if len(errs) > 0 {
diff --git a/android/packaging.go b/android/packaging.go
new file mode 100644
index 0000000..da745ff
--- /dev/null
+++ b/android/packaging.go
@@ -0,0 +1,225 @@
+// 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"
+ "path/filepath"
+
+ "github.com/google/blueprint"
+)
+
+// PackagingSpec abstracts a request to place a built artifact at a certain path in a package. A
+// package can be the traditional <partition>.img, but isn't limited to those. Other examples could
+// be a new filesystem image that is a subset of system.img (e.g. for an Android-like mini OS
+// running on a VM), or a zip archive for some of the host tools.
+type PackagingSpec struct {
+ // Path relative to the root of the package
+ relPathInPackage string
+
+ // The path to the built artifact
+ srcPath Path
+
+ // If this is not empty, then relPathInPackage should be a symlink to this target. (Then
+ // srcPath is of course ignored.)
+ symlinkTarget string
+
+ // Whether relPathInPackage should be marked as executable or not
+ 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
+
+ // AddDeps adds dependencies to the `deps` modules. This should be called in DepsMutator.
+ // When adding the dependencies, depTag is used as the tag.
+ AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
+
+ // CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
+ // returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
+ // followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
+ // etc.) from the extracted files
+ CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) []string
+}
+
+// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
+// include this struct and call InitPackageModule.
+type PackagingBase struct {
+ properties PackagingProperties
+
+ // Allows this module to skip missing dependencies. In most cases, this is not required, but
+ // for rare cases like when there's a dependency to a module which exists in certain repo
+ // checkouts, this is needed.
+ IgnoreMissingDependencies bool
+}
+
+type depsProperty struct {
+ // Modules to include in this package
+ Deps []string `android:"arch_variant"`
+}
+
+type packagingMultilibProperties struct {
+ First depsProperty `android:"arch_variant"`
+ Common depsProperty `android:"arch_variant"`
+ Lib32 depsProperty `android:"arch_variant"`
+ Lib64 depsProperty `android:"arch_variant"`
+}
+
+type PackagingProperties struct {
+ Deps []string `android:"arch_variant"`
+ Multilib packagingMultilibProperties `android:"arch_variant"`
+}
+
+func InitPackageModule(p PackageModule) {
+ base := p.packagingBase()
+ p.AddProperties(&base.properties)
+}
+
+func (p *PackagingBase) packagingBase() *PackagingBase {
+ return p
+}
+
+// From deps and multilib.*.deps, select the dependencies that are for the given arch deps is for
+// the current archicture when this module is not configured for multi target. When configured for
+// multi target, deps is selected for each of the targets and is NOT selected for the current
+// architecture which would be Common.
+func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []string {
+ var ret []string
+ if arch == ctx.Target().Arch.ArchType && len(ctx.MultiTargets()) == 0 {
+ ret = append(ret, p.properties.Deps...)
+ } else if arch.Multilib == "lib32" {
+ ret = append(ret, p.properties.Multilib.Lib32.Deps...)
+ } else if arch.Multilib == "lib64" {
+ ret = append(ret, p.properties.Multilib.Lib64.Deps...)
+ } else if arch == Common {
+ ret = append(ret, p.properties.Multilib.Common.Deps...)
+ }
+ for i, t := range ctx.MultiTargets() {
+ if t.Arch.ArchType == arch {
+ ret = append(ret, p.properties.Deps...)
+ if i == 0 {
+ ret = append(ret, p.properties.Multilib.First.Deps...)
+ }
+ }
+ }
+ return FirstUniqueStrings(ret)
+}
+
+func (p *PackagingBase) getSupportedTargets(ctx BaseModuleContext) []Target {
+ var ret []Target
+ // The current and the common OS targets are always supported
+ ret = append(ret, ctx.Target())
+ if ctx.Arch().ArchType != Common {
+ ret = append(ret, Target{Os: ctx.Os(), Arch: Arch{ArchType: Common}})
+ }
+ // If this module is configured for multi targets, those should be supported as well
+ ret = append(ret, ctx.MultiTargets()...)
+ return ret
+}
+
+// See PackageModule.AddDeps
+func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag) {
+ for _, t := range p.getSupportedTargets(ctx) {
+ for _, dep := range p.getDepsForArch(ctx, t.Arch.ArchType) {
+ if p.IgnoreMissingDependencies && !ctx.OtherModuleExists(dep) {
+ continue
+ }
+ ctx.AddFarVariationDependencies(t.Variations(), depTag, dep)
+ }
+ }
+}
+
+// See PackageModule.CopyDepsToZip
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
+ m := make(map[string]PackagingSpec)
+ ctx.WalkDeps(func(child Module, parent Module) bool {
+ if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
+ return false
+ }
+ for _, ps := range child.PackagingSpecs() {
+ if _, ok := m[ps.relPathInPackage]; !ok {
+ m[ps.relPathInPackage] = ps
+ }
+ }
+ return true
+ })
+
+ builder := NewRuleBuilder(pctx, ctx)
+
+ dir := PathForModuleOut(ctx, ".zip").OutputPath
+ builder.Command().Text("rm").Flag("-rf").Text(dir.String())
+ builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
+
+ seenDir := make(map[string]bool)
+ for _, k := range SortedStringKeys(m) {
+ ps := m[k]
+ destPath := dir.Join(ctx, ps.relPathInPackage).String()
+ destDir := filepath.Dir(destPath)
+ entries = append(entries, ps.relPathInPackage)
+ if _, ok := seenDir[destDir]; !ok {
+ seenDir[destDir] = true
+ builder.Command().Text("mkdir").Flag("-p").Text(destDir)
+ }
+ if ps.symlinkTarget == "" {
+ builder.Command().Text("cp").Input(ps.srcPath).Text(destPath)
+ } else {
+ builder.Command().Text("ln").Flag("-sf").Text(ps.symlinkTarget).Text(destPath)
+ }
+ if ps.executable {
+ builder.Command().Text("chmod").Flag("a+x").Text(destPath)
+ }
+ }
+
+ builder.Command().
+ 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("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
new file mode 100644
index 0000000..7269bfb
--- /dev/null
+++ b/android/packaging_test.go
@@ -0,0 +1,196 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/google/blueprint"
+)
+
+// Module to be packaged
+type componentTestModule struct {
+ ModuleBase
+ props struct {
+ Deps []string
+ }
+}
+
+// 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)
+ InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth)
+ return m
+}
+
+func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
+}
+
+func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ builtFile := PathForModuleOut(ctx, m.Name())
+ dir := ctx.Target().Arch.ArchType.Multilib
+ installDir := PathForModuleInstall(ctx, dir)
+ ctx.InstallFile(installDir, m.Name(), builtFile)
+}
+
+// Module that itself is a package
+type packageTestModule struct {
+ ModuleBase
+ PackagingBase
+
+ entries []string
+}
+
+func packageTestModuleFactory() Module {
+ module := &packageTestModule{}
+ InitPackageModule(module)
+ InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
+ return module
+}
+
+func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
+ m.AddDeps(ctx, installDepTag{})
+}
+
+func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ zipFile := PathForModuleOut(ctx, "myzip.zip").OutputPath
+ m.entries = m.CopyDepsToZip(ctx, zipFile)
+}
+
+func runPackagingTest(t *testing.T, bp string, expected []string) {
+ t.Helper()
+
+ config := TestArchConfig(buildDir, nil, bp, nil)
+
+ ctx := NewTestArchContext(config)
+ ctx.RegisterModuleType("component", componentTestModuleFactory)
+ ctx.RegisterModuleType("package_module", packageTestModuleFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ p := ctx.ModuleForTests("package", "android_common").Module().(*packageTestModule)
+ actual := p.entries
+ actual = SortedUniqueStrings(actual)
+ expected = SortedUniqueStrings(expected)
+ if !reflect.DeepEqual(actual, expected) {
+ t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected)
+ }
+}
+
+func TestPackagingBase(t *testing.T) {
+ runPackagingTest(t,
+ `
+ component {
+ name: "foo",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ }
+ `, []string{"lib64/foo"})
+
+ runPackagingTest(t,
+ `
+ component {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ }
+ `, []string{"lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t,
+ `
+ component {
+ name: "foo",
+ deps: ["bar"],
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ compile_multilib: "both",
+ }
+ `, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"})
+
+ runPackagingTest(t,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ compile_multilib: "32",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ multilib: {
+ lib32: {
+ deps: ["bar"],
+ },
+ },
+ compile_multilib: "both",
+ }
+ `, []string{"lib32/foo", "lib32/bar", "lib64/foo"})
+
+ runPackagingTest(t,
+ `
+ component {
+ name: "foo",
+ }
+
+ component {
+ name: "bar",
+ }
+
+ package_module {
+ name: "package",
+ deps: ["foo"],
+ multilib: {
+ first: {
+ deps: ["bar"],
+ },
+ },
+ compile_multilib: "both",
+ }
+ `, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
+}
diff --git a/android/path_properties.go b/android/path_properties.go
index 6b1cdb3..4bb706a 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -21,27 +21,31 @@
"github.com/google/blueprint/proptools"
)
+// This file implements support for automatically adding dependencies on any module referenced
+// with the ":module" module reference syntax in a property that is annotated with `android:"path"`.
+// The dependency is used by android.PathForModuleSrc to convert the module reference into the path
+// to the output file of the referenced module.
+
func registerPathDepsMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("pathdeps", pathDepsMutator).Parallel()
}
-// The pathDepsMutator automatically adds dependencies on any module that is listed with ":module" syntax in a
-// property that is tagged with android:"path".
+// The pathDepsMutator automatically adds dependencies on any module that is listed with the
+// ":module" module reference syntax in a property that is tagged with `android:"path"`.
func pathDepsMutator(ctx BottomUpMutatorContext) {
- m := ctx.Module().(Module)
- if m == nil {
- return
- }
+ props := ctx.Module().base().generalProperties
- props := m.base().generalProperties
-
+ // Iterate through each property struct of the module extracting the contents of all properties
+ // tagged with `android:"path"`.
var pathProperties []string
for _, ps := range props {
- pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...)
}
+ // Remove duplicates to avoid multiple dependencies.
pathProperties = FirstUniqueStrings(pathProperties)
+ // Add dependencies to anything that is a module reference.
for _, s := range pathProperties {
if m, t := SrcIsModuleWithTag(s); m != "" {
ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
@@ -49,34 +53,45 @@
}
}
-// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with android:"path" to extract
-// all their values from a property struct, returning them as a single slice of strings..
-func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
+// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
+// android:"path" to extract all their values from a property struct, returning them as a single
+// slice of strings.
+func pathPropertiesForPropertyStruct(ps interface{}) []string {
v := reflect.ValueOf(ps)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
}
+
+ // If the property struct is a nil pointer it can't have any paths set in it.
if v.IsNil() {
return nil
}
+
+ // v is now the reflect.Value for the concrete property struct.
v = v.Elem()
+ // Get or create the list of indexes of properties that are tagged with `android:"path"`.
pathPropertyIndexes := pathPropertyIndexesForPropertyStruct(ps)
var ret []string
for _, i := range pathPropertyIndexes {
+ // Turn an index into a field.
sv := fieldByIndex(v, i)
if !sv.IsValid() {
+ // Skip properties inside a nil pointer.
continue
}
+ // If the field is a non-nil pointer step into it.
if sv.Kind() == reflect.Ptr {
if sv.IsNil() {
continue
}
sv = sv.Elem()
}
+
+ // Collect paths from all strings and slices of strings.
switch sv.Kind() {
case reflect.String:
ret = append(ret, sv.String())
@@ -91,8 +106,8 @@
return ret
}
-// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when traversing a nil pointer
-// to a struct.
+// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
+// traversing a nil pointer to a struct.
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
if len(index) == 1 {
return v.Field(index[0])
@@ -111,9 +126,9 @@
var pathPropertyIndexesCache OncePer
-// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in property struct type that
-// are tagged with android:"path". Each index is a []int suitable for passing to reflect.Value.FieldByIndex. The value
-// is cached in a global cache by type.
+// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in
+// property struct type that are tagged with `android:"path"`. Each index is a []int suitable for
+// passing to reflect.Value.FieldByIndex. The value is cached in a global cache by type.
func pathPropertyIndexesForPropertyStruct(ps interface{}) [][]int {
key := NewCustomOnceKey(reflect.TypeOf(ps))
return pathPropertyIndexesCache.Once(key, func() interface{} {
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index f367b82..f964d9f 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -116,12 +116,12 @@
`
config := TestArchConfig(buildDir, nil, bp, nil)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
ctx.RegisterModuleType("test", pathDepsMutatorTestModuleFactory)
ctx.RegisterModuleType("filegroup", FileGroupFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
diff --git a/android/paths.go b/android/paths.go
index b13979d..10d8d0d 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 {
@@ -665,7 +706,7 @@
return Paths(ret)
}
-// WritablePaths is a slice of WritablePaths, used for multiple outputs.
+// WritablePaths is a slice of WritablePath, used for multiple outputs.
type WritablePaths []WritablePath
// Strings returns the string forms of the writable paths.
@@ -1113,6 +1154,17 @@
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
+type BazelOutPath struct {
+ OutputPath
+}
+
+var _ Path = BazelOutPath{}
+var _ objPathProvider = BazelOutPath{}
+
+func (p BazelOutPath) objPathWithExt(ctx ModuleContext, subdir, ext string) ModuleObjPath {
+ return PathForModuleObj(ctx, subdir, pathtools.ReplaceExtension(p.path, ext))
+}
+
// PathForVndkRefAbiDump returns an OptionalPath representing the path of the
// reference abi dump for the given module. This is not guaranteed to be valid.
func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string,
@@ -1151,6 +1203,24 @@
fileName+ext)
}
+// PathForBazelOut returns a Path representing the paths... under an output directory dedicated to
+// bazel-owned outputs.
+func PathForBazelOut(ctx PathContext, paths ...string) BazelOutPath {
+ execRootPathComponents := append([]string{"execroot", "__main__"}, paths...)
+ execRootPath := filepath.Join(execRootPathComponents...)
+ validatedExecRootPath, err := validatePath(execRootPath)
+ if err != nil {
+ reportPathError(ctx, err)
+ }
+
+ outputPath := OutputPath{basePath{"", ctx.Config(), ""},
+ ctx.Config().BazelContext.OutputBase()}
+
+ return BazelOutPath{
+ OutputPath: outputPath.withRel(validatedExecRootPath),
+ }
+}
+
// PathForModuleOut returns a Path representing the paths... under the module's
// output directory.
func PathForModuleOut(ctx ModuleContext, paths ...string) ModuleOutPath {
@@ -1249,6 +1319,10 @@
return p.config.buildDir
}
+func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
+ panic("Not implemented")
+}
+
var _ Path = InstallPath{}
var _ WritablePath = InstallPath{}
@@ -1312,7 +1386,7 @@
ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
- if ctx.InstallBypassMake() && ctx.Config().EmbeddedInMake() {
+ if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
ret = ret.ToMakePath()
}
@@ -1511,6 +1585,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/paths_test.go b/android/paths_test.go
index e7fd763..14a4773 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -980,12 +980,6 @@
func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- ctx := NewTestContext()
-
- ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
- ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
- ctx.RegisterModuleType("filegroup", FileGroupFactory)
-
fgBp := `
filegroup {
name: "a",
@@ -1015,7 +1009,13 @@
config := TestConfig(buildDir, nil, "", mockFS)
- ctx.Register(config)
+ ctx := NewTestContext(config)
+
+ ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
+ ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
+ ctx.RegisterModuleType("filegroup", FileGroupFactory)
+
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"})
FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
@@ -1224,12 +1224,12 @@
config := TestConfig(buildDir, nil, bp, nil)
config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.SetAllowMissingDependencies(true)
ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
diff --git a/android/phony.go b/android/phony.go
index f8e5a44..0adbb55 100644
--- a/android/phony.go
+++ b/android/phony.go
@@ -53,7 +53,7 @@
p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])
}
- if !ctx.Config().EmbeddedInMake() {
+ if !ctx.Config().KatiEnabled() {
for _, phony := range p.phonyList {
ctx.Build(pctx, BuildParams{
Rule: blueprint.Phony,
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 294a6e0..39d30c5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -17,6 +17,7 @@
import (
"fmt"
"reflect"
+ "strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -74,6 +75,12 @@
srcsPropertyName string
}
+// RemoveOptionalPrebuiltPrefix returns the result of removing the "prebuilt_" prefix from the
+// supplied name if it has one, or returns the name unmodified if it does not.
+func RemoveOptionalPrebuiltPrefix(name string) string {
+ return strings.TrimPrefix(name, "prebuilt_")
+}
+
func (p *Prebuilt) Name(name string) string {
return "prebuilt_" + name
}
@@ -178,6 +185,9 @@
srcPropertyName := proptools.PropertyNameForField(srcField)
srcsSupplier := func(ctx BaseModuleContext) []string {
+ if !module.Enabled() {
+ return nil
+ }
value := srcPropsValue.FieldByIndex(srcFieldIndex)
if value.Kind() == reflect.Ptr {
value = value.Elem()
@@ -279,7 +289,7 @@
})
}
} else {
- m.SkipInstall()
+ m.HideFromMake()
}
}
}
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index e2555e4..516d042 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -57,7 +57,7 @@
func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
sourcePath := t.prebuilt.SingleSourcePath(ctx)
- installedPath := PathForModuleOut(ctx, t.ModuleBase.Name())
+ installedPath := PathForModuleOut(ctx, t.BaseModuleName())
deps := PathsForModuleSrc(ctx, t.properties.Deps)
var fromPath = sourcePath.String()
@@ -75,11 +75,20 @@
},
})
+ packagingDir := PathForModuleInstall(ctx, t.BaseModuleName())
+ ctx.PackageFile(packagingDir, sourcePath.String(), sourcePath)
+ for _, dep := range deps {
+ ctx.PackageFile(packagingDir, dep.String(), dep)
+ }
+
t.toolPath = OptionalPathForPath(installedPath)
}
func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+ if t.Target().Os != BuildOs {
+ return
+ }
ctx.StrictRaw(makeVar, t.toolPath.String())
}
}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 854395e..9ac3875 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -288,10 +288,10 @@
{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
}
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
registerTestPrebuiltBuildComponents(ctx)
ctx.RegisterModuleType("filegroup", FileGroupFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
FailIfErrored(t, errs)
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
new file mode 100644
index 0000000..1b7e77d
--- /dev/null
+++ b/android/queryview.go
@@ -0,0 +1,112 @@
+// 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"
+ "os"
+ "strings"
+
+ "github.com/google/blueprint"
+)
+
+// The Bazel QueryView singleton is responsible for generating the Ninja actions
+// 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{}
+}
+
+// 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{}
+}
+
+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
+ moduleListFilePath := pathForBuildToolDep(ctx, ctx.Config().moduleListFile)
+ deps = append(deps, moduleListFilePath)
+ deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
+
+ 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 %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(
+ descriptionTemplate,
+ primaryBuilder.Base()),
+ Deps: blueprint.DepsGCC,
+ Depfile: "${outDir}/.queryview-depfile.d",
+ },
+ "outDir")
+
+ ctx.Build(pctx, BuildParams{
+ Rule: bazelQueryView,
+ Output: bazelQueryViewWorkspaceFile,
+ Inputs: deps,
+ Args: map[string]string{
+ "outDir": bazelQueryViewDirectory.String(),
+ },
+ })
+
+ // 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 ad3df7e..b26f9b9 100644
--- a/android/register.go
+++ b/android/register.go
@@ -29,7 +29,7 @@
type singleton struct {
name string
- factory blueprint.SingletonFactory
+ factory SingletonFactory
}
var singletons []singleton
@@ -57,11 +57,11 @@
// SingletonFactoryAdaptor wraps a SingletonFactory into a blueprint.SingletonFactory by converting
// a Singleton into a blueprint.Singleton
-func SingletonFactoryAdaptor(factory SingletonFactory) blueprint.SingletonFactory {
+func SingletonFactoryAdaptor(ctx *Context, factory SingletonFactory) blueprint.SingletonFactory {
return func() blueprint.Singleton {
singleton := factory()
if makevars, ok := singleton.(SingletonMakeVarsProvider); ok {
- registerSingletonMakeVarsProvider(makevars)
+ registerSingletonMakeVarsProvider(ctx.config, makevars)
}
return &singletonAdaptor{Singleton: singleton}
}
@@ -72,26 +72,42 @@
}
func RegisterSingletonType(name string, factory SingletonFactory) {
- singletons = append(singletons, singleton{name, SingletonFactoryAdaptor(factory)})
+ singletons = append(singletons, singleton{name, factory})
}
func RegisterPreSingletonType(name string, factory SingletonFactory) {
- preSingletons = append(preSingletons, singleton{name, SingletonFactoryAdaptor(factory)})
+ preSingletons = append(preSingletons, singleton{name, factory})
}
type Context struct {
*blueprint.Context
+ config Config
}
-func NewContext() *Context {
- ctx := &Context{blueprint.NewContext()}
+func NewContext(config Config) *Context {
+ ctx := &Context{blueprint.NewContext(), config}
ctx.SetSrcDir(absSrcDir)
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, t.factory)
+ ctx.RegisterPreSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
for _, t := range moduleTypes {
@@ -99,21 +115,23 @@
}
for _, t := range singletons {
- ctx.RegisterSingletonType(t.name, t.factory)
+ ctx.RegisterSingletonType(t.name, SingletonFactoryAdaptor(ctx, t.factory))
}
registerMutators(ctx.Context, preArch, preDeps, postDeps, finalDeps)
- ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(BazelSingleton))
+ ctx.RegisterSingletonType("bazeldeps", SingletonFactoryAdaptor(ctx, BazelSingleton))
// Register phony just before makevars so it can write out its phony rules as Make rules
- ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(phonySingletonFactory))
+ ctx.RegisterSingletonType("phony", SingletonFactoryAdaptor(ctx, phonySingletonFactory))
// Register makevars after other singletons so they can export values through makevars
- ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(makeVarsSingletonFunc))
+ ctx.RegisterSingletonType("makevars", SingletonFactoryAdaptor(ctx, makeVarsSingletonFunc))
- // Register env last so that it can track all used environment variables
- ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(EnvSingleton))
+ // Register env and ninjadeps last so that they can track all used environment variables and
+ // Ninja file dependencies stored in the config.
+ ctx.RegisterSingletonType("env", SingletonFactoryAdaptor(ctx, EnvSingleton))
+ ctx.RegisterSingletonType("ninjadeps", SingletonFactoryAdaptor(ctx, ninjaDepsSingletonFactory))
}
func ModuleTypeFactories() map[string]ModuleFactory {
@@ -157,7 +175,7 @@
// Extracting the actual registration into a separate RegisterBuildComponents(ctx) function
// allows it to be used to initialize test context, e.g.
//
-// ctx := android.NewTestContext()
+// ctx := android.NewTestContext(config)
// RegisterBuildComponents(ctx)
var InitRegistrationContext RegistrationContext = &initRegistrationContext{
moduleTypes: make(map[string]ModuleFactory),
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 8dc9d6a..84501fe 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -15,33 +15,50 @@
package android
import (
+ "crypto/sha256"
"fmt"
+ "path/filepath"
"sort"
"strings"
+ "testing"
+ "github.com/golang/protobuf/proto"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
+ "android/soong/cmd/sbox/sbox_proto"
"android/soong/shared"
)
+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 {
- commands []*RuleBuilderCommand
- installs RuleBuilderInstalls
- temporariesSet map[WritablePath]bool
- restat bool
- sbox bool
- highmem bool
- remoteable RemoteRuleSupports
- sboxOutDir WritablePath
- missingDeps []string
+ pctx PackageContext
+ ctx BuilderContext
+
+ commands []*RuleBuilderCommand
+ installs RuleBuilderInstalls
+ temporariesSet map[WritablePath]bool
+ restat bool
+ sbox bool
+ highmem bool
+ remoteable RemoteRuleSupports
+ 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),
}
}
@@ -102,12 +119,14 @@
return r
}
-// Sbox marks the rule as needing to be wrapped by sbox. The WritablePath should point to the output
-// directory that sbox will wipe. It should not be written to by any other rule. sbox will ensure
-// that all outputs have been written, and will discard any output files that were not specified.
+// Sbox marks the rule as needing to be wrapped by sbox. The outputDir should point to the output
+// directory that sbox will wipe. It should not be written to by any other rule. manifestPath should
+// point to a location where sbox's manifest will be written and must be outside outputDir. sbox
+// will ensure that all outputs have been written, and will discard any output files that were not
+// specified.
//
// Sbox is not compatible with Restat()
-func (r *RuleBuilder) Sbox(outputDir WritablePath) *RuleBuilder {
+func (r *RuleBuilder) Sbox(outputDir WritablePath, manifestPath WritablePath) *RuleBuilder {
if r.sbox {
panic("Sbox() may not be called more than once")
}
@@ -118,7 +137,21 @@
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
}
@@ -133,8 +166,7 @@
// race with any call to Build.
func (r *RuleBuilder) Command() *RuleBuilderCommand {
command := &RuleBuilderCommand{
- sbox: r.sbox,
- sboxOutDir: r.sboxOutDir,
+ rule: r,
}
r.commands = append(r.commands, command)
return command
@@ -163,7 +195,7 @@
}
// Inputs returns the list of paths that were passed to the RuleBuilderCommand methods that take
-// input paths, such as RuleBuilderCommand.Input, RuleBuilderComand.Implicit, or
+// input paths, such as RuleBuilderCommand.Input, RuleBuilderCommand.Implicit, or
// RuleBuilderCommand.FlagWithInput. Inputs to a command that are also outputs of another command
// in the same RuleBuilder are filtered out. The list is sorted and duplicates removed.
func (r *RuleBuilder) Inputs() Paths {
@@ -362,7 +394,7 @@
return commands
}
-// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
+// NinjaEscapedCommands returns a slice containing the built command line after ninja escaping for each call to
// RuleBuilder.Command.
func (r *RuleBuilder) NinjaEscapedCommands() []string {
var commands []string
@@ -382,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(),
@@ -413,12 +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
+ // 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())
}
}
}
@@ -427,6 +460,7 @@
tools := r.Tools()
commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
+ inputs := r.Inputs()
if len(commands) == 0 {
return
@@ -438,30 +472,90 @@
commandString := strings.Join(commands, " && ")
if r.sbox {
- sboxOutputs := make([]string, len(outputs))
- for i, output := range outputs {
- sboxOutputs[i] = "__SBOX_OUT_DIR__/" + Rel(ctx, r.sboxOutDir.String(), output.String())
- }
-
- commandString = proptools.ShellEscape(commandString)
- if !strings.HasPrefix(commandString, `'`) {
- commandString = `'` + commandString + `'`
- }
-
- sboxCmd := &RuleBuilderCommand{}
- sboxCmd.BuiltTool(ctx, "sbox").
- Flag("-c").Text(commandString).
- Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
- Flag("--output-root").Text(r.sboxOutDir.String())
+ // If running the command inside sbox, write the rule data out to an sbox
+ // manifest.textproto.
+ manifest := sbox_proto.Manifest{}
+ command := sbox_proto.Command{}
+ manifest.Commands = append(manifest.Commands, &command)
+ command.Command = proto.String(commandString)
if depFile != nil {
- sboxCmd.Flag("--depfile-out").Text(depFile.String())
+ manifest.OutputDepfile = proto.String(depFile.String())
}
- sboxCmd.Flags(sboxOutputs)
+ // 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 after running the commands.
+ sboxOutputs := make([]string, len(outputs))
+ for i, output := range outputs {
+ 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)),
+ To: proto.String(output.String()),
+ })
+ }
+
+ // Add a hash of the list of input files to the manifest so that the textproto file
+ // changes when the list of input files changes and causes the sbox rule that
+ // depends on it to rerun.
+ command.InputHash = proto.String(hashSrcFiles(inputs))
+
+ // 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(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
+ if manifestInOutDir {
+ 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(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{
+ rule: &RuleBuilder{
+ ctx: r.ctx,
+ },
+ }
+ sboxCmd.Text("rm -rf").Output(r.outDir)
+ sboxCmd.Text("&&")
+ 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
+ // dependencies of the final sbox rule.
commandString = sboxCmd.buf.String()
tools = append(tools, sboxCmd.tools...)
+ inputs = append(inputs, sboxCmd.inputs...)
+ } else {
+ // If not using sbox the rule will run the command directly, put the hash of the
+ // list of input files in a comment at the end of the command line to ensure ninja
+ // reruns the rule when the list of input files changes.
+ commandString += " # hash of input list: " + hashSrcFiles(inputs)
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
@@ -478,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,
@@ -499,7 +593,7 @@
Pool: pool,
}),
Inputs: rspFileInputs,
- Implicits: r.Inputs(),
+ Implicits: inputs,
Output: output,
ImplicitOutputs: implicitOutputs,
SymlinkOutputs: r.SymlinkOutputs(),
@@ -514,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
@@ -522,19 +618,17 @@
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
- sboxOutDir WritablePath
}
func (c *RuleBuilderCommand) addInput(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
- return "__SBOX_OUT_DIR__/" + rel
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
+ return filepath.Join(sboxOutDir, rel)
}
}
c.inputs = append(c.inputs, path)
@@ -542,9 +636,9 @@
}
func (c *RuleBuilderCommand) addImplicit(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
- return "__SBOX_OUT_DIR__/" + rel
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
+ return filepath.Join(sboxOutDir, rel)
}
}
c.implicits = append(c.implicits, path)
@@ -555,15 +649,92 @@
c.orderOnlys = append(c.orderOnlys, path)
}
-func (c *RuleBuilderCommand) outputStr(path Path) string {
- if c.sbox {
+// 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.sboxOutDir.String(), path.String())
- return "__SBOX_OUT_DIR__/" + rel
+ rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+ return filepath.Join(sboxOutDir, rel)
}
return path.String()
}
+// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox. It assumes that sandboxing and tool
+// sandboxing are enabled.
+func SboxPathForTool(ctx BuilderContext, path Path) string {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
+}
+
+func sboxPathForToolRel(ctx BuilderContext, path Path) string {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
+ if isRelOut {
+ // The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+ return filepath.Join(sboxToolsSubDir, "out", relOut)
+ }
+ // The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+ return filepath.Join(sboxToolsSubDir, "src", path.String())
+}
+
+// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox. This can be used on the RuleBuilder command line to
+// reference the tool.
+func SboxPathForPackagedTool(spec PackagingSpec) string {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
+func sboxPathForPackagedToolRel(spec PackagingSpec) string {
+ return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
+}
+
+// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
+// if it is not. This can be used on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathForTool(path Path) string {
+ if c.rule.sbox && c.rule.sboxTools {
+ return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+ }
+ return path.String()
+}
+
+// PackagedTool adds the specified tool path to the command line. It can only be used with tool
+// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
+func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("PackagedTool() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, spec)
+ c.Text(sboxPathForPackagedToolRel(spec))
+ return c
+}
+
+// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
+// line. It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("ImplicitPackagedTool() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, spec)
+ return c
+}
+
+// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
+// line. It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
+ if !c.rule.sboxTools {
+ panic("ImplicitPackagedTools() requires SandboxTools()")
+ }
+
+ c.packagedTools = append(c.packagedTools, specs...)
+ return c
+}
+
// Text adds the specified raw text to the command line. The text should not contain input or output paths or the
// rule will not have them listed in its dependencies or outputs.
func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@@ -632,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
@@ -640,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
@@ -709,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
@@ -724,10 +907,10 @@
// 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("__SBOX_OUT_DIR__")
+ return c.Text(sboxOutDir)
}
// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
@@ -735,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
@@ -826,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
@@ -867,6 +1050,19 @@
return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
}
+// RuleBuilderSboxProtoForTests takes the BuildParams for the manifest passed to RuleBuilder.Sbox()
+// and returns sbox testproto generated by the RuleBuilder.
+func RuleBuilderSboxProtoForTests(t *testing.T, params TestingBuildParams) *sbox_proto.Manifest {
+ t.Helper()
+ content := ContentFromFileRuleForTests(t, params)
+ manifest := sbox_proto.Manifest{}
+ err := proto.UnmarshalText(content, &manifest)
+ if err != nil {
+ t.Fatalf("failed to unmarshal manifest: %s", err.Error())
+ }
+ return &manifest
+}
+
func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
if len(spans) == 0 {
return proptools.NinjaEscape(s)
@@ -906,3 +1102,30 @@
}
return s
}
+
+// hashSrcFiles returns a hash of the list of source files. It is used to ensure the command line
+// or the sbox textproto manifest change even if the input files are not listed on the command line.
+func hashSrcFiles(srcFiles Paths) string {
+ h := sha256.New()
+ srcFileList := strings.Join(srcFiles.Strings(), "\n")
+ 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 0d1070d..06ea124 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -18,6 +18,7 @@
"fmt"
"path/filepath"
"reflect"
+ "regexp"
"strings"
"testing"
@@ -26,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,
@@ -43,9 +44,9 @@
}
func ExampleRuleBuilder() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ld")).
@@ -54,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())
@@ -69,9 +70,9 @@
}
func ExampleRuleBuilder_SymlinkOutputs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ln")).
@@ -95,9 +96,9 @@
}
func ExampleRuleBuilder_Temporary() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -122,9 +123,9 @@
}
func ExampleRuleBuilder_DeleteTemporaryFiles() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -150,9 +151,9 @@
}
func ExampleRuleBuilder_Installs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
out := PathForOutput(ctx, "linked")
@@ -170,9 +171,9 @@
}
func ExampleRuleBuilderCommand() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
// chained
rule.Command().
@@ -193,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:
@@ -218,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:
@@ -227,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:
@@ -236,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:
@@ -245,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:
@@ -254,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:
@@ -263,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())
@@ -273,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())
@@ -282,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())
@@ -304,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().
@@ -354,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{
@@ -388,22 +394,23 @@
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)
wantCommands := []string{
- "__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output __SBOX_OUT_DIR__/SymlinkOutput Text Tool after command2 old cmd",
- "command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2",
- "command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3",
+ "__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 Tool after command2 old cmd",
+ "command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 tool2",
+ "command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
}
- wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer __SBOX_OUT_DIR__/DepFile __SBOX_OUT_DIR__/depfile __SBOX_OUT_DIR__/ImplicitDepFile __SBOX_OUT_DIR__/depfile2"
+ wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/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)
@@ -425,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)
}
})
@@ -441,7 +486,7 @@
type testRuleBuilderModule struct {
ModuleBase
properties struct {
- Src string
+ Srcs []string
Restat bool
Sbox bool
@@ -449,12 +494,13 @@
}
func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- in := PathForSource(ctx, t.properties.Src)
- out := PathForModuleOut(ctx, ctx.ModuleName())
- outDep := PathForModuleOut(ctx, ctx.ModuleName()+".d")
- outDir := PathForModuleOut(ctx)
+ in := PathsForSource(ctx, t.properties.Srcs)
+ out := PathForModuleOut(ctx, "gen", ctx.ModuleName())
+ outDep := PathForModuleOut(ctx, "gen", ctx.ModuleName()+".d")
+ outDir := PathForModuleOut(ctx, "gen")
+ manifestPath := PathForModuleOut(ctx, "sbox.textproto")
- testRuleBuilder_Build(ctx, in, out, outDep, outDir, t.properties.Restat, t.properties.Sbox)
+ testRuleBuilder_Build(ctx, in, out, outDep, outDir, manifestPath, t.properties.Restat, t.properties.Sbox)
}
type testRuleBuilderSingleton struct{}
@@ -465,26 +511,27 @@
func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
in := PathForSource(ctx, "bar")
- out := PathForOutput(ctx, "baz")
- outDep := PathForOutput(ctx, "baz.d")
- outDir := PathForOutput(ctx)
- testRuleBuilder_Build(ctx, in, out, outDep, outDir, true, false)
+ out := PathForOutput(ctx, "singleton/gen/baz")
+ outDep := PathForOutput(ctx, "singleton/gen/baz.d")
+ outDir := PathForOutput(ctx, "singleton/gen")
+ manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
+ testRuleBuilder_Build(ctx, Paths{in}, out, outDep, outDir, manifestPath, true, false)
}
-func testRuleBuilder_Build(ctx BuilderContext, in Path, out, outDep, outDir WritablePath, restat, sbox bool) {
- rule := NewRuleBuilder()
+func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
+ rule := NewRuleBuilder(pctx, ctx)
if sbox {
- rule.Sbox(outDir)
+ rule.Sbox(outDir, manifestPath)
}
- rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out).ImplicitDepFile(outDep)
+ rule.Command().Tool(PathForSource(ctx, "cp")).Inputs(in).Output(out).ImplicitDepFile(outDep)
if restat {
rule.Restat()
}
- rule.Build(pctx, ctx, "rule", "desc")
+ rule.Build("rule", "desc")
}
func TestRuleBuilder_Build(t *testing.T) {
@@ -496,30 +543,33 @@
bp := `
rule_builder_test {
name: "foo",
- src: "bar",
+ srcs: ["bar"],
restat: true,
}
rule_builder_test {
name: "foo_sbox",
- src: "bar",
+ srcs: ["bar"],
sbox: true,
}
`
config := TestConfig(buildDir, nil, bp, fs)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
ctx.RegisterSingletonType("rule_builder_test", testRuleBuilderSingletonFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
FailIfErrored(t, errs)
- check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraCmdDeps []string) {
+ check := func(t *testing.T, params TestingBuildParams, wantCommand, wantOutput, wantDepfile string, wantRestat bool, extraImplicits, extraCmdDeps []string) {
t.Helper()
- if params.RuleParams.Command != wantCommand {
+ command := params.RuleParams.Command
+ re := regexp.MustCompile(" # hash of input list: [a-z0-9]*$")
+ command = re.ReplaceAllLiteralString(command, "")
+ if command != wantCommand {
t.Errorf("\nwant RuleParams.Command = %q\n got %q", wantCommand, params.RuleParams.Command)
}
@@ -532,7 +582,8 @@
t.Errorf("want RuleParams.Restat = %v, got %v", wantRestat, params.RuleParams.Restat)
}
- if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" {
+ wantImplicits := append([]string{"bar"}, extraImplicits...)
+ if !reflect.DeepEqual(params.Implicits.Strings(), wantImplicits) {
t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings())
}
@@ -554,27 +605,29 @@
}
t.Run("module", func(t *testing.T) {
- outFile := filepath.Join(buildDir, ".intermediates", "foo", "foo")
+ outFile := filepath.Join(buildDir, ".intermediates", "foo", "gen", "foo")
check(t, ctx.ModuleForTests("foo", "").Rule("rule"),
"cp bar "+outFile,
- outFile, outFile+".d", true, nil)
+ outFile, outFile+".d", true, nil, nil)
})
t.Run("sbox", func(t *testing.T) {
outDir := filepath.Join(buildDir, ".intermediates", "foo_sbox")
- outFile := filepath.Join(outDir, "foo_sbox")
- depFile := filepath.Join(outDir, "foo_sbox.d")
+ outFile := filepath.Join(outDir, "gen/foo_sbox")
+ depFile := filepath.Join(outDir, "gen/foo_sbox.d")
+ manifest := filepath.Join(outDir, "sbox.textproto")
sbox := filepath.Join(buildDir, "host", config.PrebuiltOS(), "bin/sbox")
sandboxPath := shared.TempDirForOutDir(buildDir)
- cmd := sbox + ` -c 'cp bar __SBOX_OUT_DIR__/foo_sbox' --sandbox-path ` + sandboxPath + " --output-root " + outDir + " --depfile-out " + depFile + " __SBOX_OUT_DIR__/foo_sbox"
+ cmd := `rm -rf ` + outDir + `/gen && ` +
+ sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
- check(t, ctx.ModuleForTests("foo_sbox", "").Rule("rule"),
- cmd, outFile, depFile, false, []string{sbox})
+ check(t, ctx.ModuleForTests("foo_sbox", "").Output("gen/foo_sbox"),
+ cmd, outFile, depFile, false, []string{manifest}, []string{sbox})
})
t.Run("singleton", func(t *testing.T) {
- outFile := filepath.Join(buildDir, "baz")
+ outFile := filepath.Join(buildDir, "singleton/gen/baz")
check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"),
- "cp bar "+outFile, outFile, outFile+".d", true, nil)
+ "cp bar "+outFile, outFile, outFile+".d", true, nil, nil)
})
}
@@ -651,3 +704,80 @@
})
}
}
+
+func TestRuleBuilderHashInputs(t *testing.T) {
+ // The basic idea here is to verify that the command (in the case of a
+ // non-sbox rule) or the sbox textproto manifest contain a hash of the
+ // inputs.
+
+ // By including a hash of the inputs, we cause the rule to re-run if
+ // the list of inputs changes because the command line or a dependency
+ // changes.
+
+ bp := `
+ rule_builder_test {
+ name: "hash0",
+ srcs: ["in1.txt", "in2.txt"],
+ }
+ rule_builder_test {
+ name: "hash0_sbox",
+ srcs: ["in1.txt", "in2.txt"],
+ sbox: true,
+ }
+ rule_builder_test {
+ name: "hash1",
+ srcs: ["in1.txt", "in2.txt", "in3.txt"],
+ }
+ rule_builder_test {
+ name: "hash1_sbox",
+ srcs: ["in1.txt", "in2.txt", "in3.txt"],
+ sbox: true,
+ }
+ `
+ testcases := []struct {
+ name string
+ expectedHash string
+ }{
+ {
+ name: "hash0",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
+ expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
+ },
+ {
+ name: "hash1",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
+ },
+ }
+
+ config := TestConfig(buildDir, nil, bp, nil)
+ ctx := NewTestContext(config)
+ ctx.RegisterModuleType("rule_builder_test", testRuleBuilderFactory)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ t.Run("sbox", func(t *testing.T) {
+ gen := ctx.ModuleForTests(test.name+"_sbox", "")
+ manifest := RuleBuilderSboxProtoForTests(t, gen.Output("sbox.textproto"))
+ hash := manifest.Commands[0].GetInputHash()
+
+ if g, w := hash, test.expectedHash; g != w {
+ t.Errorf("Expected has %q, got %q", w, g)
+ }
+ })
+ t.Run("", func(t *testing.T) {
+ gen := ctx.ModuleForTests(test.name+"", "")
+ command := gen.Output("gen/" + test.name).RuleParams.Command
+ if g, w := command, " # hash of input list: "+test.expectedHash; !strings.HasSuffix(g, w) {
+ t.Errorf("Expected command line to end with %q, got %q", w, g)
+ }
+ })
+ })
+ }
+}
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 9677f34..b1810b3 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -175,7 +175,7 @@
},
}
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
@@ -183,7 +183,7 @@
ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
FailIfErrored(t, errs)
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 142a813..5a6917e 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -158,11 +158,7 @@
return []error{fmt.Errorf("bool_variable name must not be blank")}
}
- mt.Variables = append(mt.Variables, &boolVariable{
- baseVariable: baseVariable{
- variable: name,
- },
- })
+ mt.Variables = append(mt.Variables, newBoolVariable(name))
}
for _, name := range props.Value_variables {
@@ -420,6 +416,9 @@
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
// based on SoongConfig values.
+// Expects that props contains a struct field with name soong_config_variables. The fields within
+// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
+// props should be generated via CreateProperties.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
props = props.Elem().FieldByName(soongConfigProperty)
@@ -505,6 +504,14 @@
baseVariable
}
+func newBoolVariable(name string) *boolVariable {
+ return &boolVariable{
+ baseVariable{
+ variable: name,
+ },
+ }
+}
+
func (b boolVariable) variableValuesType() reflect.Type {
return emptyInterfaceType
}
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 4190016..fb0e189 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -17,6 +17,8 @@
import (
"reflect"
"testing"
+
+ "github.com/google/blueprint/proptools"
)
func Test_CanonicalizeToProperty(t *testing.T) {
@@ -247,3 +249,72 @@
})
}
}
+
+type properties struct {
+ A *string
+ B bool
+}
+type soongConfigVariables struct {
+ Bool_var properties
+ Other_bool_var properties
+}
+
+type soongConfigProps struct {
+ Soong_config_variables soongConfigVariables
+}
+
+func Test_PropertiesToApply(t *testing.T) {
+
+ mt := &ModuleType{
+ BaseModuleType: "foo",
+ ConfigNamespace: "bar",
+ Variables: []soongConfigVariable{
+ newBoolVariable("bool_var"),
+ newBoolVariable("other_bool_var"),
+ },
+ affectableProperties: []string{
+ "a",
+ "b",
+ },
+ }
+ props := soongConfigProps{
+ Soong_config_variables: soongConfigVariables{
+ Bool_var: properties{
+ A: proptools.StringPtr("a"),
+ B: true,
+ },
+ Other_bool_var: properties{
+ A: proptools.StringPtr("other"),
+ B: false,
+ },
+ },
+ }
+
+ testCases := []struct {
+ config SoongConfig
+ wantProps []interface{}
+ }{
+ {
+ config: Config(map[string]string{}),
+ },
+ {
+ config: Config(map[string]string{"bool_var": "y"}),
+ wantProps: []interface{}{props.Soong_config_variables.Bool_var},
+ },
+ {
+ config: Config(map[string]string{"other_bool_var": "y"}),
+ wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+ },
+ }
+
+ for _, tc := range testCases {
+ gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+ if err != nil {
+ t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+ }
+
+ if !reflect.DeepEqual(gotProps, tc.wantProps) {
+ t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+ }
+ }
+}
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/testing.go b/android/testing.go
index 8ea4168..6539063 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -25,14 +25,14 @@
"github.com/google/blueprint"
)
-func NewTestContext() *TestContext {
+func NewTestContext(config Config) *TestContext {
namespaceExportFilter := func(namespace *Namespace) bool {
return true
}
nameResolver := NewNameResolver(namespaceExportFilter)
ctx := &TestContext{
- Context: &Context{blueprint.NewContext()},
+ Context: &Context{blueprint.NewContext(), config},
NameResolver: nameResolver,
}
@@ -40,11 +40,16 @@
ctx.postDeps = append(ctx.postDeps, registerPathDepsMutator)
+ ctx.SetFs(ctx.config.fs)
+ if ctx.config.mockBpList != "" {
+ ctx.SetModuleListFile(ctx.config.mockBpList)
+ }
+
return ctx
}
-func NewTestArchContext() *TestContext {
- ctx := NewTestContext()
+func NewTestArchContext(config Config) *TestContext {
+ ctx := NewTestContext(config)
ctx.preDeps = append(ctx.preDeps, registerArchMutator)
return ctx
}
@@ -53,7 +58,6 @@
*Context
preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
NameResolver *NameResolver
- config Config
}
func (ctx *TestContext) PreArchMutators(f RegisterMutatorFunc) {
@@ -77,16 +81,10 @@
ctx.finalDeps = append(ctx.finalDeps, f)
}
-func (ctx *TestContext) Register(config Config) {
- ctx.SetFs(config.fs)
- if config.mockBpList != "" {
- ctx.SetModuleListFile(config.mockBpList)
- }
+func (ctx *TestContext) Register() {
registerMutators(ctx.Context.Context, ctx.preArch, ctx.preDeps, ctx.postDeps, ctx.finalDeps)
ctx.RegisterSingletonType("env", EnvSingleton)
-
- ctx.config = config
}
func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -106,7 +104,7 @@
}
func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
- ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(factory))
+ ctx.Context.RegisterSingletonType(name, SingletonFactoryAdaptor(ctx.Context, factory))
}
func (ctx *TestContext) ModuleForTests(name, variant string) TestingModule {
@@ -426,8 +424,8 @@
}
-func SetInMakeForTests(config Config) {
- config.inMake = true
+func SetKatiEnabledForTests(config Config) {
+ config.katiEnabled = true
}
func AndroidMkEntriesForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) []AndroidMkEntries {
diff --git a/android/util.go b/android/util.go
index 65c5f1b..0f940fa 100644
--- a/android/util.go
+++ b/android/util.go
@@ -29,56 +29,44 @@
return append([]string(nil), s...)
}
+// JoinWithPrefix prepends the prefix to each string in the list and
+// returns them joined together with " " as separator.
func JoinWithPrefix(strs []string, prefix string) string {
if len(strs) == 0 {
return ""
}
- if len(strs) == 1 {
- return prefix + strs[0]
+ var buf strings.Builder
+ buf.WriteString(prefix)
+ buf.WriteString(strs[0])
+ for i := 1; i < len(strs); i++ {
+ buf.WriteString(" ")
+ buf.WriteString(prefix)
+ buf.WriteString(strs[i])
}
-
- n := len(" ") * (len(strs) - 1)
- for _, s := range strs {
- n += len(prefix) + len(s)
- }
-
- ret := make([]byte, 0, n)
- for i, s := range strs {
- if i != 0 {
- ret = append(ret, ' ')
- }
- ret = append(ret, prefix...)
- ret = append(ret, s...)
- }
- return string(ret)
+ return buf.String()
}
+// JoinWithSuffix appends the suffix to each string in the list and
+// returns them joined together with given separator.
func JoinWithSuffix(strs []string, suffix string, separator string) string {
if len(strs) == 0 {
return ""
}
- if len(strs) == 1 {
- return strs[0] + suffix
+ var buf strings.Builder
+ buf.WriteString(strs[0])
+ buf.WriteString(suffix)
+ for i := 1; i < len(strs); i++ {
+ buf.WriteString(separator)
+ buf.WriteString(strs[i])
+ buf.WriteString(suffix)
}
-
- n := len(" ") * (len(strs) - 1)
- for _, s := range strs {
- n += len(suffix) + len(s)
- }
-
- ret := make([]byte, 0, n)
- for i, s := range strs {
- if i != 0 {
- ret = append(ret, separator...)
- }
- ret = append(ret, s...)
- ret = append(ret, suffix...)
- }
- return string(ret)
+ return buf.String()
}
+// SortedIntKeys returns the keys of the given integer-keyed map in the ascending order
+// TODO(asmundak): once Go has generics, combine this with SortedStringKeys below.
func SortedIntKeys(m interface{}) []int {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
@@ -93,6 +81,7 @@
return s
}
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
func SortedStringKeys(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
@@ -107,6 +96,7 @@
return s
}
+// SortedStringMapValues returns the values of the string-values map in the ascending order
func SortedStringMapValues(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
@@ -121,6 +111,7 @@
return s
}
+// IndexList returns the index of the first occurrence of the given string in the list or -1
func IndexList(s string, list []string) int {
for i, l := range list {
if l == s {
@@ -131,6 +122,7 @@
return -1
}
+// InList checks if the string belongs to the list
func InList(s string, list []string) bool {
return IndexList(s, list) != -1
}
@@ -176,7 +168,10 @@
return -1
}
+// FilterList divides the string list into two lists: one with the strings belonging
+// to the given filter list, and the other with the remaining ones
func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
+ // InList is O(n). May be worth using more efficient lookup for longer lists.
for _, l := range list {
if InList(l, filter) {
filtered = append(filtered, l)
@@ -188,6 +183,8 @@
return
}
+// RemoveListFromList removes the strings belonging to the filter list from the
+// given list and returns the result
func RemoveListFromList(list []string, filter_out []string) (result []string) {
result = make([]string, 0, len(list))
for _, l := range list {
@@ -198,20 +195,18 @@
return
}
+// RemoveFromList removes given string from the string list.
func RemoveFromList(s string, list []string) (bool, []string) {
- i := IndexList(s, list)
- if i == -1 {
- return false, list
- }
-
- result := make([]string, 0, len(list)-1)
- result = append(result, list[:i]...)
- for _, l := range list[i+1:] {
- if l != s {
- result = append(result, l)
+ result := make([]string, 0, len(list))
+ var removed bool
+ for _, item := range list {
+ if item != s {
+ result = append(result, item)
+ } else {
+ removed = true
}
}
- return true, result
+ return removed, result
}
// FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
@@ -317,11 +312,10 @@
return s[1], s[2], true
}
+// GetNumericSdkVersion removes the first occurrence of system_ in a string,
+// which is assumed to be something like "system_1.2.3"
func GetNumericSdkVersion(v string) string {
- if strings.Contains(v, "system_") {
- return strings.Replace(v, "system_", "", 1)
- }
- return v
+ return strings.Replace(v, "system_", "", 1)
}
// copied from build/kati/strutil.go
@@ -334,17 +328,17 @@
return str
}
in := str
- trimed := str
+ trimmed := str
if ps[0] != "" {
- trimed = strings.TrimPrefix(in, ps[0])
- if trimed == in {
+ trimmed = strings.TrimPrefix(in, ps[0])
+ if trimmed == in {
return str
}
}
- in = trimed
+ in = trimmed
if ps[1] != "" {
- trimed = strings.TrimSuffix(in, ps[1])
- if trimed == in {
+ trimmed = strings.TrimSuffix(in, ps[1])
+ if trimmed == in {
return str
}
}
@@ -353,7 +347,7 @@
if len(rs) != 2 {
return repl
}
- return rs[0] + trimed + rs[1]
+ return rs[0] + trimmed + rs[1]
}
// copied from build/kati/strutil.go
@@ -407,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 {
@@ -424,13 +435,15 @@
return ret
}
+// CheckDuplicate checks if there are duplicates in given string list.
+// If there are, it returns first such duplicate and true.
func CheckDuplicate(values []string) (duplicate string, found bool) {
seen := make(map[string]string)
for _, v := range values {
if duplicate, found = seen[v]; found {
- return
+ return duplicate, true
}
seen[v] = v
}
- return
+ return "", false
}
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/android/variable_test.go b/android/variable_test.go
index 9cafedd..393fe01 100644
--- a/android/variable_test.go
+++ b/android/variable_test.go
@@ -157,23 +157,6 @@
}
func TestProductVariables(t *testing.T) {
- ctx := NewTestContext()
- // A module type that has a srcs property but not a cflags property.
- ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
- Srcs []string
- }{}))
- // A module type that has a cflags property but not a srcs property.
- ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
- Cflags []string
- }{}))
- // A module type that does not have any properties that match product_variables.
- ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
- Foo []string
- }{}))
- ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
- ctx.BottomUp("variable", VariableMutator).Parallel()
- })
-
// Test that a module can use one product variable even if it doesn't have all the properties
// supported by that product variable.
bp := `
@@ -201,7 +184,24 @@
config := TestConfig(buildDir, nil, bp, nil)
config.TestProductVariables.Eng = proptools.BoolPtr(true)
- ctx.Register(config)
+ ctx := NewTestContext(config)
+ // A module type that has a srcs property but not a cflags property.
+ ctx.RegisterModuleType("module1", testProductVariableModuleFactoryFactory(&struct {
+ Srcs []string
+ }{}))
+ // A module type that has a cflags property but not a srcs property.
+ ctx.RegisterModuleType("module2", testProductVariableModuleFactoryFactory(&struct {
+ Cflags []string
+ }{}))
+ // A module type that does not have any properties that match product_variables.
+ ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
+ Foo []string
+ }{}))
+ ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
+ ctx.BottomUp("variable", VariableMutator).Parallel()
+ })
+
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
@@ -293,7 +293,7 @@
config := TestConfig(buildDir, nil, bp, nil)
config.TestProductVariables.Eng = boolPtr(true)
- ctx := NewTestContext()
+ ctx := NewTestContext(config)
ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
ctx.RegisterModuleType("defaults", productVariablesDefaultsTestDefaultsFactory)
@@ -303,7 +303,7 @@
ctx.BottomUp("variable", VariableMutator).Parallel()
})
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
FailIfErrored(t, errs)
diff --git a/android/visibility_test.go b/android/visibility_test.go
index cb5ef35..87a295e 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1168,7 +1168,7 @@
// Create a new config per test as visibility information is stored in the config.
config := TestArchConfig(buildDir, nil, "", fs)
- ctx := NewTestArchContext()
+ ctx := NewTestArchContext(config)
ctx.RegisterModuleType("mock_library", newMockLibraryModule)
ctx.RegisterModuleType("mock_parent", newMockParentFactory)
ctx.RegisterModuleType("mock_defaults", defaultsFactory)
@@ -1180,7 +1180,7 @@
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles(".")
if len(errs) > 0 {
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 739a965..4540a1f 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -214,6 +214,8 @@
"LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
"LOCAL_JETIFIER_ENABLED": "jetifier",
+
+ "LOCAL_IS_UNIT_TEST": "unit_test",
})
}
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 03cf74d..f4e5fa0 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -137,7 +137,14 @@
switch x := node.(type) {
case *mkparser.Comment:
- file.insertComment("//" + x.Comment)
+ // Split the comment on escaped newlines and then
+ // add each chunk separately.
+ chunks := strings.Split(x.Comment, "\\\n")
+ file.insertComment("//" + chunks[0])
+ for i := 1; i < len(chunks); i++ {
+ file.bpPos.Line++
+ file.insertComment("//" + chunks[i])
+ }
case *mkparser.Assignment:
handleAssignment(file, x, assignmentCond)
case *mkparser.Directive:
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 16cb138..f32ff2a 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1260,10 +1260,10 @@
desc: "comment with ESC",
in: `
# Comment line 1 \
-# Comment line 2
+ Comment line 2
`,
expected: `
-// Comment line 1 \
+// Comment line 1
// Comment line 2
`,
},
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 86dabf9..5afef65 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -212,8 +212,21 @@
expression := SimpleMakeString("", pos)
switch d {
- case "endif", "endef", "else":
+ case "endif", "endef":
// Nothing
+ case "else":
+ p.ignoreSpaces()
+ if p.tok != '\n' {
+ d = p.scanner.TokenText()
+ p.accept(scanner.Ident)
+ if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
+ d = "el" + d
+ p.ignoreSpaces()
+ expression = p.parseExpression()
+ } else {
+ p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
+ }
+ }
case "define":
expression, endPos = p.parseDefine()
default:
@@ -484,12 +497,6 @@
switch p.tok {
case '\\':
p.parseEscape()
- if p.tok == '\n' {
- // Special case: '\' does not "escape" newline in comment (b/127521510)
- comment += "\\"
- p.accept(p.tok)
- break loop
- }
comment += "\\" + p.scanner.TokenText()
p.accept(p.tok)
case '\n':
@@ -546,12 +553,14 @@
"else",
"endef",
"endif",
+ "export",
"ifdef",
"ifeq",
"ifndef",
"ifneq",
"include",
"-include",
+ "unexport",
}
var functions = [...]string{
diff --git a/apex/Android.bp b/apex/Android.bp
index 1a5f683..9e8c30d 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -9,6 +9,7 @@
"soong-cc",
"soong-java",
"soong-python",
+ "soong-rust",
"soong-sh",
],
srcs: [
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 8c70a56..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)
@@ -172,6 +173,7 @@
framework-sdkextensions(minSdkVersion:30)
framework-sdkextensions(minSdkVersion:current)
framework-statsd(minSdkVersion:current)
+framework-tethering(minSdkVersion:30)
framework-tethering(minSdkVersion:current)
gemmlowp_headers(minSdkVersion:(no version))
GoogleCellBroadcastApp(minSdkVersion:29)
@@ -183,6 +185,7 @@
i18n.module.public.api.stubs(minSdkVersion:(no version))
iconloader(minSdkVersion:21)
ike-internals(minSdkVersion:current)
+InProcessTethering(minSdkVersion:30)
InProcessTethering(minSdkVersion:current)
ipmemorystore-aidl-interfaces-java(minSdkVersion:29)
ipmemorystore-aidl-interfaces-unstable-java(minSdkVersion:29)
@@ -206,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)
@@ -220,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)
@@ -235,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)
@@ -273,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))
@@ -292,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)
@@ -301,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)
@@ -310,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))
@@ -331,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)
@@ -342,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)
@@ -379,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))
@@ -388,8 +417,12 @@
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))
libtextclassifier-java(minSdkVersion:current)
@@ -399,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)
@@ -415,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))
@@ -443,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))
@@ -540,7 +578,9 @@
statsd-aidl-ndk_platform(minSdkVersion:(no version))
statsprotos(minSdkVersion:29)
tensorflow_headers(minSdkVersion:(no version))
+Tethering(minSdkVersion:30)
Tethering(minSdkVersion:current)
+TetheringApiCurrentLib(minSdkVersion:30)
TetheringApiCurrentLib(minSdkVersion:current)
TetheringGoogle(minSdkVersion:current)
textclassifier-statsd(minSdkVersion:current)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index ee8b2b3..e7f8b7f 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -36,6 +36,33 @@
return a.androidMkForType()
}
+// nameInMake converts apexFileClass into the corresponding class name in Make.
+func (class apexFileClass) nameInMake() string {
+ switch class {
+ case etc:
+ return "ETC"
+ case nativeSharedLib:
+ return "SHARED_LIBRARIES"
+ case nativeExecutable, shBinary, pyBinary, goBinary:
+ return "EXECUTABLES"
+ case javaSharedLib:
+ return "JAVA_LIBRARIES"
+ case nativeTest:
+ return "NATIVE_TESTS"
+ case app, appSet:
+ // b/142537672 Why isn't this APP? We want to have full control over
+ // the paths and file names of the apk file under the flattend APEX.
+ // If this is set to APP, then the paths and file names are modified
+ // by the Make build system. For example, it is installed to
+ // /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
+ // /system/apex/<apexname>/app/<Appname> because the build system automatically
+ // appends module name (which is <apexname>.<Appname> to the path.
+ return "ETC"
+ default:
+ panic(fmt.Errorf("unknown class %d", class))
+ }
+}
+
func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, apexName, moduleDir string,
apexAndroidMkData android.AndroidMkData) []string {
@@ -68,10 +95,10 @@
var postInstallCommands []string
for _, fi := range a.filesInfo {
- if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+ if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- linkTarget := filepath.Join("/system", fi.Path())
- linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.Path())
+ linkTarget := filepath.Join("/system", fi.path())
+ linkPath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.path())
mkdirCmd := "mkdir -p " + filepath.Dir(linkPath)
linkCmd := "ln -sfn " + linkTarget + " " + linkPath
postInstallCommands = append(postInstallCommands, mkdirCmd, linkCmd)
@@ -85,7 +112,7 @@
continue
}
- linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform()
+ linkToSystemLib := a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform()
var moduleName string
if linkToSystemLib {
@@ -151,7 +178,7 @@
fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true")
}
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
- fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
+ fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
if fi.module != nil {
archStr := fi.module.Target().Arch.ArchType.String()
host := false
@@ -189,7 +216,7 @@
// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.jar.jar
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".jar"))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".jar"))
if javaModule, ok := fi.module.(java.ApexDependency); ok {
fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
@@ -205,7 +232,7 @@
// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.apk.apk
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.stem(), ".apk"))
if app, ok := fi.module.(*java.AndroidApp); ok {
if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
@@ -224,7 +251,7 @@
fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
case nativeSharedLib, nativeExecutable, nativeTest:
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
if ccMod, ok := fi.module.(*cc.Module); ok {
if ccMod.UnstrippedOutputFile() != nil {
fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", ccMod.UnstrippedOutputFile().String())
@@ -236,7 +263,7 @@
}
fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
default:
- fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.stem())
if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
if a.primaryApexType {
// To install companion files (init_rc, vintf_fragments)
@@ -333,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
@@ -391,6 +422,18 @@
fmt.Fprintf(w, "$(call dist-for-goals,%s,%s:%s)\n",
goal, a.installedFilesFile.String(), distFile)
}
+ 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 fdc105e..88d93af 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// package apex implements build rules for creating the APEX files which are container for
+// lower-level system components. See https://source.android.com/devices/tech/ota/apex
package apex
import (
@@ -30,14 +32,1004 @@
prebuilt_etc "android/soong/etc"
"android/soong/java"
"android/soong/python"
+ "android/soong/rust"
"android/soong/sh"
)
+func init() {
+ android.RegisterModuleType("apex", BundleFactory)
+ android.RegisterModuleType("apex_test", testApexBundleFactory)
+ android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
+ android.RegisterModuleType("apex_defaults", defaultsFactory)
+ android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
+ android.RegisterModuleType("override_apex", overrideApexFactory)
+ android.RegisterModuleType("apex_set", apexSetFactory)
+
+ android.PreDepsMutators(RegisterPreDepsMutators)
+ android.PostDepsMutators(RegisterPostDepsMutators)
+}
+
+func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
+ ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
+}
+
+func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
+ 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()
+ ctx.BottomUp("apex", apexMutator).Parallel()
+ ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
+ ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
+ ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
+}
+
+type apexBundleProperties struct {
+ // Json manifest file describing meta info of this APEX bundle. Refer to
+ // system/apex/proto/apex_manifest.proto for the schema. Default: "apex_manifest.json"
+ Manifest *string `android:"path"`
+
+ // AndroidManifest.xml file used for the zip container of this APEX bundle. If unspecified,
+ // a default one is automatically generated.
+ AndroidManifest *string `android:"path"`
+
+ // Canonical name of this APEX bundle. Used to determine the path to the activated APEX on
+ // device (/apex/<apex_name>). If unspecified, follows the name property.
+ Apex_name *string
+
+ // Determines the file contexts file for setting the security contexts to files in this APEX
+ // bundle. For platform APEXes, this should points to a file under /system/sepolicy Default:
+ // /system/sepolicy/apex/<module_name>_file_contexts.
+ File_contexts *string `android:"path"`
+
+ ApexNativeDependencies
+
+ Multilib apexMultilibProperties
+
+ // List of java libraries that are embedded inside this APEX bundle.
+ Java_libs []string
+
+ // List of prebuilt files that are embedded inside this APEX bundle.
+ Prebuilts []string
+
+ // List of BPF programs inside this APEX bundle.
+ Bpfs []string
+
+ // Name of the apex_key module that provides the private key to sign this APEX bundle.
+ Key *string
+
+ // Specifies the certificate and the private key to sign the zip container of this APEX. If
+ // this is "foo", foo.x509.pem and foo.pk8 under PRODUCT_DEFAULT_DEV_CERTIFICATE are used
+ // as the certificate and the private key, respectively. If this is ":module", then the
+ // certificate and the private key are provided from the android_app_certificate module
+ // named "module".
+ Certificate *string
+
+ // The minimum SDK version that this APEX must support at minimum. This is usually set to
+ // the SDK version that the APEX was first introduced.
+ Min_sdk_version *string
+
+ // Whether this APEX is considered updatable or not. When set to true, this will enforce
+ // additional rules for making sure that the APEX is truly updatable. To be updatable,
+ // min_sdk_version should be set as well. This will also disable the size optimizations like
+ // symlinking to the system libs. Default is false.
+ Updatable *bool
+
+ // Whether this APEX is installable to one of the partitions like system, vendor, etc.
+ // 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.
+ Use_vendor *bool
+
+ // If set true, VNDK libs are considered as stable libs and are not included in this APEX.
+ // Should be only used in non-system apexes (e.g. vendor: true). Default is false.
+ Use_vndk_as_stable *bool
+
+ // List of SDKs that are used to build this APEX. A reference to an SDK should be either
+ // `name#version` or `name` which is an alias for `name#current`. If left empty,
+ // `platform#current` is implied. This value affects all modules included in this APEX. In
+ // other words, they are also built with the SDKs specified here.
+ Uses_sdks []string
+
+ // The type of APEX to build. Controls what the APEX payload is. Either 'image', 'zip' or
+ // 'both'. When set to image, contents are stored in a filesystem image inside a zip
+ // container. When set to zip, contents are stored in a zip container directly. This type is
+ // mostly for host-side debugging. When set to both, the two types are both built. Default
+ // is 'image'.
+ Payload_type *string
+
+ // The type of filesystem to use when the payload_type is 'image'. Either 'ext4' or 'f2fs'.
+ // Default 'ext4'.
+ Payload_fs_type *string
+
+ // For telling the APEX to ignore special handling for system libraries such as bionic.
+ // Default is false.
+ Ignore_system_library_special_case *bool
+
+ // Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only
+ // used in tests.
+ Test_only_no_hashtree *bool
+
+ // Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
+ // used in tests.
+ Test_only_unsigned_payload *bool
+
+ IsCoverageVariant bool `blueprint:"mutated"`
+
+ // List of sanitizer names that this APEX is enabled for
+ SanitizerNames []string `blueprint:"mutated"`
+
+ PreventInstall bool `blueprint:"mutated"`
+
+ HideFromMake bool `blueprint:"mutated"`
+
+ // Internal package method for this APEX. When payload_type is image, this can be either
+ // imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip,
+ // this becomes zipApex.
+ ApexType apexPackaging `blueprint:"mutated"`
+}
+
+type ApexNativeDependencies struct {
+ // List of native libraries that are embedded inside this APEX.
+ Native_shared_libs []string
+
+ // List of JNI libraries that are embedded inside this APEX.
+ Jni_libs []string
+
+ // List of rust dyn libraries
+ Rust_dyn_libs []string
+
+ // List of native executables that are embedded inside this APEX.
+ Binaries []string
+
+ // List of native tests that are embedded inside this APEX.
+ Tests []string
+}
+
+type apexMultilibProperties struct {
+ // Native dependencies whose compile_multilib is "first"
+ First ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "both"
+ Both ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "prefer32"
+ Prefer32 ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "32"
+ Lib32 ApexNativeDependencies
+
+ // Native dependencies whose compile_multilib is "64"
+ Lib64 ApexNativeDependencies
+}
+
+type apexTargetBundleProperties struct {
+ Target struct {
+ // Multilib properties only for android.
+ Android struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host.
+ Host struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host linux_bionic.
+ Linux_bionic struct {
+ Multilib apexMultilibProperties
+ }
+
+ // Multilib properties only for host linux_glibc.
+ Linux_glibc struct {
+ Multilib apexMultilibProperties
+ }
+ }
+}
+
+type apexArchBundleProperties struct {
+ Arch struct {
+ Arm struct {
+ ApexNativeDependencies
+ }
+ Arm64 struct {
+ ApexNativeDependencies
+ }
+ X86 struct {
+ ApexNativeDependencies
+ }
+ X86_64 struct {
+ ApexNativeDependencies
+ }
+ }
+}
+
+// These properties can be used in override_apex to override the corresponding properties in the
+// base apex.
+type overridableProperties struct {
+ // List of APKs that are embedded inside this APEX.
+ Apps []string
+
+ // List of runtime resource overlays (RROs) that are embedded inside this APEX.
+ Rros []string
+
+ // Names of modules to be overridden. Listed modules can only be other binaries (in Make or
+ // Soong). This does not completely prevent installation of the overridden binaries, but if
+ // both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
+ // be removed from PRODUCT_PACKAGES.
+ Overrides []string
+
+ // Logging parent value.
+ Logging_parent string
+
+ // Apex Container package name. Override value for attribute package:name in
+ // AndroidManifest.xml
+ Package_name string
+
+ // A txt file containing list of files that are allowed to be included in this APEX.
+ Allowed_files *string `android:"path"`
+}
+
+type apexBundle struct {
+ // Inherited structs
+ android.ModuleBase
+ android.DefaultableModuleBase
+ android.OverridableModuleBase
+ android.SdkBase
+
+ // Properties
+ properties apexBundleProperties
+ targetProperties apexTargetBundleProperties
+ archProperties apexArchBundleProperties
+ overridableProperties overridableProperties
+ vndkProperties apexVndkProperties // only for apex_vndk modules
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Inputs
+
+ // Keys for apex_paylaod.img
+ public_key_file android.Path
+ private_key_file android.Path
+
+ // Cert/priv-key for the zip container
+ container_certificate_file android.Path
+ container_private_key_file android.Path
+
+ // Flags for special variants of APEX
+ testApex bool
+ vndkApex bool
+ artApex bool
+
+ // Tells whether this variant of the APEX bundle is the primary one or not. Only the primary
+ // one gets installed to the device.
+ primaryApexType bool
+
+ // Suffix of module name in Android.mk ".flattened", ".apex", ".zipapex", or ""
+ suffix string
+
+ // File system type of apex_payload.img
+ payloadFsType fsType
+
+ // Whether to create symlink to the system file instead of having a file inside the apex or
+ // not
+ linkToSystemLib bool
+
+ // List of files to be included in this APEX. This is filled in the first part of
+ // GenerateAndroidBuildActions.
+ filesInfo []apexFile
+
+ // List of other module names that should be installed when this APEX gets installed.
+ requiredDeps []string
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Outputs (final and intermediates)
+
+ // Processed apex manifest in JSONson format (for Q)
+ manifestJsonOut android.WritablePath
+
+ // Processed apex manifest in PB format (for R+)
+ manifestPbOut android.WritablePath
+
+ // Processed file_contexts files
+ fileContexts android.WritablePath
+
+ // Struct holding the merged notice file paths in different formats
+ mergedNotices android.NoticeOutputs
+
+ // The built APEX file. This is the main product.
+ outputFile android.WritablePath
+
+ // The built APEX file in app bundle format. This file is not directly installed to the
+ // device. For an APEX, multiple app bundles are created each of which is for a specific ABI
+ // like arm, arm64, x86, etc. Then they are processed again (outside of the Android build
+ // system) to be merged into a single app bundle file that Play accepts. See
+ // vendor/google/build/build_unbundled_mainline_module.sh for more detail.
+ bundleModuleFile android.WritablePath
+
+ // Target path to install this APEX. Usually out/target/product/<device>/<partition>/apex.
+ installDir android.InstallPath
+
+ // List of commands to create symlinks for backward compatibility. These commands will be
+ // attached as LOCAL_POST_INSTALL_CMD to apex package itself (for unflattened build) or
+ // apex_manifest (for flattened build) so that compat symlinks are always installed
+ // regardless of TARGET_FLATTEN_APEX setting.
+ compatSymlinks []string
+
+ // Text file having the list of individual files that are included in this APEX. Used for
+ // debugging purpose.
+ installedFilesFile android.WritablePath
+
+ // List of module names that this APEX is including (to be shown via *-deps-info target).
+ // Used for debugging purpose.
+ android.ApexBundleDepsInfo
+
+ // Optional list of lint report zip files for apexes that contain java or app modules
+ 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.
+type apexFileClass int
+
const (
+ app apexFileClass = iota
+ appSet
+ etc
+ goBinary
+ javaSharedLib
+ nativeExecutable
+ nativeSharedLib
+ nativeTest
+ pyBinary
+ shBinary
+)
+
+// apexFile represents a file in an APEX bundle. This is created during the first half of
+// GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half
+// of the function, this is used to create commands that copies the files into a staging directory,
+// where they are packaged into the APEX file. This struct is also used for creating Make modules
+// for each of the files in case when the APEX is flattened.
+type apexFile struct {
+ // buildFile is put in the installDir inside the APEX.
+ builtFile android.Path
+ noticeFiles android.Paths
+ installDir string
+ customStem string
+ symlinks []string // additional symlinks
+
+ // Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
+ // module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
+ // suffix>]
+ androidMkModuleName string // becomes LOCAL_MODULE
+ class apexFileClass // becomes LOCAL_MODULE_CLASS
+ moduleDir string // becomes LOCAL_PATH
+ requiredModuleNames []string // becomes LOCAL_REQUIRED_MODULES
+ targetRequiredModuleNames []string // becomes LOCAL_TARGET_REQUIRED_MODULES
+ hostRequiredModuleNames []string // becomes LOCAL_HOST_REQUIRED_MODULES
+ dataPaths []android.DataPath // becomes LOCAL_TEST_DATA
+
+ jacocoReportClassesFile android.Path // only for javalibs and apps
+ lintDepSets java.LintDepSets // only for javalibs and apps
+ certificate java.Certificate // only for apps
+ overriddenPackageName string // only for apps
+
+ transitiveDep bool
+ isJniLib bool
+
+ // TODO(jiyong): remove this
+ module android.Module
+}
+
+// TODO(jiyong): shorten the arglist using an option struct
+func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
+ ret := apexFile{
+ builtFile: builtFile,
+ installDir: installDir,
+ androidMkModuleName: androidMkModuleName,
+ class: class,
+ module: module,
+ }
+ if module != nil {
+ ret.noticeFiles = module.NoticeFiles()
+ ret.moduleDir = ctx.OtherModuleDir(module)
+ ret.requiredModuleNames = module.RequiredModuleNames()
+ ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
+ ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
+ }
+ return ret
+}
+
+func (af *apexFile) ok() bool {
+ return af.builtFile != nil && af.builtFile.String() != ""
+}
+
+// apexRelativePath returns the relative path of the given path from the install directory of this
+// apexFile.
+// TODO(jiyong): rename this
+func (af *apexFile) apexRelativePath(path string) string {
+ return filepath.Join(af.installDir, path)
+}
+
+// path returns path of this apex file relative to the APEX root
+func (af *apexFile) path() string {
+ return af.apexRelativePath(af.stem())
+}
+
+// stem returns the base filename of this apex file
+func (af *apexFile) stem() string {
+ if af.customStem != "" {
+ return af.customStem
+ }
+ return af.builtFile.Base()
+}
+
+// symlinkPaths returns paths of the symlinks (if any) relative to the APEX root
+func (af *apexFile) symlinkPaths() []string {
+ var ret []string
+ for _, symlink := range af.symlinks {
+ ret = append(ret, af.apexRelativePath(symlink))
+ }
+ return ret
+}
+
+// availableToPlatform tests whether this apexFile is from a module that can be installed to the
+// platform.
+func (af *apexFile) availableToPlatform() bool {
+ if af.module == nil {
+ return false
+ }
+ if am, ok := af.module.(android.ApexModule); ok {
+ return am.AvailableFor(android.AvailableToPlatform)
+ }
+ return false
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Mutators
+//
+// Brief description about mutators for APEX. The following three mutators are the most important
+// ones.
+//
+// 1) DepsMutator: from the properties like native_shared_libs, java_libs, etc., modules are added
+// to the (direct) dependencies of this APEX bundle.
+//
+// 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 apexInfoMutator. For each module that
+// are marked by the apexInfoMutator, apex variations are created using CreateApexVariations().
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+
+ // Determines if the dependent will be part of the APEX payload. Can be false for the
+ // dependencies to the signing key module, etc.
+ payload bool
+}
+
+var (
+ androidAppTag = dependencyTag{name: "androidApp", payload: true}
+ bpfTag = dependencyTag{name: "bpf", payload: true}
+ certificateTag = dependencyTag{name: "certificate"}
+ executableTag = dependencyTag{name: "executable", payload: true}
+ javaLibTag = dependencyTag{name: "javaLib", payload: true}
+ jniLibTag = dependencyTag{name: "jniLib", payload: true}
+ keyTag = dependencyTag{name: "key"}
+ prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
+ rroTag = dependencyTag{name: "rro", payload: true}
+ sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
+ testForTag = dependencyTag{name: "test for"}
+ testTag = dependencyTag{name: "test", payload: true}
+)
+
+// TODO(jiyong): shorten this function signature
+func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
+ binVariations := target.Variations()
+ libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
+ rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"})
+
+ if ctx.Device() {
+ binVariations = append(binVariations, 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
+ // this module. This is required since arch variant of an APEX bundle is 'common' but it is
+ // 'arm' or 'arm64' for native shared libs.
+ ctx.AddFarVariationDependencies(binVariations, executableTag, nativeModules.Binaries...)
+ ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
+ ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
+ ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
+ ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag, nativeModules.Rust_dyn_libs...)
+}
+
+func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
+ if ctx.Device() {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil)
+ if ctx.Os().Bionic() {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil)
+ } else {
+ proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil)
+ }
+ }
+}
+
+// getImageVariation returns the image variant name for this apexBundle. In most cases, it's simply
+// android.CoreVariation, but gets complicated for the vendor APEXes and the VNDK APEX.
+func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
+ deviceConfig := ctx.DeviceConfig()
+ if a.vndkApex {
+ return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
+ }
+
+ var prefix string
+ var vndkVersion string
+ if deviceConfig.VndkVersion() != "" {
+ if proptools.Bool(a.properties.Use_vendor) {
+ prefix = cc.VendorVariationPrefix
+ vndkVersion = deviceConfig.PlatformVndkVersion()
+ } else if a.SocSpecific() || a.DeviceSpecific() {
+ prefix = cc.VendorVariationPrefix
+ vndkVersion = deviceConfig.VndkVersion()
+ } else if a.ProductSpecific() {
+ prefix = cc.ProductVariationPrefix
+ vndkVersion = deviceConfig.ProductVndkVersion()
+ }
+ }
+ if vndkVersion == "current" {
+ vndkVersion = deviceConfig.PlatformVndkVersion()
+ }
+ if vndkVersion != "" {
+ return prefix + vndkVersion
+ }
+
+ return android.CoreVariation // The usual case
+}
+
+func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // TODO(jiyong): move this kind of checks to GenerateAndroidBuildActions?
+ checkUseVendorProperty(ctx, a)
+
+ // apexBundle is a multi-arch targets module. Arch variant of apexBundle is set to 'common'.
+ // arch-specific targets are enabled by the compile_multilib setting of the apex bundle. For
+ // each target os/architectures, appropriate dependencies are selected by their
+ // target.<os>.multilib.<type> groups and are added as (direct) dependencies.
+ targets := ctx.MultiTargets()
+ config := ctx.DeviceConfig()
+ imageVariation := a.getImageVariation(ctx)
+
+ a.combineProperties(ctx)
+
+ has32BitTarget := false
+ for _, target := range targets {
+ if target.Arch.ArchType.Multilib == "lib32" {
+ has32BitTarget = true
+ }
+ }
+ for i, target := range targets {
+ // Don't include artifacts for the host cross targets because there is no way for us
+ // to run those artifacts natively on host
+ if target.HostCross {
+ continue
+ }
+
+ var depsList []ApexNativeDependencies
+
+ // Add native modules targeting both ABIs. When multilib.* is omitted for
+ // native_shared_libs/jni_libs/tests, it implies multilib.both
+ depsList = append(depsList, a.properties.Multilib.Both)
+ depsList = append(depsList, ApexNativeDependencies{
+ Native_shared_libs: a.properties.Native_shared_libs,
+ Tests: a.properties.Tests,
+ Jni_libs: a.properties.Jni_libs,
+ Binaries: nil,
+ })
+
+ // Add native modules targeting the first ABI When multilib.* is omitted for
+ // binaries, it implies multilib.first
+ isPrimaryAbi := i == 0
+ if isPrimaryAbi {
+ depsList = append(depsList, a.properties.Multilib.First)
+ depsList = append(depsList, ApexNativeDependencies{
+ Native_shared_libs: nil,
+ Tests: nil,
+ Jni_libs: nil,
+ Binaries: a.properties.Binaries,
+ })
+ }
+
+ // Add native modules targeting either 32-bit or 64-bit ABI
+ switch target.Arch.ArchType.Multilib {
+ case "lib32":
+ depsList = append(depsList, a.properties.Multilib.Lib32)
+ depsList = append(depsList, a.properties.Multilib.Prefer32)
+ case "lib64":
+ depsList = append(depsList, a.properties.Multilib.Lib64)
+ if !has32BitTarget {
+ depsList = append(depsList, a.properties.Multilib.Prefer32)
+ }
+ }
+
+ // Add native modules targeting a specific arch variant
+ switch target.Arch.ArchType {
+ case android.Arm:
+ depsList = append(depsList, a.archProperties.Arch.Arm.ApexNativeDependencies)
+ case android.Arm64:
+ depsList = append(depsList, a.archProperties.Arch.Arm64.ApexNativeDependencies)
+ case android.X86:
+ depsList = append(depsList, a.archProperties.Arch.X86.ApexNativeDependencies)
+ case android.X86_64:
+ depsList = append(depsList, a.archProperties.Arch.X86_64.ApexNativeDependencies)
+ default:
+ panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType))
+ }
+
+ for _, d := range depsList {
+ addDependenciesForNativeModules(ctx, d, target, imageVariation)
+ }
+ }
+
+ // For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
+ // regardless of the TARGET_PREFER_* setting. See b/144532908
+ archForPrebuiltEtc := config.Arches()[0]
+ for _, arch := range config.Arches() {
+ // Prefer 64-bit arch if there is any
+ if arch.ArchType.Multilib == "lib64" {
+ archForPrebuiltEtc = arch
+ break
+ }
+ }
+ ctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "os", Variation: ctx.Os().String()},
+ {Mutator: "arch", Variation: archForPrebuiltEtc.String()},
+ }, prebuiltTag, a.properties.Prebuilts...)
+
+ // Common-arch dependencies come next
+ commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
+ ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
+
+ // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
+ if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+ ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
+ }
+
+ // Dependencies for signing
+ if String(a.properties.Key) == "" {
+ ctx.PropertyErrorf("key", "missing")
+ return
+ }
+ ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
+
+ cert := android.SrcIsModule(a.getCertString(ctx))
+ if cert != "" {
+ ctx.AddDependency(ctx.Module(), certificateTag, cert)
+ // empty cert is not an error. Cert and private keys will be directly found under
+ // PRODUCT_DEFAULT_DEV_CERTIFICATE
+ }
+
+ // Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
+ // This field currently isn't used.
+ // TODO(jiyong): consider dropping this feature
+ // TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
+ if len(a.properties.Uses_sdks) > 0 {
+ sdkRefs := []android.SdkRef{}
+ for _, str := range a.properties.Uses_sdks {
+ parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
+ sdkRefs = append(sdkRefs, parsed)
+ }
+ a.BuildWithSdks(sdkRefs)
+ }
+}
+
+// DepsMutator for the overridden properties.
+func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+ if a.overridableProperties.Allowed_files != nil {
+ android.ExtractSourceDeps(ctx, a.overridableProperties.Allowed_files)
+ }
+
+ commonVariation := ctx.Config().AndroidCommonTarget.Variations()
+ ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
+ ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+}
+
+type ApexBundleInfo struct {
+ Contents *android.ApexContents
+}
+
+var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_info")
+
+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.
+//
+// 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
+ // libraries are self-identified by their vndk.enabled properties. There is no need to run
+ // this mutator for the APEX as nothing will be collected. So, let's return fast.
+ if a.vndkApex {
+ return
+ }
+
+ // Special casing for APEXes on non-system (e.g., vendor, odm, etc.) partitions. They are
+ // provided with a property named use_vndk_as_stable, which when set to true doesn't collect
+ // VNDK libraries as transitive dependencies. This option is useful for reducing the size of
+ // the non-system APEXes because the VNDK libraries won't be included (and duped) in the
+ // APEX, but shared across APEXes via the VNDK APEX.
+ useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
+ excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
+ if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
+ mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
+ return
+ }
+
+ continueApexDepsWalk := func(child, parent android.Module) bool {
+ am, ok := child.(android.ApexModule)
+ if !ok || !am.CanHaveApexVariants() {
+ return false
+ }
+ if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
+ return false
+ }
+ if excludeVndkLibs {
+ if c, ok := child.(*cc.Module); ok && c.IsVndk() {
+ return false
+ }
+ }
+ // By default, all the transitive dependencies are collected, unless filtered out
+ // above.
+ return true
+ }
+
+ // Records whether a certain module is included in this apexBundle via direct dependency or
+ // inndirect dependency.
+ contents := make(map[string]android.ApexMembership)
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !continueApexDepsWalk(child, parent) {
+ return false
+ }
+ // If the parent is apexBundle, this child is directly depended.
+ _, directDep := parent.(*apexBundle)
+ depName := mctx.OtherModuleName(child)
+ contents[depName] = contents[depName].Add(directDep)
+ return true
+ })
+
+ // The membership information is saved for later access
+ apexContents := android.NewApexContents(contents)
+ mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
+ Contents: apexContents,
+ })
+
+ // This is the main part of this mutator. Mark the collected dependencies that they need to
+ // be built for this apexBundle.
+ apexInfo := android.ApexInfo{
+ ApexVariationName: mctx.ModuleName(),
+ MinSdkVersionStr: a.minSdkVersion(mctx).String(),
+ RequiredSdks: a.RequiredSdks(),
+ Updatable: a.Updatable(),
+ InApexes: []string{mctx.ModuleName()},
+ ApexContents: []*android.ApexContents{apexContents},
+ }
+ mctx.WalkDeps(func(child, parent android.Module) bool {
+ if !continueApexDepsWalk(child, parent) {
+ return false
+ }
+ child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
+ return true
+ })
+}
+
+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?
+func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ android.UpdateUniqueApexVariationsForDeps(mctx, am)
+ }
+}
+
+// apexTestForDepsMutator checks if this module is a test for an apex. If so, add a dependency on
+// the apex in order to retrieve its contents later.
+// TODO(jiyong): move this to android/apex.go?
+func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ if testFor := am.TestFor(); len(testFor) > 0 {
+ mctx.AddFarVariationDependencies([]blueprint.Variation{
+ {Mutator: "os", Variation: am.Target().OsVariation()},
+ {"arch", "common"},
+ }, testForTag, testFor...)
+ }
+ }
+}
+
+// TODO(jiyong): move this to android/apex.go?
+func apexTestForMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if _, ok := mctx.Module().(android.ApexModule); ok {
+ var contents []*android.ApexContents
+ for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
+ abInfo := mctx.OtherModuleProvider(testFor, ApexBundleInfoProvider).(ApexBundleInfo)
+ contents = append(contents, abInfo.Contents)
+ }
+ mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
+ ApexContents: contents,
+ })
+ }
+}
+
+// markPlatformAvailability marks whether or not a module can be available to platform. A module
+// cannot be available to platform if 1) it is explicitly marked as not available (i.e.
+// "//apex_available:platform" is absent) or 2) it depends on another module that isn't (or can't
+// be) available to platform
+// TODO(jiyong): move this to android/apex.go?
+func markPlatformAvailability(mctx android.BottomUpMutatorContext) {
+ // Host and recovery are not considered as platform
+ if mctx.Host() || mctx.Module().InstallInRecovery() {
+ return
+ }
+
+ am, ok := mctx.Module().(android.ApexModule)
+ if !ok {
+ return
+ }
+
+ availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
+
+ // If any of the dep is not available to platform, this module is also considered as being
+ // not available to platform even if it has "//apex_available:platform"
+ mctx.VisitDirectDeps(func(child android.Module) {
+ if !am.DepIsInSameApex(mctx, child) {
+ // if the dependency crosses apex boundary, don't consider it
+ return
+ }
+ if dep, ok := child.(android.ApexModule); ok && dep.NotAvailableForPlatform() {
+ availableToPlatform = false
+ // TODO(b/154889534) trigger an error when 'am' has
+ // "//apex_available:platform"
+ }
+ })
+
+ // Exception 1: stub libraries and native bridge libraries are always available to platform
+ if cc, ok := mctx.Module().(*cc.Module); ok &&
+ (cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) {
+ availableToPlatform = true
+ }
+
+ // Exception 2: bootstrap bionic libraries are also always available to platform
+ if cc.InstallToBootstrap(mctx.ModuleName(), mctx.Config()) {
+ availableToPlatform = true
+ }
+
+ if !availableToPlatform {
+ am.SetNotAvailableForPlatform()
+ }
+}
+
+// apexMutator visits each module and creates apex variations if the module was marked in the
+// previous run of apexInfoMutator.
+func apexMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+
+ // This is the usual path.
+ if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
+ android.CreateApexVariations(mctx, am)
+ return
+ }
+
+ // apexBundle itself is mutated so that it and its dependencies have the same apex variant.
+ // TODO(jiyong): document the reason why the VNDK APEX is an exception here.
+ if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
+ apexBundleName := mctx.ModuleName()
+ mctx.CreateVariations(apexBundleName)
+ } else if o, ok := mctx.Module().(*OverrideApex); ok {
+ apexBundleName := o.GetOverriddenModuleName()
+ if apexBundleName == "" {
+ mctx.ModuleErrorf("base property is not set")
+ return
+ }
+ mctx.CreateVariations(apexBundleName)
+ }
+}
+
+// See android.UpdateDirectlyInAnyApex
+// TODO(jiyong): move this to android/apex.go?
+func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if am, ok := mctx.Module().(android.ApexModule); ok {
+ android.UpdateDirectlyInAnyApex(mctx, am)
+ }
+}
+
+// apexPackaging represents a specific packaging method for an APEX.
+type apexPackaging int
+
+const (
+ // imageApex is a packaging method where contents are included in a filesystem image which
+ // is then included in a zip container. This is the most typical way of packaging.
+ imageApex apexPackaging = iota
+
+ // zipApex is a packaging method where contents are directly included in the zip container.
+ // This is used for host-side testing - because the contents are easily accessible by
+ // unzipping the container.
+ zipApex
+
+ // flattendApex is a packaging method where contents are not included in the APEX file, but
+ // installed to /apex/<apexname> directory on the device. This packaging method is used for
+ // old devices where the filesystem-based APEX file can't be supported.
+ flattenedApex
+)
+
+const (
+ // File extensions of an APEX for different packaging methods
imageApexSuffix = ".apex"
zipApexSuffix = ".zipapex"
flattenedSuffix = ".flattened"
+ // variant names each of which is for a packaging method
imageApexType = "image"
zipApexType = "zip"
flattenedApexType = "flattened"
@@ -46,34 +1038,1200 @@
f2fsFsType = "f2fs"
)
-type dependencyTag struct {
- blueprint.BaseDependencyTag
- name string
+// The suffix for the output "file", not the module
+func (a apexPackaging) suffix() string {
+ switch a {
+ case imageApex:
+ return imageApexSuffix
+ case zipApex:
+ return zipApexSuffix
+ default:
+ panic(fmt.Errorf("unknown APEX type %d", a))
+ }
+}
- // determines if the dependent will be part of the APEX payload
- payload bool
+func (a apexPackaging) name() string {
+ switch a {
+ case imageApex:
+ return imageApexType
+ case zipApex:
+ return zipApexType
+ default:
+ panic(fmt.Errorf("unknown APEX type %d", a))
+ }
+}
+
+// apexFlattenedMutator creates one or more variations each of which is for a packaging method.
+// TODO(jiyong): give a better name to this mutator
+func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
+ if !mctx.Module().Enabled() {
+ return
+ }
+ if ab, ok := mctx.Module().(*apexBundle); ok {
+ var variants []string
+ switch proptools.StringDefault(ab.properties.Payload_type, "image") {
+ case "image":
+ // This is the normal case. Note that both image and flattend APEXes are
+ // created. The image type is installed to the system partition, while the
+ // flattened APEX is (optionally) installed to the system_ext partition.
+ // This is mostly for GSI which has to support wide range of devices. If GSI
+ // is installed on a newer (APEX-capable) device, the image APEX in the
+ // system will be used. However, if the same GSI is installed on an old
+ // device which can't support image APEX, the flattened APEX in the
+ // system_ext partion (which still is part of GSI) is used instead.
+ variants = append(variants, imageApexType, flattenedApexType)
+ case "zip":
+ variants = append(variants, zipApexType)
+ case "both":
+ variants = append(variants, imageApexType, zipApexType, flattenedApexType)
+ default:
+ mctx.PropertyErrorf("payload_type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
+ return
+ }
+
+ modules := mctx.CreateLocalVariations(variants...)
+
+ for i, v := range variants {
+ switch v {
+ case imageApexType:
+ modules[i].(*apexBundle).properties.ApexType = imageApex
+ case zipApexType:
+ modules[i].(*apexBundle).properties.ApexType = zipApex
+ case flattenedApexType:
+ modules[i].(*apexBundle).properties.ApexType = flattenedApex
+ // See the comment above for why system_ext.
+ if !mctx.Config().FlattenApex() && ab.Platform() {
+ modules[i].(*apexBundle).MakeAsSystemExt()
+ }
+ }
+ }
+ } else if _, ok := mctx.Module().(*OverrideApex); ok {
+ // payload_type is forcibly overridden to "image"
+ // TODO(jiyong): is this the right decision?
+ mctx.CreateVariations(imageApexType, flattenedApexType)
+ }
+}
+
+// checkUseVendorProperty checks if the use of `use_vendor` property is allowed for the given APEX.
+// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
+// which may cause compatibility issues. (e.g. libbinder) Even though libbinder restricts its
+// availability via 'apex_available' property and relies on yet another macro
+// __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules to avoid
+// similar problems.
+func checkUseVendorProperty(ctx android.BottomUpMutatorContext, a *apexBundle) {
+ if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorAllowList(ctx.Config())) {
+ ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
+ }
}
var (
- sharedLibTag = dependencyTag{name: "sharedLib", payload: true}
- jniLibTag = dependencyTag{name: "jniLib", payload: true}
- executableTag = dependencyTag{name: "executable", payload: true}
- javaLibTag = dependencyTag{name: "javaLib", payload: true}
- prebuiltTag = dependencyTag{name: "prebuilt", payload: true}
- testTag = dependencyTag{name: "test", payload: true}
- keyTag = dependencyTag{name: "key"}
- certificateTag = dependencyTag{name: "certificate"}
- usesTag = dependencyTag{name: "uses"}
- androidAppTag = dependencyTag{name: "androidApp", payload: true}
- rroTag = dependencyTag{name: "rro", payload: true}
- bpfTag = dependencyTag{name: "bpf", payload: true}
- testForTag = dependencyTag{name: "test for"}
+ useVendorAllowListKey = android.NewOnceKey("useVendorAllowList")
+)
- apexAvailBaseline = makeApexAvailableBaseline()
+func useVendorAllowList(config android.Config) []string {
+ return config.Once(useVendorAllowListKey, func() interface{} {
+ return []string{
+ // swcodec uses "vendor" variants for smaller size
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ }
+ }).([]string)
+}
+// setUseVendorAllowListForTest overrides useVendorAllowList and must be called before the first
+// call to useVendorAllowList()
+func setUseVendorAllowListForTest(config android.Config, allowList []string) {
+ config.Once(useVendorAllowListKey, func() interface{} {
+ return allowList
+ })
+}
+
+var _ android.DepIsInSameApex = (*apexBundle)(nil)
+
+// Implements android.DepInInSameApex
+func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ // direct deps of an APEX bundle are all part of the APEX bundle
+ // TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
+ return true
+}
+
+var _ android.OutputFileProducer = (*apexBundle)(nil)
+
+// Implements android.OutputFileProducer
+func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
+ switch tag {
+ case "", android.DefaultDistTag:
+ // This is the default dist path.
+ return android.Paths{a.outputFile}, nil
+ default:
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+ }
+}
+
+var _ cc.Coverage = (*apexBundle)(nil)
+
+// Implements cc.Coverage
+func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
+ return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) PreventInstall() {
+ a.properties.PreventInstall = true
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) HideFromMake() {
+ a.properties.HideFromMake = true
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
+ a.properties.IsCoverageVariant = coverage
+}
+
+// Implements cc.Coverage
+func (a *apexBundle) EnableCoverageIfNeeded() {}
+
+var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
+
+// Implements android.ApexBudleDepsInfoIntf
+func (a *apexBundle) Updatable() bool {
+ return proptools.Bool(a.properties.Updatable)
+}
+
+// getCertString returns the name of the cert that should be used to sign this APEX. This is
+// basically from the "certificate" property, but could be overridden by the device config.
+func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
+ moduleName := ctx.ModuleName()
+ // VNDK APEXes share the same certificate. To avoid adding a new VNDK version to the
+ // OVERRIDE_* list, we check with the pseudo module name to see if its certificate is
+ // overridden.
+ if a.vndkApex {
+ moduleName = vndkApexName
+ }
+ certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
+ if overridden {
+ return ":" + certificate
+ }
+ return String(a.properties.Certificate)
+}
+
+// See the installable property
+func (a *apexBundle) installable() bool {
+ return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
+}
+
+// See the test_only_no_hashtree property
+func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
+ return proptools.Bool(a.properties.Test_only_no_hashtree)
+}
+
+// See the test_only_unsigned_payload property
+func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
+ return proptools.Bool(a.properties.Test_only_unsigned_payload)
+}
+
+// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
+// members) can be sanitized, either forcibly, or by the global configuration. For some of the
+// sanitizers, extra dependencies can be forcibly added as well.
+
+func (a *apexBundle) EnableSanitizer(sanitizerName string) {
+ if !android.InList(sanitizerName, a.properties.SanitizerNames) {
+ a.properties.SanitizerNames = append(a.properties.SanitizerNames, sanitizerName)
+ }
+}
+
+func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
+ if android.InList(sanitizerName, a.properties.SanitizerNames) {
+ return true
+ }
+
+ // Then follow the global setting
+ globalSanitizerNames := []string{}
+ if a.Host() {
+ globalSanitizerNames = ctx.Config().SanitizeHost()
+ } else {
+ arches := ctx.Config().SanitizeDeviceArch()
+ if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
+ globalSanitizerNames = ctx.Config().SanitizeDevice()
+ }
+ }
+ return android.InList(sanitizerName, globalSanitizerNames)
+}
+
+func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
+ // TODO(jiyong): move this info (the sanitizer name, the lib name, etc.) to cc/sanitize.go
+ // Keep only the mechanism here.
+ if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
+ imageVariation := a.getImageVariation(ctx)
+ for _, target := range ctx.MultiTargets() {
+ if target.Arch.ArchType.Multilib == "lib64" {
+ addDependenciesForNativeModules(ctx, ApexNativeDependencies{
+ Native_shared_libs: []string{"libclang_rt.hwasan-aarch64-android"},
+ Tests: nil,
+ Jni_libs: nil,
+ Binaries: nil,
+ }, target, imageVariation)
+ break
+ }
+ }
+ }
+}
+
+// apexFileFor<Type> functions below create an apexFile struct for a given Soong module. The
+// returned apexFile saves information about the Soong module that will be used for creating the
+// build rules.
+func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
+ // Decide the APEX-local directory by the multilib of the library In the future, we may
+ // query this to the module.
+ // TODO(jiyong): use the new PackagingSpec
+ var dirInApex string
+ switch ccMod.Arch().ArchType.Multilib {
+ case "lib32":
+ dirInApex = "lib"
+ case "lib64":
+ dirInApex = "lib64"
+ }
+ if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
+ }
+ dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
+ if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
+ // Special case for Bionic libs and other libs installed with them. This is to
+ // prevent those libs from being included in the search path
+ // /apex/com.android.runtime/${LIB}. This exclusion is required because those libs
+ // in the Runtime APEX are available via the legacy paths in /system/lib/. By the
+ // init process, the libs in the APEX are bind-mounted to the legacy paths and thus
+ // will be loaded into the default linker namespace (aka "platform" namespace). If
+ // the libs are directly in /apex/com.android.runtime/${LIB} then the same libs will
+ // be loaded again into the runtime linker namespace, which will result in double
+ // loading of them, which isn't supported.
+ dirInApex = filepath.Join(dirInApex, "bionic")
+ }
+
+ fileToCopy := ccMod.OutputFile().Path()
+ androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
+ return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
+}
+
+func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
+ dirInApex := "bin"
+ if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
+ }
+ dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
+ fileToCopy := cc.OutputFile().Path()
+ androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
+ af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
+ af.symlinks = cc.Symlinks()
+ af.dataPaths = cc.DataPaths()
+ return af
+}
+
+func apexFileForRustExecutable(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+ dirInApex := "bin"
+ if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
+ }
+ fileToCopy := rustm.OutputFile().Path()
+ androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
+ af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
+ return af
+}
+
+func apexFileForRustLibrary(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+ // Decide the APEX-local directory by the multilib of the library
+ // In the future, we may query this to the module.
+ var dirInApex string
+ switch rustm.Arch().ArchType.Multilib {
+ case "lib32":
+ dirInApex = "lib"
+ case "lib64":
+ dirInApex = "lib64"
+ }
+ if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
+ }
+ fileToCopy := rustm.OutputFile().Path()
+ androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
+ return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
+}
+
+func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
+ dirInApex := "bin"
+ fileToCopy := py.HostToolPath().Path()
+ return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
+}
+
+func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
+ dirInApex := "bin"
+ s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
+ if err != nil {
+ ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
+ return apexFile{}
+ }
+ fileToCopy := android.PathForOutput(ctx, s)
+ // NB: Since go binaries are static we don't need the module for anything here, which is
+ // good since the go tool is a blueprint.Module not an android.Module like we would
+ // normally use.
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
+}
+
+func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
+ dirInApex := filepath.Join("bin", sh.SubDir())
+ fileToCopy := sh.OutputFile()
+ af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
+ af.symlinks = sh.Symlinks()
+ return af
+}
+
+func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
+ dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
+ fileToCopy := prebuilt.OutputFile()
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
+}
+
+func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
+ dirInApex := filepath.Join("etc", config.SubDir())
+ fileToCopy := config.CompatConfig()
+ return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
+}
+
+// javaModule is an interface to handle all Java modules (java_library, dex_import, etc) in the same
+// way.
+type javaModule interface {
+ android.Module
+ BaseModuleName() string
+ DexJarBuildPath() android.Path
+ JacocoReportClassesFile() android.Path
+ LintDepSets() java.LintDepSets
+ Stem() string
+}
+
+var _ javaModule = (*java.Library)(nil)
+var _ javaModule = (*java.SdkLibrary)(nil)
+var _ javaModule = (*java.DexImport)(nil)
+var _ javaModule = (*java.SdkLibraryImport)(nil)
+
+func apexFileForJavaModule(ctx android.BaseModuleContext, module javaModule) apexFile {
+ dirInApex := "javalib"
+ fileToCopy := module.DexJarBuildPath()
+ af := newApexFile(ctx, fileToCopy, module.BaseModuleName(), dirInApex, javaSharedLib, module)
+ af.jacocoReportClassesFile = module.JacocoReportClassesFile()
+ af.lintDepSets = module.LintDepSets()
+ af.customStem = module.Stem() + ".jar"
+ return af
+}
+
+// androidApp is an interface to handle all app modules (android_app, android_app_import, etc.) in
+// the same way.
+type androidApp interface {
+ android.Module
+ Privileged() bool
+ InstallApkName() string
+ OutputFile() android.Path
+ JacocoReportClassesFile() android.Path
+ Certificate() java.Certificate
+ BaseModuleName() string
+}
+
+var _ androidApp = (*java.AndroidApp)(nil)
+var _ androidApp = (*java.AndroidAppImport)(nil)
+
+func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
+ appDir := "app"
+ if aapp.Privileged() {
+ appDir = "priv-app"
+ }
+ dirInApex := filepath.Join(appDir, aapp.InstallApkName())
+ fileToCopy := aapp.OutputFile()
+ af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
+ af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
+ af.certificate = aapp.Certificate()
+
+ if app, ok := aapp.(interface {
+ OverriddenManifestPackageName() string
+ }); ok {
+ af.overriddenPackageName = app.OverriddenManifestPackageName()
+ }
+ return af
+}
+
+func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
+ rroDir := "overlay"
+ dirInApex := filepath.Join(rroDir, rro.Theme())
+ fileToCopy := rro.OutputFile()
+ af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
+ af.certificate = rro.Certificate()
+
+ if a, ok := rro.(interface {
+ OverriddenManifestPackageName() string
+ }); ok {
+ af.overriddenPackageName = a.OverriddenManifestPackageName()
+ }
+ return af
+}
+
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
+ dirInApex := filepath.Join("etc", "bpf")
+ return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
+}
+
+// WalyPayloadDeps visits dependencies that contributes to the payload of this APEX. For each of the
+// visited module, the `do` callback is executed. Returning true in the callback continues the visit
+// to the child modules. Returning false makes the visit to continue in the sibling or the parent
+// modules. This is used in check* functions below.
+func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ am, ok := child.(android.ApexModule)
+ if !ok || !am.CanHaveApexVariants() {
+ return false
+ }
+
+ // Filter-out unwanted depedendencies
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+ if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
+ return false
+ }
+
+ ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
+ externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
+
+ // Visit actually
+ return do(ctx, parent, am, externalDep)
+ })
+}
+
+// filesystem type of the apex_payload.img inside the APEX. Currently, ext4 and f2fs are supported.
+type fsType int
+
+const (
+ ext4 fsType = iota
+ f2fs
+)
+
+func (f fsType) string() string {
+ switch f {
+ case ext4:
+ return ext4FsType
+ case f2fs:
+ return f2fsFsType
+ default:
+ panic(fmt.Errorf("unknown APEX payload type %d", f))
+ }
+}
+
+// Creates build rules for an APEX. It consists of the following major steps:
+//
+// 1) do some validity checks such as apex_available, min_sdk_version, etc.
+// 2) traverse the dependency tree to collect apexFile structs from them.
+// 3) some fields in apexBundle struct are configured
+// 4) generate the build rules to create the APEX. This is mostly done in builder.go.
+func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 1) do some validity checks such as apex_available, min_sdk_version, etc.
+ a.checkApexAvailability(ctx)
+ a.checkUpdatable(ctx)
+ a.checkMinSdkVersion(ctx)
+ a.checkStaticLinkingToStubLibraries(ctx)
+ if len(a.properties.Tests) > 0 && !a.testApex {
+ ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
+ return
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 2) traverse the dependency tree to collect apexFile structs from them.
+
+ // all the files that will be included in this APEX
+ var filesInfo []apexFile
+
+ // native lib dependencies
+ var provideNativeLibs []string
+ var requireNativeLibs []string
+
+ handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
+
+ // TODO(jiyong): do this using WalkPayloadDeps
+ // TODO(jiyong): make this clean!!!
+ ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(child)
+ if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+ return false
+ }
+ depName := ctx.OtherModuleName(child)
+ if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+ switch depTag {
+ case sharedLibTag, jniLibTag:
+ isJniLib := depTag == jniLibTag
+ if c, ok := child.(*cc.Module); ok {
+ fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
+ fi.isJniLib = isJniLib
+ filesInfo = append(filesInfo, fi)
+ // Collect the list of stub-providing libs except:
+ // - VNDK libs are only for vendors
+ // - bootstrap bionic libs are treated as provided by system
+ if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
+ 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 {
+ propertyName = "jni_libs"
+ }
+ ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+ }
+ case executableTag:
+ if cc, ok := child.(*cc.Module); ok {
+ filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
+ return true // track transitive dependencies
+ } else if sh, ok := child.(*sh.ShBinary); ok {
+ filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
+ } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
+ filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
+ } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
+ filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
+ } else if rust, ok := child.(*rust.Module); ok {
+ filesInfo = append(filesInfo, apexFileForRustExecutable(ctx, rust))
+ return true // track transitive dependencies
+ } else {
+ ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
+ }
+ case javaLibTag:
+ switch child.(type) {
+ case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+ af := apexFileForJavaModule(ctx, child.(javaModule))
+ if !af.ok() {
+ ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+ return false
+ }
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ default:
+ ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+ }
+ case androidAppTag:
+ if ap, ok := child.(*java.AndroidApp); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ return true // track transitive dependencies
+ } else if ap, ok := child.(*java.AndroidAppImport); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ } else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
+ filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
+ } else if ap, ok := child.(*java.AndroidAppSet); ok {
+ appDir := "app"
+ if ap.Privileged() {
+ appDir = "priv-app"
+ }
+ af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(),
+ filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
+ af.certificate = java.PresignedCertificate
+ filesInfo = append(filesInfo, af)
+ } else {
+ ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+ }
+ case rroTag:
+ if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+ filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+ } else {
+ ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+ }
+ case bpfTag:
+ if bpfProgram, ok := child.(bpf.BpfModule); ok {
+ filesToCopy, _ := bpfProgram.OutputFiles("")
+ for _, bpfFile := range filesToCopy {
+ filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, bpfProgram))
+ }
+ } else {
+ ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
+ }
+ case prebuiltTag:
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ } else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
+ filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
+ } else {
+ ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc and not a platform_compat_config module", depName)
+ }
+ case testTag:
+ if ccTest, ok := child.(*cc.Module); ok {
+ if ccTest.IsTestPerSrcAllTestsVariation() {
+ // Multiple-output test module (where `test_per_src: true`).
+ //
+ // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+ // We do not add this variation to `filesInfo`, as it has no output;
+ // however, we do add the other variations of this module as indirect
+ // dependencies (see below).
+ } else {
+ // Single-output test module (where `test_per_src: false`).
+ af := apexFileForExecutable(ctx, ccTest)
+ af.class = nativeTest
+ filesInfo = append(filesInfo, af)
+ }
+ return true // track transitive dependencies
+ } else {
+ ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+ }
+ case keyTag:
+ if key, ok := child.(*apexKey); ok {
+ a.private_key_file = key.private_key_file
+ a.public_key_file = key.public_key_file
+ } else {
+ ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+ }
+ return false
+ case certificateTag:
+ if dep, ok := child.(*java.AndroidAppCertificate); ok {
+ a.container_certificate_file = dep.Certificate.Pem
+ a.container_private_key_file = dep.Certificate.Key
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+ }
+ case android.PrebuiltDepTag:
+ // If the prebuilt is force disabled, remember to delete the prebuilt file
+ // that might have been installed in the previous builds
+ if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
+ a.prebuiltFileToDelete = prebuilt.InstallFilename()
+ }
+ }
+ } else if !a.vndkApex {
+ // indirect dependencies
+ if am, ok := child.(android.ApexModule); ok {
+ // We cannot use a switch statement on `depTag` here as the checked
+ // tags used below are private (e.g. `cc.sharedDepTag`).
+ if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
+ requireNativeLibs = append(requireNativeLibs, ":vndk")
+ return false
+ }
+ af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
+ af.transitiveDep = true
+
+ // Always track transitive dependencies for host.
+ if a.Host() {
+ filesInfo = append(filesInfo, af)
+ return true
+ }
+
+ abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+ if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
+ // If the dependency is a stubs lib, don't include it in this APEX,
+ // but make sure that the lib is installed on the device.
+ // In case no APEX is having the lib, the lib is installed to the system
+ // partition.
+ //
+ // Always include if we are a host-apex however since those won't have any
+ // system libraries.
+ if !am.DirectlyInAnyApex() {
+ // we need a module name for Make
+ name := cc.ImplementationModuleNameForMake(ctx)
+
+ if !proptools.Bool(a.properties.Use_vendor) {
+ // we don't use subName(.vendor) for a "use_vendor: true" apex
+ // which is supposed to be installed in /system
+ name += cc.Properties.SubName
+ }
+ if !android.InList(name, a.requiredDeps) {
+ a.requiredDeps = append(a.requiredDeps, name)
+ }
+ }
+ requireNativeLibs = append(requireNativeLibs, af.stem())
+ // Don't track further
+ return false
+ }
+
+ // If the dep is not considered to be in the same
+ // apex, don't add it to filesInfo so that it is not
+ // included in this APEX.
+ // TODO(jiyong): move this to at the top of the
+ // else-if clause for the indirect dependencies.
+ // Currently, that's impossible because we would
+ // like to record requiredNativeLibs even when
+ // DepIsInSameAPex is false. We also shouldn't do
+ // this for host.
+ if !am.DepIsInSameApex(ctx, am) {
+ return false
+ }
+
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ } else if rm, ok := child.(*rust.Module); ok {
+ af := apexFileForRustLibrary(ctx, rm)
+ af.transitiveDep = true
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if cc.IsTestPerSrcDepTag(depTag) {
+ if cc, ok := child.(*cc.Module); ok {
+ af := apexFileForExecutable(ctx, cc)
+ // Handle modules created as `test_per_src` variations of a single test module:
+ // use the name of the generated test binary (`fileToCopy`) instead of the name
+ // of the original test module (`depName`, shared by all `test_per_src`
+ // variations of that module).
+ af.androidMkModuleName = filepath.Base(af.builtFile.String())
+ // these are not considered transitive dep
+ af.transitiveDep = false
+ 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
+ } else if java.IsXmlPermissionsFileDepTag(depTag) {
+ if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+ filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+ }
+ } else if rust.IsDylibDepTag(depTag) {
+ if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+ af := apexFileForRustLibrary(ctx, rustm)
+ af.transitiveDep = true
+ filesInfo = append(filesInfo, af)
+ return true // track transitive dependencies
+ }
+ } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
+ // nothing
+ } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+ ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+ }
+ }
+ }
+ return false
+ })
+ if a.private_key_file == nil {
+ ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
+ return
+ }
+
+ // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries. Build rules are
+ // generated by the dexpreopt singleton, and here we access build artifacts via the global
+ // boot image config.
+ if a.artApex {
+ for arch, files := range java.DexpreoptedArtApexJars(ctx) {
+ dirInApex := filepath.Join("javalib", arch.String())
+ for _, f := range files {
+ localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+ af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
+ filesInfo = append(filesInfo, af)
+ }
+ }
+ }
+
+ // Remove duplicates in filesInfo
+ removeDup := func(filesInfo []apexFile) []apexFile {
+ encountered := make(map[string]apexFile)
+ for _, f := range filesInfo {
+ dest := filepath.Join(f.installDir, f.builtFile.Base())
+ if e, ok := encountered[dest]; !ok {
+ encountered[dest] = f
+ } else {
+ // If a module is directly included and also transitively depended on
+ // consider it as directly included.
+ e.transitiveDep = e.transitiveDep && f.transitiveDep
+ encountered[dest] = e
+ }
+ }
+ var result []apexFile
+ for _, v := range encountered {
+ result = append(result, v)
+ }
+ return result
+ }
+ filesInfo = removeDup(filesInfo)
+
+ // Sort to have consistent build rules
+ sort.Slice(filesInfo, func(i, j int) bool {
+ return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
+ })
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 3) some fields in apexBundle struct are configured
+ a.installDir = android.PathForModuleInstall(ctx, "apex")
+ a.filesInfo = filesInfo
+
+ // Set suffix and primaryApexType depending on the ApexType
+ buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
+ switch a.properties.ApexType {
+ case imageApex:
+ if buildFlattenedAsDefault {
+ a.suffix = imageApexSuffix
+ } else {
+ a.suffix = ""
+ a.primaryApexType = true
+
+ if ctx.Config().InstallExtraFlattenedApexes() {
+ a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
+ }
+ }
+ case zipApex:
+ if proptools.String(a.properties.Payload_type) == "zip" {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = zipApexSuffix
+ }
+ case flattenedApex:
+ if buildFlattenedAsDefault {
+ a.suffix = ""
+ a.primaryApexType = true
+ } else {
+ a.suffix = flattenedSuffix
+ }
+ }
+
+ switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+ case ext4FsType:
+ a.payloadFsType = ext4
+ case f2fsFsType:
+ a.payloadFsType = f2fs
+ default:
+ ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
+ }
+
+ // Optimization. If we are building bundled APEX, for the files that are gathered due to the
+ // transitive dependencies, don't place them inside the APEX, but place a symlink pointing
+ // the same library in the system partition, thus effectively sharing the same libraries
+ // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
+ // in the APEX.
+ a.linkToSystemLib = !ctx.Config().UnbundledBuild() && a.installable() && !proptools.Bool(a.properties.Use_vendor)
+
+ // APEXes targeting other than system/system_ext partitions use vendor/product variants.
+ // So we can't link them to /system/lib libs which are core variants.
+ if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+ a.linkToSystemLib = false
+ }
+
+ // We don't need the optimization for updatable APEXes, as it might give false signal
+ // to the system health when the APEXes are still bundled (b/149805758)
+ if a.Updatable() && a.properties.ApexType == imageApex {
+ a.linkToSystemLib = false
+ }
+
+ // We also don't want the optimization for host APEXes, because it doesn't make sense.
+ if ctx.Host() {
+ a.linkToSystemLib = false
+ }
+
+ a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // 4) generate the build rules to create the APEX. This is done in builder.go.
+ a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+ if a.properties.ApexType == flattenedApex {
+ a.buildFlattenedApex(ctx)
+ } else {
+ a.buildUnflattenedApex(ctx)
+ }
+ a.buildApexDependencyInfo(ctx)
+ a.buildLintReports(ctx)
+
+ // Append meta-files to the filesInfo list so that they are reflected in Android.mk as well.
+ if a.installable() {
+ // For flattened APEX, make sure that APEX manifest and apex_pubkey are also copied
+ // along with other ordinary files. (Note that this is done by apexer for
+ // non-flattened APEXes)
+ a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
+
+ // Place the public key as apex_pubkey. This is also done by apexer for
+ // non-flattened APEXes case.
+ // TODO(jiyong): Why do we need this CP rule?
+ copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: a.public_key_file,
+ Output: copiedPubkey,
+ })
+ a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Factory functions
+//
+
+func newApexBundle() *apexBundle {
+ module := &apexBundle{}
+
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.targetProperties)
+ module.AddProperties(&module.archProperties)
+ module.AddProperties(&module.overridableProperties)
+
+ android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ android.InitSdkAwareModule(module)
+ android.InitOverridableModule(module, &module.overridableProperties.Overrides)
+ return module
+}
+
+func ApexBundleFactory(testApex bool, artApex bool) android.Module {
+ bundle := newApexBundle()
+ bundle.testApex = testApex
+ bundle.artApex = artApex
+ return bundle
+}
+
+// apex_test is an APEX for testing. The difference from the ordinary apex module type is that
+// certain compatibility checks such as apex_available are not done for apex_test.
+func testApexBundleFactory() android.Module {
+ bundle := newApexBundle()
+ bundle.testApex = true
+ return bundle
+}
+
+// apex packages other modules into an APEX file which is a packaging format for system-level
+// components like binaries, shared libraries, etc.
+func BundleFactory() android.Module {
+ return newApexBundle()
+}
+
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+// apex_defaults provides defaultable properties to other apex modules.
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &apexBundleProperties{},
+ &apexTargetBundleProperties{},
+ &overridableProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+ return module
+}
+
+type OverrideApex struct {
+ android.ModuleBase
+ android.OverrideModuleBase
+}
+
+func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // All the overrides happen in the base module.
+}
+
+// override_apex is used to create an apex module based on another apex module by overriding some of
+// its properties.
+func overrideApexFactory() android.Module {
+ m := &OverrideApex{}
+
+ m.AddProperties(&overridableProperties{})
+
+ android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ android.InitOverrideModule(m)
+ return m
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Vality check routines
+//
+// These are called in at the very beginning of GenerateAndroidBuildActions to flag an error when
+// certain conditions are not met.
+//
+// TODO(jiyong): move these checks to a separate go file.
+
+// Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version
+// of this apexBundle.
+func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
+ if a.testApex || a.vndkApex {
+ return
+ }
+ // Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
+ if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
+ return
+ }
+ // apexBundle::minSdkVersion reports its own errors.
+ minSdkVersion := a.minSdkVersion(ctx)
+ android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+}
+
+func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
+ ver := proptools.String(a.properties.Min_sdk_version)
+ if ver == "" {
+ return android.FutureApiLevel
+ }
+ apiLevel, err := android.ApiLevelFromUser(ctx, ver)
+ if err != nil {
+ ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+ return android.NoneApiLevel
+ }
+ if apiLevel.IsPreview() {
+ // All codenames should build against "current".
+ return android.FutureApiLevel
+ }
+ return apiLevel
+}
+
+// Ensures that a lib providing stub isn't statically linked
+func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
+ // Practically, we only care about regular APEXes on the device.
+ if ctx.Host() || a.testApex || a.vndkApex {
+ return
+ }
+
+ abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+
+ a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+ if ccm, ok := to.(*cc.Module); ok {
+ apexName := ctx.ModuleName()
+ fromName := ctx.OtherModuleName(from)
+ toName := ctx.OtherModuleName(to)
+
+ // If `to` is not actually in the same APEX as `from` then it does not need
+ // apex_available and neither do any of its dependencies.
+ if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+ // As soon as the dependency graph crosses the APEX boundary, don't go further.
+ return false
+ }
+
+ // 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.
+ // 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
+ }
+
+ isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !abInfo.Contents.DirectlyInApex(toName)
+ if isStubLibraryFromOtherApex && !externalDep {
+ ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
+ "It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
+ }
+
+ }
+ return true
+ })
+}
+
+// Enforce that Java deps of the apex are using stable SDKs to compile
+func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
+ if a.Updatable() {
+ if String(a.properties.Min_sdk_version) == "" {
+ ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
+ }
+ a.checkJavaStableSdkVersion(ctx)
+ }
+}
+
+func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
+ // Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
+ // java's checkLinkType guarantees correct usage for transitive deps
+ ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ switch tag {
+ case javaLibTag, androidAppTag:
+ if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
+ if err := m.CheckStableSdkVersion(); err != nil {
+ ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
+ }
+ }
+ }
+ })
+}
+
+// Ensures that the all the dependencies are marked as available for this APEX
+func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
+ // Let's be practical. Availability for test, host, and the VNDK apex isn't important
+ if ctx.Host() || a.testApex || a.vndkApex {
+ return
+ }
+
+ // Because APEXes targeting other than system/system_ext partitions can't set
+ // apex_available, we skip checks for these APEXes
+ if a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+ return
+ }
+
+ // Coverage build adds additional dependencies for the coverage-only runtime libraries.
+ // Requiring them and their transitive depencies with apex_available is not right
+ // because they just add noise.
+ if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || a.IsNativeCoverageNeeded(ctx) {
+ return
+ }
+
+ a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+ // As soon as the dependency graph crosses the APEX boundary, don't go further.
+ if externalDep {
+ return false
+ }
+
+ apexName := ctx.ModuleName()
+ fromName := ctx.OtherModuleName(from)
+ toName := ctx.OtherModuleName(to)
+
+ // If `to` is not actually in the same APEX as `from` then it does not need
+ // apex_available and neither do any of its dependencies.
+ if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+ // As soon as the dependency graph crosses the APEX boundary, don't go
+ // further.
+ return false
+ }
+
+ if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
+ return true
+ }
+ ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s",
+ fromName, toName, ctx.GetPathString(true))
+ // Visit this module's dependencies to check and report any issues with their availability.
+ return true
+ })
+}
+
+var (
+ apexAvailBaseline = makeApexAvailableBaseline()
inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
)
+func baselineApexAvailable(apex, moduleName string) bool {
+ key := apex
+ moduleName = normalizeModuleName(moduleName)
+
+ if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
+ return true
+ }
+
+ key = android.AvailableToAnyApex
+ if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
+ return true
+ }
+
+ return false
+}
+
+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 = 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
+ moduleName = "libclang_rt"
+ }
+ if strings.HasPrefix(moduleName, "androidx.") {
+ // TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
+ moduleName = "androidx"
+ }
+ return moduleName
+}
+
// Transform the map of apex -> modules to module -> apexes.
func invertApexBaseline(m map[string][]string) map[string][]string {
r := make(map[string][]string)
@@ -90,9 +2248,8 @@
return inverseApexAvailBaseline[normalizeModuleName(moduleName)]
}
-// This is a map from apex to modules, which overrides the
-// apex_available setting for that particular module to make
-// it available for the apex regardless of its setting.
+// This is a map from apex to modules, which overrides the apex_available setting for that
+// particular module to make it available for the apex regardless of its setting.
// TODO(b/147364041): remove this
func makeApexAvailableBaseline() map[string][]string {
// The "Module separator"s below are employed to minimize merge conflicts.
@@ -671,6 +2828,26 @@
return m
}
+func init() {
+ android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
+ android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
+}
+
+func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
+ rules := make([]android.Rule, 0, len(modules_packages))
+ for module_name, module_packages := range modules_packages {
+ permitted_packages_rule := android.NeverAllow().
+ BootclasspathJar().
+ With("apex_available", module_name).
+ WithMatcher("permitted_packages", android.NotInList(module_packages)).
+ Because("jars that are part of the " + module_name +
+ " module may only allow these packages: " + strings.Join(module_packages, ",") +
+ ". Please jarjar or move code around.")
+ rules = append(rules, permitted_packages_rule)
+ }
+ return rules
+}
+
// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
// Adding code to the bootclasspath in new packages will cause issues on module update.
func qModulesPackages() map[string][]string {
@@ -719,1938 +2896,3 @@
},
}
}
-
-func init() {
- android.RegisterModuleType("apex", BundleFactory)
- android.RegisterModuleType("apex_test", testApexBundleFactory)
- android.RegisterModuleType("apex_vndk", vndkApexBundleFactory)
- android.RegisterModuleType("apex_defaults", defaultsFactory)
- android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
- android.RegisterModuleType("override_apex", overrideApexFactory)
- android.RegisterModuleType("apex_set", apexSetFactory)
-
- android.PreDepsMutators(RegisterPreDepsMutators)
- android.PostDepsMutators(RegisterPostDepsMutators)
-
- android.AddNeverAllowRules(createApexPermittedPackagesRules(qModulesPackages())...)
- android.AddNeverAllowRules(createApexPermittedPackagesRules(rModulesPackages())...)
-}
-
-func createApexPermittedPackagesRules(modules_packages map[string][]string) []android.Rule {
- rules := make([]android.Rule, 0, len(modules_packages))
- for module_name, module_packages := range modules_packages {
- permitted_packages_rule := android.NeverAllow().
- BootclasspathJar().
- With("apex_available", module_name).
- WithMatcher("permitted_packages", android.NotInList(module_packages)).
- Because("jars that are part of the " + module_name +
- " module may only allow these packages: " + strings.Join(module_packages, ",") +
- ". Please jarjar or move code around.")
- rules = append(rules, permitted_packages_rule)
- }
- return rules
-}
-
-func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
- ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
-}
-
-func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.TopDown("apex_deps", apexDepsMutator).Parallel()
- ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel()
- ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel()
- ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel()
- ctx.BottomUp("apex", apexMutator).Parallel()
- ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel()
- ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel()
- ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
- ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel()
-}
-
-// Mark the direct and transitive dependencies of apex bundles so that they
-// can be built for the apex bundles.
-func apexDepsMutator(mctx android.TopDownMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- a, ok := mctx.Module().(*apexBundle)
- if !ok || a.vndkApex {
- return
- }
-
- useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
- excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
- if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
- mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
- return
- }
-
- contents := make(map[string]android.ApexMembership)
-
- continueApexDepsWalk := func(child, parent android.Module) bool {
- am, ok := child.(android.ApexModule)
- if !ok || !am.CanHaveApexVariants() {
- return false
- }
- if !parent.(android.DepIsInSameApex).DepIsInSameApex(mctx, child) {
- return false
- }
- if excludeVndkLibs {
- if c, ok := child.(*cc.Module); ok && c.IsVndk() {
- return false
- }
- }
- return true
- }
-
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !continueApexDepsWalk(child, parent) {
- return false
- }
-
- depName := mctx.OtherModuleName(child)
- // If the parent is apexBundle, this child is directly depended.
- _, directDep := parent.(*apexBundle)
- contents[depName] = contents[depName].Add(directDep)
- return true
- })
-
- apexContents := android.NewApexContents(mctx.ModuleName(), contents)
- mctx.SetProvider(ApexBundleInfoProvider, ApexBundleInfo{
- Contents: apexContents,
- })
-
- apexInfo := android.ApexInfo{
- ApexVariationName: mctx.ModuleName(),
- MinSdkVersionStr: a.minSdkVersion(mctx).String(),
- RequiredSdks: a.RequiredSdks(),
- Updatable: a.Updatable(),
- InApexes: []string{mctx.ModuleName()},
- ApexContents: []*android.ApexContents{apexContents},
- }
-
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !continueApexDepsWalk(child, parent) {
- return false
- }
-
- child.(android.ApexModule).BuildForApex(apexInfo)
- return true
- })
-}
-
-func apexUniqueVariationsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if am, ok := mctx.Module().(android.ApexModule); ok {
- // Check if any dependencies use unique apex variations. If so, use unique apex variations
- // for this module.
- android.UpdateUniqueApexVariationsForDeps(mctx, am)
- }
-}
-
-func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- // Check if this module is a test for an apex. If so, add a dependency on the apex
- // in order to retrieve its contents later.
- if am, ok := mctx.Module().(android.ApexModule); ok {
- if testFor := am.TestFor(); len(testFor) > 0 {
- mctx.AddFarVariationDependencies([]blueprint.Variation{
- {Mutator: "os", Variation: am.Target().OsVariation()},
- {"arch", "common"},
- }, testForTag, testFor...)
- }
- }
-}
-
-func apexTestForMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
-
- if _, ok := mctx.Module().(android.ApexModule); ok {
- var contents []*android.ApexContents
- for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
- abInfo := mctx.OtherModuleProvider(testFor, ApexBundleInfoProvider).(ApexBundleInfo)
- contents = append(contents, abInfo.Contents)
- }
- mctx.SetProvider(android.ApexTestForInfoProvider, android.ApexTestForInfo{
- ApexContents: contents,
- })
- }
-}
-
-// mark if a module cannot be available to platform. A module cannot be available
-// to platform if 1) it is explicitly marked as not available (i.e. "//apex_available:platform"
-// is absent) or 2) it depends on another module that isn't (or can't be) available to platform
-func markPlatformAvailability(mctx android.BottomUpMutatorContext) {
- // Host and recovery are not considered as platform
- if mctx.Host() || mctx.Module().InstallInRecovery() {
- return
- }
-
- if am, ok := mctx.Module().(android.ApexModule); ok {
- availableToPlatform := am.AvailableFor(android.AvailableToPlatform)
-
- // If any of the dep is not available to platform, this module is also considered
- // as being not available to platform even if it has "//apex_available:platform"
- mctx.VisitDirectDeps(func(child android.Module) {
- if !am.DepIsInSameApex(mctx, child) {
- // if the dependency crosses apex boundary, don't consider it
- return
- }
- if dep, ok := child.(android.ApexModule); ok && dep.NotAvailableForPlatform() {
- availableToPlatform = false
- // TODO(b/154889534) trigger an error when 'am' has "//apex_available:platform"
- }
- })
-
- // Exception 1: stub libraries and native bridge libraries are always available to platform
- if cc, ok := mctx.Module().(*cc.Module); ok &&
- (cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) {
- availableToPlatform = true
- }
-
- // Exception 2: bootstrap bionic libraries are also always available to platform
- if cc.InstallToBootstrap(mctx.ModuleName(), mctx.Config()) {
- availableToPlatform = true
- }
-
- if !availableToPlatform {
- am.SetNotAvailableForPlatform()
- }
- }
-}
-
-// If a module in an APEX depends on a module from an SDK then it needs an APEX
-// specific variant created for it. Refer to sdk.sdkDepsReplaceMutator.
-func inAnySdk(module android.Module) bool {
- if sa, ok := module.(android.SdkAware); ok {
- return sa.IsInAnySdk()
- }
-
- return false
-}
-
-// Create apex variations if a module is included in APEX(s).
-func apexMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
-
- if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
- android.CreateApexVariations(mctx, am)
- } else if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
- // apex bundle itself is mutated so that it and its modules have same
- // apex variant.
- apexBundleName := mctx.ModuleName()
- mctx.CreateVariations(apexBundleName)
- } else if o, ok := mctx.Module().(*OverrideApex); ok {
- apexBundleName := o.GetOverriddenModuleName()
- if apexBundleName == "" {
- mctx.ModuleErrorf("base property is not set")
- return
- }
- mctx.CreateVariations(apexBundleName)
- }
-
-}
-
-func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if am, ok := mctx.Module().(android.ApexModule); ok {
- android.UpdateDirectlyInAnyApex(mctx, am)
- }
-}
-
-func apexFlattenedMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled() {
- return
- }
- if ab, ok := mctx.Module().(*apexBundle); ok {
- var variants []string
- switch proptools.StringDefault(ab.properties.Payload_type, "image") {
- case "image":
- variants = append(variants, imageApexType, flattenedApexType)
- case "zip":
- variants = append(variants, zipApexType)
- case "both":
- variants = append(variants, imageApexType, zipApexType, flattenedApexType)
- default:
- mctx.PropertyErrorf("type", "%q is not one of \"image\", \"zip\", or \"both\".", *ab.properties.Payload_type)
- return
- }
-
- modules := mctx.CreateLocalVariations(variants...)
-
- for i, v := range variants {
- switch v {
- case imageApexType:
- modules[i].(*apexBundle).properties.ApexType = imageApex
- case zipApexType:
- modules[i].(*apexBundle).properties.ApexType = zipApex
- case flattenedApexType:
- modules[i].(*apexBundle).properties.ApexType = flattenedApex
- if !mctx.Config().FlattenApex() && ab.Platform() {
- modules[i].(*apexBundle).MakeAsSystemExt()
- }
- }
- }
- } else if _, ok := mctx.Module().(*OverrideApex); ok {
- mctx.CreateVariations(imageApexType, flattenedApexType)
- }
-}
-
-func apexUsesMutator(mctx android.BottomUpMutatorContext) {
- if ab, ok := mctx.Module().(*apexBundle); ok {
- mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...)
- }
-}
-
-var (
- useVendorAllowListKey = android.NewOnceKey("useVendorAllowList")
-)
-
-// useVendorAllowList returns the list of APEXes which are allowed to use_vendor.
-// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
-// which may cause compatibility issues. (e.g. libbinder)
-// Even though libbinder restricts its availability via 'apex_available' property and relies on
-// yet another macro __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules
-// to avoid similar problems.
-func useVendorAllowList(config android.Config) []string {
- return config.Once(useVendorAllowListKey, func() interface{} {
- return []string{
- // swcodec uses "vendor" variants for smaller size
- "com.android.media.swcodec",
- "test_com.android.media.swcodec",
- }
- }).([]string)
-}
-
-// setUseVendorAllowListForTest overrides useVendorAllowList and must be
-// called before the first call to useVendorAllowList()
-func setUseVendorAllowListForTest(config android.Config, allowList []string) {
- config.Once(useVendorAllowListKey, func() interface{} {
- return allowList
- })
-}
-
-type ApexNativeDependencies struct {
- // List of native libraries
- Native_shared_libs []string
-
- // List of JNI libraries
- Jni_libs []string
-
- // List of native executables
- Binaries []string
-
- // List of native tests
- Tests []string
-}
-
-type apexMultilibProperties struct {
- // Native dependencies whose compile_multilib is "first"
- First ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "both"
- Both ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "prefer32"
- Prefer32 ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "32"
- Lib32 ApexNativeDependencies
-
- // Native dependencies whose compile_multilib is "64"
- Lib64 ApexNativeDependencies
-}
-
-type apexBundleProperties struct {
- // Json manifest file describing meta info of this APEX bundle. Default:
- // "apex_manifest.json"
- Manifest *string `android:"path"`
-
- // AndroidManifest.xml file used for the zip container of this APEX bundle.
- // If unspecified, a default one is automatically generated.
- AndroidManifest *string `android:"path"`
-
- // Canonical name of the APEX bundle. Used to determine the path to the activated APEX on
- // device (/apex/<apex_name>).
- // If unspecified, defaults to the value of name.
- Apex_name *string
-
- // Determines the file contexts file for setting security context to each file in this APEX bundle.
- // For platform APEXes, this should points to a file under /system/sepolicy
- // Default: /system/sepolicy/apex/<module_name>_file_contexts.
- File_contexts *string `android:"path"`
-
- ApexNativeDependencies
-
- // List of java libraries that are embedded inside this APEX bundle
- Java_libs []string
-
- // List of prebuilt files that are embedded inside this APEX bundle
- Prebuilts []string
-
- // List of BPF programs inside APEX
- Bpfs []string
-
- // Name of the apex_key module that provides the private key to sign APEX
- Key *string
-
- // The type of APEX to build. Controls what the APEX payload is. Either
- // 'image', 'zip' or 'both'. Default: 'image'.
- Payload_type *string
-
- // The name of a certificate in the default certificate directory, blank to use the default product certificate,
- // or an android_app_certificate module name in the form ":module".
- Certificate *string
-
- // Whether this APEX is installable to one of the partitions. Default: true.
- Installable *bool
-
- // For native libraries and binaries, use the vendor variant instead of the core (platform) variant.
- // Default is false.
- Use_vendor *bool
-
- // For telling the apex to ignore special handling for system libraries such as bionic. Default is false.
- Ignore_system_library_special_case *bool
-
- Multilib apexMultilibProperties
-
- // List of sanitizer names that this APEX is enabled for
- SanitizerNames []string `blueprint:"mutated"`
-
- PreventInstall bool `blueprint:"mutated"`
-
- HideFromMake bool `blueprint:"mutated"`
-
- // Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
- Provide_cpp_shared_libs *bool
-
- // List of providing APEXes' names so that this APEX can depend on provided shared libraries.
- Uses []string
-
- // package format of this apex variant; could be non-flattened, flattened, or zip.
- // imageApex, zipApex or flattened
- ApexType apexPackaging `blueprint:"mutated"`
-
- // List of SDKs that are used to build this APEX. A reference to an SDK should be either
- // `name#version` or `name` which is an alias for `name#current`. If left empty, `platform#current`
- // is implied. This value affects all modules included in this APEX. In other words, they are
- // also built with the SDKs specified here.
- Uses_sdks []string
-
- // Whenever apex_payload.img of the APEX should include dm-verity hashtree.
- // Should be only used in tests#.
- Test_only_no_hashtree *bool
-
- // Whenever apex_payload.img of the APEX should not be dm-verity signed.
- // Should be only used in tests#.
- Test_only_unsigned_payload *bool
-
- IsCoverageVariant bool `blueprint:"mutated"`
-
- // Whether this APEX is considered updatable or not. When set to true, this will enforce additional
- // rules for making sure that the APEX is truly updatable.
- // - To be updatable, min_sdk_version should be set as well
- // This will also disable the size optimizations like symlinking to the system libs.
- // Default is false.
- Updatable *bool
-
- // The minimum SDK version that this apex must be compatibile with.
- Min_sdk_version *string
-
- // If set true, VNDK libs are considered as stable libs and are not included in this apex.
- // Should be only used in non-system apexes (e.g. vendor: true).
- // Default is false.
- Use_vndk_as_stable *bool
-
- // The type of filesystem to use for an image apex. Either 'ext4' or 'f2fs'.
- // Default 'ext4'.
- Payload_fs_type *string
-}
-
-type ApexBundleInfo struct {
- Contents *android.ApexContents
-}
-
-var ApexBundleInfoProvider = blueprint.NewMutatorProvider(ApexBundleInfo{}, "apex_deps")
-
-type apexTargetBundleProperties struct {
- Target struct {
- // Multilib properties only for android.
- Android struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host.
- Host struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host linux_bionic.
- Linux_bionic struct {
- Multilib apexMultilibProperties
- }
-
- // Multilib properties only for host linux_glibc.
- Linux_glibc struct {
- Multilib apexMultilibProperties
- }
- }
-}
-
-type overridableProperties struct {
- // List of APKs to package inside APEX
- Apps []string
-
- // List of runtime resource overlays (RROs) inside APEX
- Rros []string
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
-
- // Logging Parent value
- Logging_parent string
-
- // Apex Container Package Name.
- // Override value for attribute package:name in AndroidManifest.xml
- Package_name string
-
- // A txt file containing list of files that are allowed to be included in this APEX.
- Allowed_files *string `android:"path"`
-}
-
-type apexPackaging int
-
-const (
- imageApex apexPackaging = iota
- zipApex
- flattenedApex
-)
-
-// The suffix for the output "file", not the module
-func (a apexPackaging) suffix() string {
- switch a {
- case imageApex:
- return imageApexSuffix
- case zipApex:
- return zipApexSuffix
- default:
- panic(fmt.Errorf("unknown APEX type %d", a))
- }
-}
-
-func (a apexPackaging) name() string {
- switch a {
- case imageApex:
- return imageApexType
- case zipApex:
- return zipApexType
- default:
- panic(fmt.Errorf("unknown APEX type %d", a))
- }
-}
-
-type apexFileClass int
-
-const (
- etc apexFileClass = iota
- nativeSharedLib
- nativeExecutable
- shBinary
- pyBinary
- goBinary
- javaSharedLib
- nativeTest
- app
- appSet
-)
-
-func (class apexFileClass) NameInMake() string {
- switch class {
- case etc:
- return "ETC"
- case nativeSharedLib:
- return "SHARED_LIBRARIES"
- case nativeExecutable, shBinary, pyBinary, goBinary:
- return "EXECUTABLES"
- case javaSharedLib:
- return "JAVA_LIBRARIES"
- case nativeTest:
- return "NATIVE_TESTS"
- case app, appSet:
- // b/142537672 Why isn't this APP? We want to have full control over
- // the paths and file names of the apk file under the flattend APEX.
- // If this is set to APP, then the paths and file names are modified
- // by the Make build system. For example, it is installed to
- // /system/apex/<apexname>/app/<Appname>/<apexname>.<Appname>/ instead of
- // /system/apex/<apexname>/app/<Appname> because the build system automatically
- // appends module name (which is <apexname>.<Appname> to the path.
- return "ETC"
- default:
- panic(fmt.Errorf("unknown class %d", class))
- }
-}
-
-// apexFile represents a file in an APEX bundle
-type apexFile struct {
- builtFile android.Path
- stem string
- // Module name of `module` in AndroidMk. Note the generated AndroidMk module for
- // apexFile is named something like <AndroidMk module name>.<apex name>[<apex suffix>]
- androidMkModuleName string
- installDir string
- class apexFileClass
- module android.Module
- // list of symlinks that will be created in installDir that point to this apexFile
- symlinks []string
- dataPaths []android.DataPath
- transitiveDep bool
- moduleDir string
-
- requiredModuleNames []string
- targetRequiredModuleNames []string
- hostRequiredModuleNames []string
-
- jacocoReportClassesFile android.Path // only for javalibs and apps
- lintDepSets java.LintDepSets // only for javalibs and apps
- certificate java.Certificate // only for apps
- overriddenPackageName string // only for apps
-
- isJniLib bool
-
- noticeFiles android.Paths
-}
-
-func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidMkModuleName string, installDir string, class apexFileClass, module android.Module) apexFile {
- ret := apexFile{
- builtFile: builtFile,
- androidMkModuleName: androidMkModuleName,
- installDir: installDir,
- class: class,
- module: module,
- }
- if module != nil {
- ret.moduleDir = ctx.OtherModuleDir(module)
- ret.requiredModuleNames = module.RequiredModuleNames()
- ret.targetRequiredModuleNames = module.TargetRequiredModuleNames()
- ret.hostRequiredModuleNames = module.HostRequiredModuleNames()
- ret.noticeFiles = module.NoticeFiles()
- }
- return ret
-}
-
-func (af *apexFile) Ok() bool {
- return af.builtFile != nil && af.builtFile.String() != ""
-}
-
-func (af *apexFile) apexRelativePath(path string) string {
- return filepath.Join(af.installDir, path)
-}
-
-// Path() returns path of this apex file relative to the APEX root
-func (af *apexFile) Path() string {
- return af.apexRelativePath(af.Stem())
-}
-
-func (af *apexFile) Stem() string {
- if af.stem != "" {
- return af.stem
- }
- return af.builtFile.Base()
-}
-
-// SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
-func (af *apexFile) SymlinkPaths() []string {
- var ret []string
- for _, symlink := range af.symlinks {
- ret = append(ret, af.apexRelativePath(symlink))
- }
- return ret
-}
-
-func (af *apexFile) AvailableToPlatform() bool {
- if af.module == nil {
- return false
- }
- if am, ok := af.module.(android.ApexModule); ok {
- return am.AvailableFor(android.AvailableToPlatform)
- }
- return false
-}
-
-type fsType int
-
-const (
- ext4 fsType = iota
- f2fs
-)
-
-func (f fsType) string() string {
- switch f {
- case ext4:
- return ext4FsType
- case f2fs:
- return f2fsFsType
- default:
- panic(fmt.Errorf("unknown APEX payload type %d", f))
- }
-}
-
-type apexBundle struct {
- android.ModuleBase
- android.DefaultableModuleBase
- android.OverridableModuleBase
- android.SdkBase
-
- properties apexBundleProperties
- targetProperties apexTargetBundleProperties
- overridableProperties overridableProperties
-
- // specific to apex_vndk modules
- vndkProperties apexVndkProperties
-
- bundleModuleFile android.WritablePath
- outputFile android.WritablePath
- installDir android.InstallPath
-
- prebuiltFileToDelete string
-
- public_key_file android.Path
- private_key_file android.Path
-
- container_certificate_file android.Path
- container_private_key_file android.Path
-
- fileContexts android.WritablePath
-
- // list of files to be included in this apex
- filesInfo []apexFile
-
- // list of module names that should be installed along with this APEX
- requiredDeps []string
-
- // list of module names that this APEX is including (to be shown via *-deps-info target)
- android.ApexBundleDepsInfo
-
- testApex bool
- vndkApex bool
- artApex bool
- primaryApexType bool
-
- manifestJsonOut android.WritablePath
- manifestPbOut android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD to
- // apex package itself(for unflattened build) or apex_manifest(for flattened build)
- // so that compat symlinks are always installed regardless of TARGET_FLATTEN_APEX setting.
- compatSymlinks []string
-
- // Suffix of module name in Android.mk
- // ".flattened", ".apex", ".zipapex", or ""
- suffix string
-
- installedFilesFile android.WritablePath
-
- // Whether to create symlink to the system file instead of having a file
- // inside the apex or not
- linkToSystemLib bool
-
- // Struct holding the merged notice file paths in different formats
- mergedNotices android.NoticeOutputs
-
- // Optional list of lint report zip files for apexes that contain java or app modules
- lintReports android.Paths
-
- payloadFsType fsType
-}
-
-func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
- nativeModules ApexNativeDependencies,
- target android.Target, imageVariation string) {
- // Use *FarVariation* to be able to depend on modules having
- // conflicting variations with this module. This is required since
- // arch variant of an APEX bundle is 'common' but it is 'arm' or 'arm64'
- // for native shared libs.
-
- binVariations := target.Variations()
- libVariations := append(target.Variations(),
- blueprint.Variation{Mutator: "link", Variation: "shared"})
-
- 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
- }
-
- ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
-
- ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
-
- ctx.AddFarVariationDependencies(binVariations, executableTag, nativeModules.Binaries...)
-
- ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
-}
-
-func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
- if ctx.Os().Class == android.Device {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Android.Multilib, nil)
- } else {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Host.Multilib, nil)
- if ctx.Os().Bionic() {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_bionic.Multilib, nil)
- } else {
- proptools.AppendProperties(&a.properties.Multilib, &a.targetProperties.Target.Linux_glibc.Multilib, nil)
- }
- }
-}
-
-func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
- if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorAllowList(ctx.Config())) {
- ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
- }
-
- targets := ctx.MultiTargets()
- config := ctx.DeviceConfig()
- imageVariation := a.getImageVariation(ctx)
-
- a.combineProperties(ctx)
-
- has32BitTarget := false
- for _, target := range targets {
- if target.Arch.ArchType.Multilib == "lib32" {
- has32BitTarget = true
- }
- }
- for i, target := range targets {
- if target.HostCross {
- // Don't include artifats for the host cross targets because there is no way
- // for us to run those artifacts natively on host
- continue
- }
-
- // When multilib.* is omitted for native_shared_libs/jni_libs/tests, it implies
- // multilib.both
- addDependenciesForNativeModules(ctx,
- ApexNativeDependencies{
- Native_shared_libs: a.properties.Native_shared_libs,
- Tests: a.properties.Tests,
- Jni_libs: a.properties.Jni_libs,
- Binaries: nil,
- },
- target, imageVariation)
-
- // Add native modules targetting both ABIs
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Both,
- target,
- imageVariation)
-
- isPrimaryAbi := i == 0
- if isPrimaryAbi {
- // When multilib.* is omitted for binaries, it implies
- // multilib.first
- addDependenciesForNativeModules(ctx,
- ApexNativeDependencies{
- Native_shared_libs: nil,
- Tests: nil,
- Jni_libs: nil,
- Binaries: a.properties.Binaries,
- },
- target, imageVariation)
-
- // Add native modules targetting the first ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.First,
- target,
- imageVariation)
- }
-
- switch target.Arch.ArchType.Multilib {
- case "lib32":
- // Add native modules targetting 32-bit ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Lib32,
- target,
- imageVariation)
-
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Prefer32,
- target,
- imageVariation)
- case "lib64":
- // Add native modules targetting 64-bit ABI
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Lib64,
- target,
- imageVariation)
-
- if !has32BitTarget {
- addDependenciesForNativeModules(ctx,
- a.properties.Multilib.Prefer32,
- target,
- imageVariation)
- }
- }
- }
-
- // For prebuilt_etc, use the first variant (64 on 64/32bit device,
- // 32 on 32bit device) regardless of the TARGET_PREFER_* setting.
- // b/144532908
- archForPrebuiltEtc := config.Arches()[0]
- for _, arch := range config.Arches() {
- // Prefer 64-bit arch if there is any
- if arch.ArchType.Multilib == "lib64" {
- archForPrebuiltEtc = arch
- break
- }
- }
- ctx.AddFarVariationDependencies([]blueprint.Variation{
- {Mutator: "os", Variation: ctx.Os().String()},
- {Mutator: "arch", Variation: archForPrebuiltEtc.String()},
- }, prebuiltTag, a.properties.Prebuilts...)
-
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- javaLibTag, a.properties.Java_libs...)
-
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- bpfTag, a.properties.Bpfs...)
-
- // With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
- if a.artApex && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- javaLibTag, "jacocoagent")
- }
-
- if String(a.properties.Key) == "" {
- ctx.ModuleErrorf("key is missing")
- return
- }
- ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
-
- cert := android.SrcIsModule(a.getCertString(ctx))
- if cert != "" {
- ctx.AddDependency(ctx.Module(), certificateTag, cert)
- }
-
- // TODO(jiyong): ensure that all apexes are with non-empty uses_sdks
- if len(a.properties.Uses_sdks) > 0 {
- sdkRefs := []android.SdkRef{}
- for _, str := range a.properties.Uses_sdks {
- parsed := android.ParseSdkRef(ctx, str, "uses_sdks")
- sdkRefs = append(sdkRefs, parsed)
- }
- a.BuildWithSdks(sdkRefs)
- }
-}
-
-func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
- if a.overridableProperties.Allowed_files != nil {
- android.ExtractSourceDeps(ctx, a.overridableProperties.Allowed_files)
- }
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- androidAppTag, a.overridableProperties.Apps...)
- ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
- rroTag, a.overridableProperties.Rros...)
-}
-
-func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
- // direct deps of an APEX bundle are all part of the APEX bundle
- return true
-}
-
-func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
- moduleName := ctx.ModuleName()
- // VNDK APEXes share the same certificate. To avoid adding a new VNDK version to the OVERRIDE_* list,
- // we check with the pseudo module name to see if its certificate is overridden.
- if a.vndkApex {
- moduleName = vndkApexName
- }
- certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
- if overridden {
- return ":" + certificate
- }
- return String(a.properties.Certificate)
-}
-
-func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
- switch tag {
- case "":
- return android.Paths{a.outputFile}, nil
- default:
- return nil, fmt.Errorf("unsupported module reference tag %q", tag)
- }
-}
-
-func (a *apexBundle) installable() bool {
- return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
-}
-
-func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
- return proptools.Bool(a.properties.Test_only_no_hashtree)
-}
-
-func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
- return proptools.Bool(a.properties.Test_only_unsigned_payload)
-}
-
-func (a *apexBundle) getImageVariation(ctx android.BottomUpMutatorContext) string {
- deviceConfig := ctx.DeviceConfig()
- if a.vndkApex {
- return cc.VendorVariationPrefix + a.vndkVersion(deviceConfig)
- }
-
- var prefix string
- var vndkVersion string
- if deviceConfig.VndkVersion() != "" {
- if proptools.Bool(a.properties.Use_vendor) {
- prefix = cc.VendorVariationPrefix
- vndkVersion = deviceConfig.PlatformVndkVersion()
- } else if a.SocSpecific() || a.DeviceSpecific() {
- prefix = cc.VendorVariationPrefix
- vndkVersion = deviceConfig.VndkVersion()
- } else if a.ProductSpecific() {
- prefix = cc.ProductVariationPrefix
- vndkVersion = deviceConfig.ProductVndkVersion()
- }
- }
- if vndkVersion == "current" {
- vndkVersion = deviceConfig.PlatformVndkVersion()
- }
- if vndkVersion != "" {
- return prefix + vndkVersion
- }
- return android.CoreVariation
-}
-
-func (a *apexBundle) EnableSanitizer(sanitizerName string) {
- if !android.InList(sanitizerName, a.properties.SanitizerNames) {
- a.properties.SanitizerNames = append(a.properties.SanitizerNames, sanitizerName)
- }
-}
-
-func (a *apexBundle) IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool {
- if android.InList(sanitizerName, a.properties.SanitizerNames) {
- return true
- }
-
- // Then follow the global setting
- globalSanitizerNames := []string{}
- if a.Host() {
- globalSanitizerNames = ctx.Config().SanitizeHost()
- } else {
- arches := ctx.Config().SanitizeDeviceArch()
- if len(arches) == 0 || android.InList(a.Arch().ArchType.Name, arches) {
- globalSanitizerNames = ctx.Config().SanitizeDevice()
- }
- }
- return android.InList(sanitizerName, globalSanitizerNames)
-}
-
-func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
- if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
- for _, target := range ctx.MultiTargets() {
- if target.Arch.ArchType.Multilib == "lib64" {
- ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
- {Mutator: "image", Variation: a.getImageVariation(ctx)},
- {Mutator: "link", Variation: "shared"},
- {Mutator: "version", Variation: ""}, // "" is the non-stub variant
- }...), sharedLibTag, "libclang_rt.hwasan-aarch64-android")
- break
- }
- }
- }
-}
-
-var _ cc.Coverage = (*apexBundle)(nil)
-
-func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
- return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
-}
-
-func (a *apexBundle) PreventInstall() {
- a.properties.PreventInstall = true
-}
-
-func (a *apexBundle) HideFromMake() {
- a.properties.HideFromMake = true
-}
-
-func (a *apexBundle) MarkAsCoverageVariant(coverage bool) {
- a.properties.IsCoverageVariant = coverage
-}
-
-func (a *apexBundle) EnableCoverageIfNeeded() {}
-
-// TODO(jiyong) move apexFileFor* close to the apexFile type definition
-func apexFileForNativeLibrary(ctx android.BaseModuleContext, ccMod *cc.Module, handleSpecialLibs bool) apexFile {
- // Decide the APEX-local directory by the multilib of the library
- // In the future, we may query this to the module.
- var dirInApex string
- switch ccMod.Arch().ArchType.Multilib {
- case "lib32":
- dirInApex = "lib"
- case "lib64":
- dirInApex = "lib64"
- }
- if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
- dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
- }
- dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
- if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
- // Special case for Bionic libs and other libs installed with them. This is
- // to prevent those libs from being included in the search path
- // /apex/com.android.runtime/${LIB}. This exclusion is required because
- // those libs in the Runtime APEX are available via the legacy paths in
- // /system/lib/. By the init process, the libs in the APEX are bind-mounted
- // to the legacy paths and thus will be loaded into the default linker
- // namespace (aka "platform" namespace). If the libs are directly in
- // /apex/com.android.runtime/${LIB} then the same libs will be loaded again
- // into the runtime linker namespace, which will result in double loading of
- // them, which isn't supported.
- dirInApex = filepath.Join(dirInApex, "bionic")
- }
-
- fileToCopy := ccMod.OutputFile().Path()
- androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
- return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, ccMod)
-}
-
-func apexFileForExecutable(ctx android.BaseModuleContext, cc *cc.Module) apexFile {
- dirInApex := "bin"
- if cc.Target().NativeBridge == android.NativeBridgeEnabled {
- dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
- }
- dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
- fileToCopy := cc.OutputFile().Path()
- androidMkModuleName := cc.BaseModuleName() + cc.Properties.SubName
- af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, cc)
- af.symlinks = cc.Symlinks()
- af.dataPaths = cc.DataPaths()
- return af
-}
-
-func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
- dirInApex := "bin"
- fileToCopy := py.HostToolPath().Path()
- return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
-}
-func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
- dirInApex := "bin"
- s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
- if err != nil {
- ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
- return apexFile{}
- }
- fileToCopy := android.PathForOutput(ctx, s)
- // NB: Since go binaries are static we don't need the module for anything here, which is
- // good since the go tool is a blueprint.Module not an android.Module like we would
- // normally use.
- return newApexFile(ctx, fileToCopy, depName, dirInApex, goBinary, nil)
-}
-
-func apexFileForShBinary(ctx android.BaseModuleContext, sh *sh.ShBinary) apexFile {
- dirInApex := filepath.Join("bin", sh.SubDir())
- fileToCopy := sh.OutputFile()
- af := newApexFile(ctx, fileToCopy, sh.BaseModuleName(), dirInApex, shBinary, sh)
- af.symlinks = sh.Symlinks()
- return af
-}
-
-type javaModule interface {
- android.Module
- BaseModuleName() string
- DexJarBuildPath() android.Path
- JacocoReportClassesFile() android.Path
- LintDepSets() java.LintDepSets
-
- Stem() string
-}
-
-var _ javaModule = (*java.Library)(nil)
-var _ javaModule = (*java.SdkLibrary)(nil)
-var _ javaModule = (*java.DexImport)(nil)
-var _ javaModule = (*java.SdkLibraryImport)(nil)
-
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, module javaModule) apexFile {
- dirInApex := "javalib"
- fileToCopy := module.DexJarBuildPath()
- af := newApexFile(ctx, fileToCopy, module.BaseModuleName(), dirInApex, javaSharedLib, module)
- af.jacocoReportClassesFile = module.JacocoReportClassesFile()
- af.lintDepSets = module.LintDepSets()
- af.stem = module.Stem() + ".jar"
- return af
-}
-
-func apexFileForPrebuiltEtc(ctx android.BaseModuleContext, prebuilt prebuilt_etc.PrebuiltEtcModule, depName string) apexFile {
- dirInApex := filepath.Join(prebuilt.BaseDir(), prebuilt.SubDir())
- fileToCopy := prebuilt.OutputFile()
- return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, prebuilt)
-}
-
-func apexFileForCompatConfig(ctx android.BaseModuleContext, config java.PlatformCompatConfigIntf, depName string) apexFile {
- dirInApex := filepath.Join("etc", config.SubDir())
- fileToCopy := config.CompatConfig()
- return newApexFile(ctx, fileToCopy, depName, dirInApex, etc, config)
-}
-
-func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp interface {
- android.Module
- Privileged() bool
- InstallApkName() string
- OutputFile() android.Path
- JacocoReportClassesFile() android.Path
- Certificate() java.Certificate
- BaseModuleName() string
-}) apexFile {
- appDir := "app"
- if aapp.Privileged() {
- appDir = "priv-app"
- }
- dirInApex := filepath.Join(appDir, aapp.InstallApkName())
- fileToCopy := aapp.OutputFile()
- af := newApexFile(ctx, fileToCopy, aapp.BaseModuleName(), dirInApex, app, aapp)
- af.jacocoReportClassesFile = aapp.JacocoReportClassesFile()
- af.certificate = aapp.Certificate()
-
- if app, ok := aapp.(interface {
- OverriddenManifestPackageName() string
- }); ok {
- af.overriddenPackageName = app.OverriddenManifestPackageName()
- }
- return af
-}
-
-func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
- rroDir := "overlay"
- dirInApex := filepath.Join(rroDir, rro.Theme())
- fileToCopy := rro.OutputFile()
- af := newApexFile(ctx, fileToCopy, rro.Name(), dirInApex, app, rro)
- af.certificate = rro.Certificate()
-
- if a, ok := rro.(interface {
- OverriddenManifestPackageName() string
- }); ok {
- af.overriddenPackageName = a.OverriddenManifestPackageName()
- }
- return af
-}
-
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
- dirInApex := filepath.Join("etc", "bpf")
- return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
-}
-
-// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
-type flattenedApexContext struct {
- android.ModuleContext
-}
-
-func (c *flattenedApexContext) InstallBypassMake() bool {
- return true
-}
-
-// Visit dependencies that contributes to the payload of this APEX
-func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
- ctx.WalkDeps(func(child, parent android.Module) bool {
- am, ok := child.(android.ApexModule)
- if !ok || !am.CanHaveApexVariants() {
- return false
- }
-
- childApexInfo := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
-
- dt := ctx.OtherModuleDependencyTag(child)
-
- if _, ok := dt.(android.ExcludeFromApexContentsTag); ok {
- return false
- }
-
- // Check for the direct dependencies that contribute to the payload
- if adt, ok := dt.(dependencyTag); ok {
- if adt.payload {
- return do(ctx, parent, am, false /* externalDep */)
- }
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- // Check for the indirect dependencies if it is considered as part of the APEX
- if android.InList(ctx.ModuleName(), childApexInfo.InApexes) {
- return do(ctx, parent, am, false /* externalDep */)
- }
-
- return do(ctx, parent, am, true /* externalDep */)
- })
-}
-
-func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
- ver := proptools.String(a.properties.Min_sdk_version)
- if ver == "" {
- return android.FutureApiLevel
- }
- apiLevel, err := android.ApiLevelFromUser(ctx, ver)
- if err != nil {
- ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
- return android.NoneApiLevel
- }
- if apiLevel.IsPreview() {
- // All codenames should build against "current".
- return android.FutureApiLevel
- }
- return apiLevel
-}
-
-func (a *apexBundle) Updatable() bool {
- return proptools.Bool(a.properties.Updatable)
-}
-
-var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
-
-// Ensures that the dependencies are marked as available for this APEX
-func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
- // Let's be practical. Availability for test, host, and the VNDK apex isn't important
- if ctx.Host() || a.testApex || a.vndkApex {
- return
- }
-
- // Because APEXes targeting other than system/system_ext partitions
- // can't set apex_available, we skip checks for these APEXes
- if a.SocSpecific() || a.DeviceSpecific() ||
- (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
- return
- }
-
- // Coverage build adds additional dependencies for the coverage-only runtime libraries.
- // Requiring them and their transitive depencies with apex_available is not right
- // because they just add noise.
- if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || a.IsNativeCoverageNeeded(ctx) {
- return
- }
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if externalDep {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- apexName := ctx.ModuleName()
- fromName := ctx.OtherModuleName(from)
- toName := ctx.OtherModuleName(to)
-
- // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
- // do any of its dependencies.
- if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
- return true
- }
- ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'. Dependency path:%s", fromName, toName, ctx.GetPathString(true))
- // Visit this module's dependencies to check and report any issues with their availability.
- return true
- })
-}
-
-func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
- if a.Updatable() {
- if String(a.properties.Min_sdk_version) == "" {
- ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
- }
-
- a.checkJavaStableSdkVersion(ctx)
- }
-}
-
-func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
- if a.testApex || a.vndkApex {
- return
- }
- // Meaningless to check min_sdk_version when building use_vendor modules against non-Trebleized targets
- if proptools.Bool(a.properties.Use_vendor) && ctx.DeviceConfig().VndkVersion() == "" {
- return
- }
- // apexBundle::minSdkVersion reports its own errors.
- minSdkVersion := a.minSdkVersion(ctx)
- android.CheckMinSdkVersion(a, ctx, minSdkVersion)
-}
-
-// Ensures that a lib providing stub isn't statically linked
-func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
- // Practically, we only care about regular APEXes on the device.
- if ctx.Host() || a.testApex || a.vndkApex {
- return
- }
-
- abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
-
- a.WalkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
- if ccm, ok := to.(*cc.Module); ok {
- apexName := ctx.ModuleName()
- fromName := ctx.OtherModuleName(from)
- toName := ctx.OtherModuleName(to)
-
- // If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
- // do any of its dependencies.
- if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
- // As soon as the dependency graph crosses the APEX boundary, don't go further.
- return false
- }
-
- // 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") {
- return false
- }
-
- isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !abInfo.Contents.DirectlyInApex(toName)
- if isStubLibraryFromOtherApex && !externalDep {
- ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
- "It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
- }
-
- }
- return true
- })
-}
-
-func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
- switch a.properties.ApexType {
- case imageApex:
- if buildFlattenedAsDefault {
- a.suffix = imageApexSuffix
- } else {
- a.suffix = ""
- a.primaryApexType = true
-
- if ctx.Config().InstallExtraFlattenedApexes() {
- a.requiredDeps = append(a.requiredDeps, a.Name()+flattenedSuffix)
- }
- }
- case zipApex:
- if proptools.String(a.properties.Payload_type) == "zip" {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = zipApexSuffix
- }
- case flattenedApex:
- if buildFlattenedAsDefault {
- a.suffix = ""
- a.primaryApexType = true
- } else {
- a.suffix = flattenedSuffix
- }
- }
-
- if len(a.properties.Tests) > 0 && !a.testApex {
- ctx.PropertyErrorf("tests", "property not allowed in apex module type")
- return
- }
-
- a.checkApexAvailability(ctx)
- a.checkUpdatable(ctx)
- a.checkMinSdkVersion(ctx)
- a.checkStaticLinkingToStubLibraries(ctx)
-
- handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
-
- // native lib dependencies
- var provideNativeLibs []string
- var requireNativeLibs []string
-
- // Check if "uses" requirements are met with dependent apexBundles
- var providedNativeSharedLibs []string
- useVendor := proptools.Bool(a.properties.Use_vendor)
- ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) {
- if ctx.OtherModuleDependencyTag(m) != usesTag {
- return
- }
- otherName := ctx.OtherModuleName(m)
- other, ok := m.(*apexBundle)
- if !ok {
- ctx.PropertyErrorf("uses", "%q is not a provider", otherName)
- return
- }
- if proptools.Bool(other.properties.Use_vendor) != useVendor {
- ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName)
- return
- }
- if !proptools.Bool(other.properties.Provide_cpp_shared_libs) {
- ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName)
- return
- }
- providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
- })
-
- var filesInfo []apexFile
- // TODO(jiyong) do this using WalkPayloadDeps
- ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
- depTag := ctx.OtherModuleDependencyTag(child)
- if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
- return false
- }
- depName := ctx.OtherModuleName(child)
- if _, isDirectDep := parent.(*apexBundle); isDirectDep {
- switch depTag {
- case sharedLibTag, jniLibTag:
- isJniLib := depTag == jniLibTag
- if c, ok := child.(*cc.Module); ok {
- fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
- fi.isJniLib = isJniLib
- filesInfo = append(filesInfo, fi)
- // Collect the list of stub-providing libs except:
- // - VNDK libs are only for vendors
- // - bootstrap bionic libs are treated as provided by system
- if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
- provideNativeLibs = append(provideNativeLibs, fi.Stem())
- }
- return true // track transitive dependencies
- } else {
- propertyName := "native_shared_libs"
- if isJniLib {
- propertyName = "jni_libs"
- }
- ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
- }
- case executableTag:
- if cc, ok := child.(*cc.Module); ok {
- filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
- return true // track transitive dependencies
- } else if sh, ok := child.(*sh.ShBinary); ok {
- filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
- } else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
- filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
- } else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
- filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
- } else {
- ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
- }
- case javaLibTag:
- switch child.(type) {
- case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
- af := apexFileForJavaLibrary(ctx, child.(javaModule))
- if !af.Ok() {
- ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- default:
- ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
- }
- case androidAppTag:
- if ap, ok := child.(*java.AndroidApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- return true // track transitive dependencies
- } else if ap, ok := child.(*java.AndroidAppImport); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
- filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
- } else if ap, ok := child.(*java.AndroidAppSet); ok {
- appDir := "app"
- if ap.Privileged() {
- appDir = "priv-app"
- }
- af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(),
- filepath.Join(appDir, ap.BaseModuleName()), appSet, ap)
- af.certificate = java.PresignedCertificate
- filesInfo = append(filesInfo, af)
- } else {
- ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
- }
- case rroTag:
- if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
- filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
- } else {
- ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
- }
- case bpfTag:
- if bpfProgram, ok := child.(bpf.BpfModule); ok {
- filesToCopy, _ := bpfProgram.OutputFiles("")
- for _, bpfFile := range filesToCopy {
- filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, bpfProgram))
- }
- } else {
- ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
- }
- case prebuiltTag:
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- } else if prebuilt, ok := child.(java.PlatformCompatConfigIntf); ok {
- filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, prebuilt, depName))
- } else {
- ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc and not a platform_compat_config module", depName)
- }
- case testTag:
- if ccTest, ok := child.(*cc.Module); ok {
- if ccTest.IsTestPerSrcAllTestsVariation() {
- // Multiple-output test module (where `test_per_src: true`).
- //
- // `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
- // We do not add this variation to `filesInfo`, as it has no output;
- // however, we do add the other variations of this module as indirect
- // dependencies (see below).
- } else {
- // Single-output test module (where `test_per_src: false`).
- af := apexFileForExecutable(ctx, ccTest)
- af.class = nativeTest
- filesInfo = append(filesInfo, af)
- }
- return true // track transitive dependencies
- } else {
- ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
- }
- case keyTag:
- if key, ok := child.(*apexKey); ok {
- a.private_key_file = key.private_key_file
- a.public_key_file = key.public_key_file
- } else {
- ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
- }
- return false
- case certificateTag:
- if dep, ok := child.(*java.AndroidAppCertificate); ok {
- a.container_certificate_file = dep.Certificate.Pem
- a.container_private_key_file = dep.Certificate.Key
- } else {
- ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
- }
- case android.PrebuiltDepTag:
- // If the prebuilt is force disabled, remember to delete the prebuilt file
- // that might have been installed in the previous builds
- if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
- a.prebuiltFileToDelete = prebuilt.InstallFilename()
- }
- }
- } else if !a.vndkApex {
- // indirect dependencies
- if am, ok := child.(android.ApexModule); ok {
- // We cannot use a switch statement on `depTag` here as the checked
- // tags used below are private (e.g. `cc.sharedDepTag`).
- if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- if android.InList(cc.Name(), providedNativeSharedLibs) {
- // If we're using a shared library which is provided from other APEX,
- // don't include it in this APEX
- return false
- }
- if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
- requireNativeLibs = append(requireNativeLibs, ":vndk")
- return false
- }
- af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
- af.transitiveDep = true
- abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
- if !a.Host() && !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
- // If the dependency is a stubs lib, don't include it in this APEX,
- // but make sure that the lib is installed on the device.
- // In case no APEX is having the lib, the lib is installed to the system
- // partition.
- //
- // Always include if we are a host-apex however since those won't have any
- // system libraries.
- if !am.DirectlyInAnyApex() {
- // we need a module name for Make
- name := cc.ImplementationModuleName(ctx)
-
- if !proptools.Bool(a.properties.Use_vendor) {
- // we don't use subName(.vendor) for a "use_vendor: true" apex
- // which is supposed to be installed in /system
- name += cc.Properties.SubName
- }
- if !android.InList(name, a.requiredDeps) {
- a.requiredDeps = append(a.requiredDeps, name)
- }
- }
- requireNativeLibs = append(requireNativeLibs, af.Stem())
- // Don't track further
- return false
- }
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if cc.IsTestPerSrcDepTag(depTag) {
- if cc, ok := child.(*cc.Module); ok {
- af := apexFileForExecutable(ctx, cc)
- // Handle modules created as `test_per_src` variations of a single test module:
- // use the name of the generated test binary (`fileToCopy`) instead of the name
- // of the original test module (`depName`, shared by all `test_per_src`
- // variations of that module).
- af.androidMkModuleName = filepath.Base(af.builtFile.String())
- // these are not considered transitive dep
- af.transitiveDep = false
- filesInfo = append(filesInfo, af)
- return true // track transitive dependencies
- }
- } else if java.IsJniDepTag(depTag) {
- // Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
- return false
- } else if java.IsXmlPermissionsFileDepTag(depTag) {
- if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
- filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
- }
- } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
- // nothing
- } else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
- ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
- }
- }
- }
- return false
- })
-
- // Specific to the ART apex: dexpreopt artifacts for libcore Java libraries.
- // Build rules are generated by the dexpreopt singleton, and here we access build artifacts
- // via the global boot image config.
- if a.artApex {
- for arch, files := range java.DexpreoptedArtApexJars(ctx) {
- dirInApex := filepath.Join("javalib", arch.String())
- for _, f := range files {
- localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
- af := newApexFile(ctx, f, localModule, dirInApex, etc, nil)
- filesInfo = append(filesInfo, af)
- }
- }
- }
-
- if a.private_key_file == nil {
- ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
- return
- }
-
- // remove duplicates in filesInfo
- removeDup := func(filesInfo []apexFile) []apexFile {
- encountered := make(map[string]apexFile)
- for _, f := range filesInfo {
- dest := filepath.Join(f.installDir, f.builtFile.Base())
- if e, ok := encountered[dest]; !ok {
- encountered[dest] = f
- } else {
- // If a module is directly included and also transitively depended on
- // consider it as directly included.
- e.transitiveDep = e.transitiveDep && f.transitiveDep
- encountered[dest] = e
- }
- }
- var result []apexFile
- for _, v := range encountered {
- result = append(result, v)
- }
- return result
- }
- filesInfo = removeDup(filesInfo)
-
- // to have consistent build rules
- sort.Slice(filesInfo, func(i, j int) bool {
- return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
- })
-
- a.installDir = android.PathForModuleInstall(ctx, "apex")
- a.filesInfo = filesInfo
-
- switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
- case ext4FsType:
- a.payloadFsType = ext4
- case f2fsFsType:
- a.payloadFsType = f2fs
- default:
- ctx.PropertyErrorf("payload_fs_type", "%q is not a valid filesystem for apex [ext4, f2fs]", *a.properties.Payload_fs_type)
- }
-
- // Optimization. If we are building bundled APEX, for the files that are gathered due to the
- // transitive dependencies, don't place them inside the APEX, but place a symlink pointing
- // the same library in the system partition, thus effectively sharing the same libraries
- // across the APEX boundary. For unbundled APEX, all the gathered files are actually placed
- // in the APEX.
- a.linkToSystemLib = !ctx.Config().UnbundledBuild() &&
- a.installable() &&
- !proptools.Bool(a.properties.Use_vendor)
-
- // APEXes targeting other than system/system_ext partitions use vendor/product variants.
- // So we can't link them to /system/lib libs which are core variants.
- if a.SocSpecific() || a.DeviceSpecific() ||
- (a.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
- a.linkToSystemLib = false
- }
-
- // We don't need the optimization for updatable APEXes, as it might give false signal
- // to the system health when the APEXes are still bundled (b/149805758)
- if a.Updatable() && a.properties.ApexType == imageApex {
- a.linkToSystemLib = false
- }
-
- // We also don't want the optimization for host APEXes, because it doesn't make sense.
- if ctx.Host() {
- a.linkToSystemLib = false
- }
-
- // prepare apex_manifest.json
- a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
-
- a.buildFileContexts(ctx)
-
- a.setCertificateAndPrivateKey(ctx)
- if a.properties.ApexType == flattenedApex {
- a.buildFlattenedApex(ctx)
- } else {
- a.buildUnflattenedApex(ctx)
- }
-
- a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
-
- a.buildApexDependencyInfo(ctx)
-
- a.buildLintReports(ctx)
-}
-
-// Enforce that Java deps of the apex are using stable SDKs to compile
-func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
- // Visit direct deps only. As long as we guarantee top-level deps are using
- // stable SDKs, java's checkLinkType guarantees correct usage for transitive deps
- ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- tag := ctx.OtherModuleDependencyTag(module)
- switch tag {
- case javaLibTag, androidAppTag:
- if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
- if err := m.CheckStableSdkVersion(); err != nil {
- ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
- }
- }
- }
- })
-}
-
-func baselineApexAvailable(apex, moduleName string) bool {
- key := apex
- moduleName = normalizeModuleName(moduleName)
-
- if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
- return true
- }
-
- key = android.AvailableToAnyApex
- if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
- return true
- }
-
- return false
-}
-
-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_")
- 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
- moduleName = "libclang_rt"
- }
- if strings.HasPrefix(moduleName, "androidx.") {
- // TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
- moduleName = "androidx"
- }
- return moduleName
-}
-
-func newApexBundle() *apexBundle {
- module := &apexBundle{}
- module.AddProperties(&module.properties)
- module.AddProperties(&module.targetProperties)
- module.AddProperties(&module.overridableProperties)
- android.InitAndroidMultiTargetsArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
- android.InitDefaultableModule(module)
- android.InitSdkAwareModule(module)
- android.InitOverridableModule(module, &module.overridableProperties.Overrides)
- return module
-}
-
-func ApexBundleFactory(testApex bool, artApex bool) android.Module {
- bundle := newApexBundle()
- bundle.testApex = testApex
- bundle.artApex = artApex
- return bundle
-}
-
-// apex_test is an APEX for testing. The difference from the ordinary apex module type is that
-// certain compatibility checks such as apex_available are not done for apex_test.
-func testApexBundleFactory() android.Module {
- bundle := newApexBundle()
- bundle.testApex = true
- return bundle
-}
-
-// apex packages other modules into an APEX file which is a packaging format for system-level
-// components like binaries, shared libraries, etc.
-func BundleFactory() android.Module {
- return newApexBundle()
-}
-
-//
-// Defaults
-//
-type Defaults struct {
- android.ModuleBase
- android.DefaultsModuleBase
-}
-
-func defaultsFactory() android.Module {
- return DefaultsFactory()
-}
-
-func DefaultsFactory(props ...interface{}) android.Module {
- module := &Defaults{}
-
- module.AddProperties(props...)
- module.AddProperties(
- &apexBundleProperties{},
- &apexTargetBundleProperties{},
- &overridableProperties{},
- )
-
- android.InitDefaultsModule(module)
- return module
-}
-
-//
-// OverrideApex
-//
-type OverrideApex struct {
- android.ModuleBase
- android.OverrideModuleBase
-}
-
-func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- // All the overrides happen in the base module.
-}
-
-// override_apex is used to create an apex module based on another apex module
-// by overriding some of its properties.
-func overrideApexFactory() android.Module {
- m := &OverrideApex{}
- m.AddProperties(&overridableProperties{})
-
- android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
- android.InitOverrideModule(m)
- return m
-}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8b1f40d..4a6aecf 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -15,6 +15,7 @@
package apex
import (
+ "fmt"
"io/ioutil"
"os"
"path"
@@ -32,6 +33,7 @@
"android/soong/dexpreopt"
prebuilt_etc "android/soong/etc"
"android/soong/java"
+ "android/soong/rust"
"android/soong/sh"
)
@@ -136,6 +138,8 @@
bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+ bp = bp + rust.GatherRequiredDepsForTest()
+
bp = bp + java.GatherRequiredDepsForTest()
fs := map[string][]byte{
@@ -149,10 +153,8 @@
"system/sepolicy/apex/myapex.updatable-file_contexts": nil,
"system/sepolicy/apex/myapex2-file_contexts": nil,
"system/sepolicy/apex/otherapex-file_contexts": nil,
- "system/sepolicy/apex/commonapex-file_contexts": nil,
"system/sepolicy/apex/com.android.vndk-file_contexts": nil,
"mylib.cpp": nil,
- "mylib_common.cpp": nil,
"mytest.cpp": nil,
"mytest1.cpp": nil,
"mytest2.cpp": nil,
@@ -187,6 +189,7 @@
"bar/baz": nil,
"testdata/baz": nil,
"AppSet.apks": nil,
+ "foo.rs": nil,
}
cc.GatherRequiredFilesForTest(fs)
@@ -216,7 +219,7 @@
handler(tempFS, config)
}
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
// from android package
android.RegisterPackageBuildComponents(ctx)
@@ -243,6 +246,7 @@
ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
cc.RegisterRequiredBuildComponentsForTest(ctx)
+ rust.RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterModuleType("cc_test", cc.TestFactory)
ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -261,7 +265,7 @@
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
- ctx.Register(config)
+ ctx.Register()
return ctx, config
}
@@ -343,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, `
@@ -351,10 +362,15 @@
manifest: ":myapex.manifest",
androidManifest: ":myapex.androidmanifest",
key: "myapex.key",
- native_shared_libs: ["mylib"],
+ binaries: ["foo.rust"],
+ native_shared_libs: [
+ "mylib",
+ "libfoo.ffi",
+ ],
+ rust_dyn_libs: ["libfoo.dylib.rust"],
multilib: {
both: {
- binaries: ["foo",],
+ binaries: ["foo"],
}
},
java_libs: [
@@ -387,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
@@ -417,6 +436,42 @@
apex_available: [ "myapex", "com.android.gki.*" ],
}
+ rust_binary {
+ name: "foo.rust",
+ srcs: ["foo.rs"],
+ rlibs: ["libfoo.rlib.rust"],
+ dylibs: ["libfoo.dylib.rust"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library_rlib {
+ name: "libfoo.rlib.rust",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ apex_available: ["myapex"],
+ }
+
+ rust_library_dylib {
+ name: "libfoo.dylib.rust",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ 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"],
@@ -531,16 +586,24 @@
ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
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")
ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
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")
@@ -1751,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) {
@@ -2349,6 +2437,7 @@
enabled: true,
},
vendor_available: true,
+ product_available: true,
}
cc_library {
name: "libvendor",
@@ -2799,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
@@ -2832,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.
@@ -2856,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) {
@@ -3022,6 +3111,7 @@
name: "libvndk",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3034,6 +3124,7 @@
name: "libvndksp",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -3075,6 +3166,7 @@
name: "libvndk",
srcs: ["libvndk.so"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3087,6 +3179,7 @@
name: "libvndk.arm",
srcs: ["libvndk.arm.so"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3159,6 +3252,7 @@
name: "libvndk27",
version: "27",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3178,6 +3272,7 @@
name: "libvndk27",
version: "27",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3231,6 +3326,7 @@
name: "libvndk",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3242,6 +3338,7 @@
name: "libvndk",
version: "27",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3301,6 +3398,7 @@
name: "libvndk",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
native_bridge_supported: true,
host_supported: true,
vndk: {
@@ -3340,6 +3438,7 @@
name: "libvndk",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
native_bridge_supported: true,
host_supported: true,
vndk: {
@@ -3371,6 +3470,7 @@
version: "27",
target_arch: "arm",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3387,6 +3487,7 @@
target_arch: "arm",
binder32bit: true,
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3434,6 +3535,7 @@
cc_library {
name: "libz",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -3789,6 +3891,64 @@
ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
}
+func TestApexWithArch(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ arch: {
+ arm64: {
+ native_shared_libs: ["mylib.arm64"],
+ },
+ x86_64: {
+ native_shared_libs: ["mylib.x64"],
+ },
+ }
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib.arm64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+
+ cc_library {
+ name: "mylib.x64",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ // TODO: remove //apex_available:platform
+ apex_available: [
+ "//apex_available:platform",
+ "myapex",
+ ],
+ }
+ `)
+
+ apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+ copyCmds := apexRule.Args["copy_commands"]
+
+ // Ensure that apex variant is created for the direct dep
+ ensureListContains(t, ctx.ModuleVariantsForTests("mylib.arm64"), "android_arm64_armv8-a_shared_apex10000")
+ ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.x64"), "android_arm64_armv8-a_shared_apex10000")
+
+ // Ensure that both direct and indirect deps are copied into apex
+ ensureContains(t, copyCmds, "image.apex/lib64/mylib.arm64.so")
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib.x64.so")
+}
+
func TestApexWithShBinary(t *testing.T) {
ctx, _ := testApex(t, `
apex {
@@ -4188,131 +4348,6 @@
ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += myapex.flattened")
}
-func TestApexUsesOtherApex(t *testing.T) {
- ctx, _ := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["mylib"],
- uses: ["commonapex"],
- }
-
- apex {
- name: "commonapex",
- key: "myapex.key",
- native_shared_libs: ["libcommon"],
- provide_cpp_shared_libs: true,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- shared_libs: ["libcommon"],
- system_shared_libs: [],
- stl: "none",
- apex_available: [ "myapex" ],
- }
-
- cc_library {
- name: "libcommon",
- srcs: ["mylib_common.cpp"],
- system_shared_libs: [],
- stl: "none",
- // TODO: remove //apex_available:platform
- apex_available: [
- "//apex_available:platform",
- "commonapex",
- "myapex",
- ],
- }
- `)
-
- module1 := ctx.ModuleForTests("myapex", "android_common_myapex_image")
- apexRule1 := module1.Rule("apexRule")
- copyCmds1 := apexRule1.Args["copy_commands"]
-
- module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex_image")
- apexRule2 := module2.Rule("apexRule")
- copyCmds2 := apexRule2.Args["copy_commands"]
-
- ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
- ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_shared_apex10000")
- ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
- ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
- ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
-}
-
-func TestApexUsesFailsIfNotProvided(t *testing.T) {
- testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- uses: ["commonapex"],
- }
-
- apex {
- name: "commonapex",
- key: "myapex.key",
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
- `)
- testApexError(t, `uses: "commonapex" is not a provider`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- uses: ["commonapex"],
- }
-
- cc_library {
- name: "commonapex",
- system_shared_libs: [],
- stl: "none",
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
- `)
-}
-
-func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) {
- testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, `
- apex {
- name: "myapex",
- key: "myapex.key",
- use_vendor: true,
- uses: ["commonapex"],
- }
-
- apex {
- name: "commonapex",
- key: "myapex.key",
- provide_cpp_shared_libs: true,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
- `, func(fs map[string][]byte, config android.Config) {
- setUseVendorAllowListForTest(config, []string{"myapex"})
- })
-}
-
func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, `
apex {
@@ -5560,7 +5595,7 @@
}
`, withManifestPackageNameOverrides([]string{"AppFoo:com.android.foo"}))
- bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Description("Bundle Config")
+ bundleConfigRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("bundle_config.json")
content := bundleConfigRule.Args["content"]
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
@@ -5586,7 +5621,7 @@
set: "AppSet.apks",
}`)
mod := ctx.ModuleForTests("myapex", "android_common_myapex_image")
- bundleConfigRule := mod.Description("Bundle Config")
+ bundleConfigRule := mod.Output("bundle_config.json")
content := bundleConfigRule.Args["content"]
ensureContains(t, content, `"compression":{"uncompressed_glob":["apex_payload.img","apex_manifest.*"]}`)
s := mod.Rule("apexRule").Args["copy_commands"]
@@ -5715,6 +5750,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()
@@ -5732,7 +5774,9 @@
}
cc.GatherRequiredFilesForTest(fs)
- ctx := android.NewTestArchContext()
+ config := android.TestArchConfig(buildDir, nil, bp, fs)
+
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("apex", BundleFactory)
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
@@ -5747,8 +5791,7 @@
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx.Register(config)
+ ctx.Register()
_ = dexpreopt.GlobalSoongConfigForTests(config)
dexpreopt.RegisterToolModulesForTest(ctx)
@@ -5883,7 +5926,15 @@
"system/sepolicy/apex/myapex-file_contexts": nil,
}
- ctx := android.NewTestArchContext()
+ config := android.TestArchConfig(buildDir, nil, bp, fs)
+ android.SetTestNeverallowRules(config, rules)
+ updatableBootJars := make([]string, 0, len(apexBootJars))
+ for _, apexBootJar := range apexBootJars {
+ updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+ }
+ config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("apex", BundleFactory)
ctx.RegisterModuleType("apex_key", ApexKeyFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
@@ -5896,15 +5947,7 @@
ctx.PostDepsMutators(RegisterPostDepsMutators)
ctx.PostDepsMutators(android.RegisterNeverallowMutator)
- config := android.TestArchConfig(buildDir, nil, bp, fs)
- android.SetTestNeverallowRules(config, rules)
- updatableBootJars := make([]string, 0, len(apexBootJars))
- for _, apexBootJar := range apexBootJars {
- updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
- }
- config.TestProductVariables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
-
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
android.FailIfErrored(t, errs)
@@ -6043,9 +6086,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
@@ -6053,6 +6114,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
@@ -6265,6 +6336,299 @@
`)
}
+func TestCompressedApex(t *testing.T) {
+ ctx, config := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ compressible: true,
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ `, func(fs map[string][]byte, config android.Config) {
+ config.TestProductVariables.CompressedApex = proptools.BoolPtr(true)
+ })
+
+ compressRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("compressRule")
+ ensureContains(t, compressRule.Output.String(), "myapex.capex.unsigned")
+
+ signApkRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Description("sign compressedApex")
+ ensureEquals(t, signApkRule.Input.String(), compressRule.Output.String())
+
+ // Make sure output of bundle is .capex
+ ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ ensureContains(t, ab.outputFile.String(), "myapex.capex")
+
+ // Verify android.mk rules
+ data := android.AndroidMkDataForTest(t, config, "", ab)
+ var builder strings.Builder
+ data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+ ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n")
+}
+
+func TestPreferredPrebuiltSharedLibDep(t *testing.T) {
+ ctx, config := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["myapex"],
+ shared_libs: ["otherlib"],
+ system_shared_libs: [],
+ }
+
+ cc_library {
+ name: "otherlib",
+ srcs: ["mylib.cpp"],
+ stubs: {
+ versions: ["current"],
+ },
+ }
+
+ cc_prebuilt_library_shared {
+ name: "otherlib",
+ prefer: true,
+ srcs: ["prebuilt.so"],
+ stubs: {
+ versions: ["current"],
+ },
+ }
+ `)
+
+ ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+ data := android.AndroidMkDataForTest(t, config, "", ab)
+ var builder strings.Builder
+ data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data)
+ androidMk := builder.String()
+
+ // The make level dependency needs to be on otherlib - prebuilt_otherlib isn't
+ // a thing there.
+ ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherlib\n")
+}
+
+func TestExcludeDependency(t *testing.T) {
+ ctx, _ := testApex(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ apex_available: ["myapex"],
+ shared_libs: ["mylib2"],
+ target: {
+ apex: {
+ exclude_shared_libs: ["mylib2"],
+ },
+ },
+ }
+
+ cc_library {
+ name: "mylib2",
+ srcs: ["mylib.cpp"],
+ system_shared_libs: [],
+ stl: "none",
+ }
+ `)
+
+ // Check if mylib is linked to mylib2 for the non-apex target
+ ldFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
+ ensureContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+
+ // Make sure that the link doesn't occur for the apex target
+ ldFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
+ ensureNotContains(t, ldFlags, "mylib2/android_arm64_armv8-a_shared_apex10000/mylib2.so")
+
+ // It shouldn't appear in the copy cmd as well.
+ copyCmds := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule").Args["copy_commands"]
+ ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+}
+
+func TestPrebuiltStubLibDep(t *testing.T) {
+ bpBase := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ }
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+ cc_library {
+ name: "mylib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["myapex"],
+ shared_libs: ["stublib"],
+ system_shared_libs: [],
+ }
+ apex {
+ name: "otherapex",
+ enabled: %s,
+ key: "myapex.key",
+ native_shared_libs: ["stublib"],
+ }
+ `
+
+ stublibSourceBp := `
+ cc_library {
+ name: "stublib",
+ srcs: ["mylib.cpp"],
+ apex_available: ["otherapex"],
+ system_shared_libs: [],
+ stl: "none",
+ stubs: {
+ versions: ["1"],
+ },
+ }
+ `
+
+ stublibPrebuiltBp := `
+ cc_prebuilt_library_shared {
+ name: "stublib",
+ srcs: ["prebuilt.so"],
+ apex_available: ["otherapex"],
+ stubs: {
+ versions: ["1"],
+ },
+ %s
+ }
+ `
+
+ tests := []struct {
+ name string
+ stublibBp string
+ usePrebuilt bool
+ modNames []string // Modules to collect AndroidMkEntries for
+ otherApexEnabled []string
+ }{
+ {
+ name: "only_source",
+ stublibBp: stublibSourceBp,
+ usePrebuilt: false,
+ modNames: []string{"stublib"},
+ otherApexEnabled: []string{"true", "false"},
+ },
+ {
+ name: "source_preferred",
+ stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, ""),
+ usePrebuilt: false,
+ modNames: []string{"stublib", "prebuilt_stublib"},
+ otherApexEnabled: []string{"true", "false"},
+ },
+ {
+ name: "prebuilt_preferred",
+ stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, "prefer: true,"),
+ usePrebuilt: true,
+ modNames: []string{"stublib", "prebuilt_stublib"},
+ otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+ },
+ {
+ name: "only_prebuilt",
+ stublibBp: fmt.Sprintf(stublibPrebuiltBp, ""),
+ usePrebuilt: true,
+ modNames: []string{"stublib"},
+ otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt.
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ for _, otherApexEnabled := range test.otherApexEnabled {
+ t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) {
+ ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp)
+
+ type modAndMkEntries struct {
+ mod *cc.Module
+ mkEntries android.AndroidMkEntries
+ }
+ entries := []*modAndMkEntries{}
+
+ // Gather shared lib modules that are installable
+ for _, modName := range test.modNames {
+ for _, variant := range ctx.ModuleVariantsForTests(modName) {
+ if !strings.HasPrefix(variant, "android_arm64_armv8-a_shared") {
+ continue
+ }
+ mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
+ if !mod.Enabled() || mod.IsHideFromMake() {
+ continue
+ }
+ for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
+ if ent.Disabled {
+ continue
+ }
+ entries = append(entries, &modAndMkEntries{
+ mod: mod,
+ mkEntries: ent,
+ })
+ }
+ }
+ }
+
+ var entry *modAndMkEntries = nil
+ for _, ent := range entries {
+ if strings.Join(ent.mkEntries.EntryMap["LOCAL_MODULE"], ",") == "stublib" {
+ if entry != nil {
+ t.Errorf("More than one AndroidMk entry for \"stublib\": %s and %s", entry.mod, ent.mod)
+ } else {
+ entry = ent
+ }
+ }
+ }
+
+ if entry == nil {
+ t.Errorf("AndroidMk entry for \"stublib\" missing")
+ } else {
+ isPrebuilt := entry.mod.Prebuilt() != nil
+ if isPrebuilt != test.usePrebuilt {
+ t.Errorf("Wrong module for \"stublib\" AndroidMk entry: got prebuilt %t, want prebuilt %t", isPrebuilt, test.usePrebuilt)
+ }
+ if !entry.mod.IsStubs() {
+ t.Errorf("Module for \"stublib\" AndroidMk entry isn't a stub: %s", entry.mod)
+ }
+ if entry.mkEntries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"] != nil {
+ t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries)
+ }
+ cflags := entry.mkEntries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ expected := "-D__STUBLIB_API__=1"
+ if !android.InList(expected, cflags) {
+ t.Errorf("LOCAL_EXPORT_CFLAGS expected to have %q, but got %q", expected, cflags)
+ }
+ }
+ })
+ }
+ })
+ }
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/apex/builder.go b/apex/builder.go
index ad673d6..9db8e59 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -37,10 +37,11 @@
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"
- // projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead.
+ // projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
if !ctx.Config().FrameworksBaseDirExists(ctx) {
@@ -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 (
@@ -175,37 +178,49 @@
`exit 1); touch ${out}`,
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.
)
+// buildManifest creates buile rules to modify the input apex_manifest.json to add information
+// gathered by the build system such as provided/required native libraries. Two output files having
+// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and
+// a.manifest.PbOut is protobuf format for R+ devices.
+// TODO(jiyong): make this to return paths instead of directly storing the paths to apexBundle
func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
- manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+ src := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
- manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
-
- // put dependency({provide|require}NativeLibs) in apex_manifest.json
+ // Put dependency({provide|require}NativeLibs) in apex_manifest.json
provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
- // apex name can be overridden
+ // APEX name can be overridden
optCommands := []string{}
if a.properties.Apex_name != nil {
optCommands = append(optCommands, "-v name "+*a.properties.Apex_name)
}
- // collect jniLibs. Notice that a.filesInfo is already sorted
+ // Collect jniLibs. Notice that a.filesInfo is already sorted
var jniLibs []string
for _, fi := range a.filesInfo {
- if fi.isJniLib && !android.InList(fi.Stem(), jniLibs) {
- jniLibs = append(jniLibs, fi.Stem())
+ if fi.isJniLib && !android.InList(fi.stem(), jniLibs) {
+ jniLibs = append(jniLibs, fi.stem())
}
}
if len(jniLibs) > 0 {
optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
}
+ manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
ctx.Build(pctx, android.BuildParams{
Rule: apexManifestRule,
- Input: manifestSrc,
+ Input: src,
Output: manifestJsonFullOut,
Args: map[string]string{
"provideNativeLibs": strings.Join(provideNativeLibs, " "),
@@ -214,10 +229,10 @@
},
})
+ // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json prepare
+ // stripped-down version so that APEX modules built from R+ can be installed to Q
minSdkVersion := a.minSdkVersion(ctx)
if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
- // b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
- // prepare stripped-down version so that APEX modules built from R+ can be installed to Q
a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
ctx.Build(pctx, android.BuildParams{
Rule: stripApexManifestRule,
@@ -226,7 +241,7 @@
})
}
- // from R+, protobuf binary format (.pb) is the standard format for apex_manifest
+ // From R+, protobuf binary format (.pb) is the standard format for apex_manifest
a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
ctx.Build(pctx, android.BuildParams{
Rule: pbApexManifestRule,
@@ -235,10 +250,11 @@
})
}
-func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) {
- if a.properties.ApexType == zipApex {
- return
- }
+// buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts
+// file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from
+// the file_contexts property of this APEX. This is to make sure that the manifest file is correctly
+// labeled as system_file.
+func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
var fileContexts android.Path
if a.properties.File_contexts == nil {
fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
@@ -248,18 +264,17 @@
if a.Platform() {
if matched, err := path.Match("system/sepolicy/**/*", fileContexts.String()); err != nil || !matched {
ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but %q", fileContexts)
- return
}
}
if !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
- ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", a.fileContexts)
- return
+ ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
}
output := android.PathForModuleOut(ctx, "file_contexts")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
- if a.properties.ApexType == imageApex {
+ switch a.properties.ApexType {
+ case imageApex:
// remove old file
rule.Command().Text("rm").FlagWithOutput("-f ", output)
// copy file_contexts
@@ -269,7 +284,7 @@
// force-label /apex_manifest.pb and / as system_file so that apexd can read them
rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
- } else {
+ case flattenedApex:
// For flattened apexes, install path should be prepended.
// File_contexts file should be emiited to make via LOCAL_FILE_CONTEXTS
// so that it can be merged into file_contexts.bin
@@ -284,13 +299,16 @@
// force-label /apex_manifest.pb and / as system_file so that apexd can read them
rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+ default:
+ panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
}
- rule.Build(pctx, ctx, "file_contexts."+a.Name(), "Generate file_contexts")
-
- a.fileContexts = output.OutputPath
+ rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
+ return output.OutputPath
}
+// buildNoticeFiles creates a buile rule for aggregating notice files from the modules that
+// contributes to this APEX. The notice files are merged into a big notice file.
func (a *apexBundle) buildNoticeFiles(ctx android.ModuleContext, apexFileName string) android.NoticeOutputs {
var noticeFiles android.Paths
@@ -299,13 +317,11 @@
// As soon as the dependency graph crosses the APEX boundary, don't go further.
return false
}
-
- notices := to.NoticeFiles()
- noticeFiles = append(noticeFiles, notices...)
-
+ noticeFiles = append(noticeFiles, to.NoticeFiles()...)
return true
})
+ // TODO(jiyong): why do we need this? WalkPayloadDeps should have already covered this.
for _, fi := range a.filesInfo {
noticeFiles = append(noticeFiles, fi.noticeFiles...)
}
@@ -317,19 +333,24 @@
return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles))
}
+// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
+// files included in this APEX is shown. The text file is dist'ed so that people can see what's
+// 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
}
+// buildBundleConfig creates a build rule for the bundle config file that will control the bundle
+// creation process.
func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.OutputPath {
output := android.PathForModuleOut(ctx, "bundle_config.json")
@@ -351,8 +372,8 @@
"apex_manifest.*",
}
- // collect the manifest names and paths of android apps
- // if their manifest names are overridden
+ // Collect the manifest names and paths of android apps if their manifest names are
+ // overridden.
for _, fi := range a.filesInfo {
if fi.class != app && fi.class != appSet {
continue
@@ -363,7 +384,7 @@
config.Apex_config.Apex_embedded_apk_config,
ApkConfig{
Package_name: packageName,
- Apk_path: fi.Path(),
+ Apk_path: fi.path(),
})
}
}
@@ -373,45 +394,39 @@
panic(fmt.Errorf("error while marshalling to %q: %#v", output, err))
}
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: output,
- Description: "Bundle Config " + output.String(),
- Args: map[string]string{
- "content": string(j),
- },
- })
+ android.WriteFileRule(ctx, output, string(j))
return output.OutputPath
}
+// buildUnflattendApex creates build rules to build an APEX using apexer.
func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
- var abis []string
- for _, target := range ctx.MultiTargets() {
- if len(target.Arch.Abi) > 0 {
- abis = append(abis, target.Arch.Abi[0])
- }
- }
-
- abis = android.FirstUniqueStrings(abis)
-
apexType := a.properties.ApexType
suffix := apexType.suffix()
- var implicitInputs []android.Path
- unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
- // TODO(jiyong): construct the copy rules using RuleBuilder
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Step 1: copy built files to appropriate directories under the image directory
+
+ imageDir := android.PathForModuleOut(ctx, "image"+suffix)
+
+ // TODO(jiyong): use the RuleBuilder
var copyCommands []string
+ var implicitInputs []android.Path
for _, fi := range a.filesInfo {
- destPath := android.PathForModuleOut(ctx, "image"+suffix, fi.Path()).String()
+ destPath := imageDir.Join(ctx, fi.path()).String()
+
+ // Prepare the destination path
destPathDir := filepath.Dir(destPath)
if fi.class == appSet {
copyCommands = append(copyCommands, "rm -rf "+destPathDir)
}
copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
- if a.linkToSystemLib && fi.transitiveDep && fi.AvailableToPlatform() {
+
+ // Copy the built file to the directory. But if the symlink optimization is turned
+ // on, place a symlink to the corresponding file in /system partition instead.
+ if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
// TODO(jiyong): pathOnDevice should come from fi.module, not being calculated here
- pathOnDevice := filepath.Join("/system", fi.Path())
+ pathOnDevice := filepath.Join("/system", fi.path())
copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
} else {
if fi.class == appSet {
@@ -422,11 +437,15 @@
}
implicitInputs = append(implicitInputs, fi.builtFile)
}
- // create additional symlinks pointing the file inside the APEX
- for _, symlinkPath := range fi.SymlinkPaths() {
- symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
+
+ // Create additional symlinks pointing the file inside the APEX (if any). Note that
+ // this is independent from the symlink optimization.
+ for _, symlinkPath := range fi.symlinkPaths() {
+ symlinkDest := imageDir.Join(ctx, symlinkPath).String()
copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
}
+
+ // Copy the test files (if any)
for _, d := range fi.dataPaths {
// TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
relPath := d.SrcPath.Rel()
@@ -435,28 +454,37 @@
panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath))
}
- dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath), d.RelativeInstallPath).String()
+ dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath), d.RelativeInstallPath).String()
copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest)
implicitInputs = append(implicitInputs, d.SrcPath)
}
}
-
- // TODO(jiyong): use RuleBuilder
- var emitCommands []string
- imageContentFile := android.PathForModuleOut(ctx, "content.txt")
- emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
- minSdkVersion := a.minSdkVersion(ctx)
- if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
- emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
- }
- for _, fi := range a.filesInfo {
- emitCommands = append(emitCommands, "echo './"+fi.Path()+"' >> "+imageContentFile.String())
- }
- emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
implicitInputs = append(implicitInputs, a.manifestPbOut)
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Step 1.a: Write the list of files in this APEX to a txt file and compare it against
+ // the allowed list given via the allowed_files property. Build fails when the two lists
+ // differ.
+ //
+ // TODO(jiyong): consider removing this. Nobody other than com.android.apex.cts.shim.* seems
+ // to be using this at this moment. Furthermore, this looks very similar to what
+ // buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't
+ // hurt readability.
+ // TODO(jiyong): use RuleBuilder
if a.overridableProperties.Allowed_files != nil {
+ // Build content.txt
+ var emitCommands []string
+ imageContentFile := android.PathForModuleOut(ctx, "content.txt")
+ emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
+ minSdkVersion := a.minSdkVersion(ctx)
+ if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
+ emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
+ }
+ for _, fi := range a.filesInfo {
+ emitCommands = append(emitCommands, "echo './"+fi.path()+"' >> "+imageContentFile.String())
+ }
+ emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
ctx.Build(pctx, android.BuildParams{
Rule: emitApexContentRule,
Implicits: implicitInputs,
@@ -467,8 +495,9 @@
},
})
implicitInputs = append(implicitInputs, imageContentFile)
- allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files))
+ // Compare content.txt against allowed_files.
+ allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files))
phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output")
ctx.Build(pctx, android.BuildParams{
Rule: diffApexContentRule,
@@ -481,22 +510,25 @@
"apex_module_name": a.Name(),
},
})
-
implicitInputs = append(implicitInputs, phonyOutput)
}
+ unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
- imageDir := android.PathForModuleOut(ctx, "image"+suffix)
if apexType == imageApex {
- // files and dirs that will be created in APEX
+ ////////////////////////////////////////////////////////////////////////////////////
+ // Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
+ // in this APEX. The file will be used by apexer in later steps.
+ // TODO(jiyong): make this as a function
+ // TODO(jiyong): use the RuleBuilder
var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
var executablePaths []string // this also includes dirs
var extractedAppSetPaths android.Paths
var extractedAppSetDirs []string
for _, f := range a.filesInfo {
- pathInApex := f.Path()
+ pathInApex := f.path()
if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
executablePaths = append(executablePaths, pathInApex)
for _, d := range f.dataPaths {
@@ -535,11 +567,17 @@
"apk_paths": strings.Join(extractedAppSetDirs, " "),
},
})
+ implicitInputs = append(implicitInputs, cannedFsConfig)
+ ////////////////////////////////////////////////////////////////////////////////////
+ // Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX.
+ // TODO(jiyong): use the RuleBuilder
optFlags := []string{}
- // Additional implicit inputs.
- implicitInputs = append(implicitInputs, cannedFsConfig, a.fileContexts, a.private_key_file, a.public_key_file)
+ fileContexts := a.buildFileContexts(ctx)
+ implicitInputs = append(implicitInputs, fileContexts)
+
+ implicitInputs = append(implicitInputs, a.private_key_file, a.public_key_file)
optFlags = append(optFlags, "--pubkey "+a.public_key_file.String())
manifestPackageName := a.getOverrideManifestPackageName(ctx)
@@ -553,15 +591,18 @@
optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
}
+ // Determine target/min sdk version from the context
+ // TODO(jiyong): make this as a function
moduleMinSdkVersion := a.minSdkVersion(ctx)
minSdkVersion := moduleMinSdkVersion.String()
- // bundletool doesn't understand what "current" is. We need to transform it to codename
+ // bundletool doesn't understand what "current" is. We need to transform it to
+ // codename
if moduleMinSdkVersion.IsCurrent() {
minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
}
- // apex module doesn't have a concept of target_sdk_version, hence for the time being
- // targetSdkVersion == default targetSdkVersion of the branch.
+ // apex module doesn't have a concept of target_sdk_version, hence for the time
+ // being targetSdkVersion == default targetSdkVersion of the branch.
targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt())
if java.UseApiFingerprint(ctx) {
@@ -602,8 +643,8 @@
}
if a.properties.Apex_name != nil {
- // If apex_name is set, apexer can skip checking if key name matches with apex name.
- // Note that apex_manifest is also mended.
+ // If apex_name is set, apexer can skip checking if key name matches with
+ // apex name. Note that apex_manifest is also mended.
optFlags = append(optFlags, "--do_not_check_keyname")
}
@@ -624,13 +665,14 @@
"image_dir": imageDir.String(),
"copy_commands": strings.Join(copyCommands, " && "),
"manifest": a.manifestPbOut.String(),
- "file_contexts": a.fileContexts.String(),
+ "file_contexts": fileContexts.String(),
"canned_fs_config": cannedFsConfig.String(),
"key": a.private_key_file.String(),
"opt_flags": strings.Join(optFlags, " "),
},
})
+ // TODO(jiyong): make the two rules below as separate functions
apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix)
bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip")
a.bundleModuleFile = bundleModuleFile
@@ -642,8 +684,33 @@
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
+ for _, target := range ctx.MultiTargets() {
+ if len(target.Arch.Abi) > 0 {
+ abis = append(abis, target.Arch.Abi[0])
+ }
+ }
+
+ abis = android.FirstUniqueStrings(abis)
+
ctx.Build(pctx, android.BuildParams{
Rule: apexBundleRule,
Input: apexProtoFile,
@@ -655,7 +722,7 @@
"config": bundleConfig.String(),
},
})
- } else {
+ } else { // zipApex
ctx.Build(pctx, android.BuildParams{
Rule: zipApexRule,
Implicits: implicitInputs,
@@ -670,92 +737,124 @@
})
}
- a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix)
+ ////////////////////////////////////////////////////////////////////////////////////
+ // Step 4: Sign the APEX using signapk
+ signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix)
+
+ pem, key := a.getCertificateAndPrivateKey(ctx)
rule := java.Signapk
args := map[string]string{
- "certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
+ "certificates": pem.String() + " " + key.String(),
"flags": "-a 4096", //alignment
}
- implicits := android.Paths{
- a.container_certificate_file,
- a.container_private_key_file,
- }
+ implicits := android.Paths{pem, key}
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() {
ctx.InstallFile(a.installDir, a.Name()+suffix, a.outputFile)
}
- a.buildFilesInfo(ctx)
// installed-files.txt is dist'ed
a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
}
+// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
+type flattenedApexContext struct {
+ android.ModuleContext
+}
+
+func (c *flattenedApexContext) InstallBypassMake() bool {
+ return true
+}
+
+// buildFlattenedApex creates rules for a flattened APEX. Flattened APEX actually doesn't have a
+// single output file. It is a phony target for all the files under /system/apex/<name> directory.
+// This function creates the installation rules for the files.
func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
- // Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it
- // reply true to `InstallBypassMake()` (thus making the call
- // `android.PathForModuleInstall` below use `android.pathForInstallInMakeDir`
- // instead of `android.PathForOutput`) to return the correct path to the flattened
- // APEX (as its contents is installed by Make, not Soong).
- factx := flattenedApexContext{ctx}
- a.outputFile = android.PathForModuleInstall(&factx, "apex", a.Name())
- a.buildFilesInfo(ctx)
-}
-
-func (a *apexBundle) setCertificateAndPrivateKey(ctx android.ModuleContext) {
- if a.container_certificate_file == nil {
- cert := String(a.properties.Certificate)
- if cert == "" {
- pem, key := ctx.Config().DefaultAppCertificate(ctx)
- a.container_certificate_file = pem
- a.container_private_key_file = key
- } else {
- defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
- a.container_certificate_file = defaultDir.Join(ctx, cert+".x509.pem")
- a.container_private_key_file = defaultDir.Join(ctx, cert+".pk8")
- }
- }
-}
-
-func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
+ bundleName := a.Name()
if a.installable() {
- // For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
- // with other ordinary files.
- a.filesInfo = append(a.filesInfo, newApexFile(ctx, a.manifestPbOut, "apex_manifest.pb", ".", etc, nil))
-
- // rename to apex_pubkey
- copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
- ctx.Build(pctx, android.BuildParams{
- Rule: android.Cp,
- Input: a.public_key_file,
- Output: copiedPubkey,
- })
- a.filesInfo = append(a.filesInfo, newApexFile(ctx, copiedPubkey, "apex_pubkey", ".", etc, nil))
-
- if a.properties.ApexType == flattenedApex {
- apexBundleName := a.Name()
- for _, fi := range a.filesInfo {
- dir := filepath.Join("apex", apexBundleName, fi.installDir)
- target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.Stem(), fi.builtFile)
- for _, sym := range fi.symlinks {
- ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
- }
+ for _, fi := range a.filesInfo {
+ dir := filepath.Join("apex", bundleName, fi.installDir)
+ target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.stem(), fi.builtFile)
+ for _, sym := range fi.symlinks {
+ ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
}
}
}
+
+ a.fileContexts = a.buildFileContexts(ctx)
+
+ // Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it reply true
+ // to `InstallBypassMake()` (thus making the call `android.PathForModuleInstall` below use
+ // `android.pathForInstallInMakeDir` instead of `android.PathForOutput`) to return the
+ // correct path to the flattened APEX (as its contents is installed by Make, not Soong).
+ // TODO(jiyong): Why do we need to set outputFile for flattened APEX? We don't seem to use
+ // it and it actually points to a path that can never be built. Remove this.
+ factx := flattenedApexContext{ctx}
+ a.outputFile = android.PathForModuleInstall(&factx, "apex", bundleName)
+}
+
+// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
+// the zip container of this APEX. See the description of the 'certificate' property for how
+// the cert and the private key are found.
+func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) {
+ if a.container_certificate_file != nil {
+ return a.container_certificate_file, a.container_private_key_file
+ }
+
+ cert := String(a.properties.Certificate)
+ if cert == "" {
+ return ctx.Config().DefaultAppCertificate(ctx)
+ }
+
+ defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+ pem = defaultDir.Join(ctx, cert+".x509.pem")
+ key = defaultDir.Join(ctx, cert+".pk8")
+ return pem, key
}
func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string {
diff --git a/apex/key.go b/apex/key.go
index d2d5786..d9e3c10 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -116,7 +116,7 @@
partition string
}
toString := func(e apexKeyEntry) string {
- format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\\n"
+ format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
if e.presigned {
return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
} else {
@@ -127,13 +127,14 @@
apexKeyMap := make(map[string]apexKeyEntry)
ctx.VisitAllModules(func(module android.Module) {
if m, ok := module.(*apexBundle); ok && m.Enabled() && m.installable() {
+ pem, key := m.getCertificateAndPrivateKey(ctx)
apexKeyMap[m.Name()] = apexKeyEntry{
name: m.Name() + ".apex",
presigned: false,
public_key: m.public_key_file.String(),
private_key: m.private_key_file.String(),
- container_certificate: m.container_certificate_file.String(),
- container_private_key: m.container_private_key_file.String(),
+ container_certificate: pem.String(),
+ container_private_key: key.String(),
partition: m.PartitionTag(ctx.DeviceConfig()),
}
}
@@ -173,17 +174,9 @@
var filecontent strings.Builder
for _, name := range moduleNames {
- fmt.Fprintf(&filecontent, "%s", toString(apexKeyMap[name]))
+ filecontent.WriteString(toString(apexKeyMap[name]))
}
-
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Description: "apexkeys.txt",
- Output: s.output,
- Args: map[string]string{
- "content": filecontent.String(),
- },
- })
+ android.WriteFileRule(ctx, s.output, filecontent.String())
}
func apexKeysTextFactory() android.Singleton {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ce16d73..7931e9e 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -218,7 +218,7 @@
})
if p.prebuiltCommon.checkForceDisable(ctx) {
- p.SkipInstall()
+ p.HideFromMake()
return
}
@@ -392,7 +392,7 @@
})
if a.prebuiltCommon.checkForceDisable(ctx) {
- a.SkipInstall()
+ a.HideFromMake()
return
}
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 60b6ed5..27d93ee 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -25,6 +25,7 @@
name: "libvndk",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -37,6 +38,7 @@
name: "libvndksp",
srcs: ["mylib.cpp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -73,6 +75,7 @@
cc_library {
name: "libfoo",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
diff --git a/bazel/Android.bp b/bazel/Android.bp
new file mode 100644
index 0000000..05eddc1
--- /dev/null
+++ b/bazel/Android.bp
@@ -0,0 +1,15 @@
+bootstrap_go_package {
+ name: "soong-bazel",
+ pkgPath: "android/soong/bazel",
+ srcs: [
+ "aquery.go",
+ "constants.go",
+ "properties.go",
+ ],
+ pluginFor: [
+ "soong_build",
+ ],
+ deps: [
+ "blueprint",
+ ],
+}
diff --git a/bazel/aquery.go b/bazel/aquery.go
new file mode 100644
index 0000000..69d4fde
--- /dev/null
+++ b/bazel/aquery.go
@@ -0,0 +1,116 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bazel
+
+import (
+ "encoding/json"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+// artifact contains relevant portions of Bazel's aquery proto, Artifact.
+// Represents a single artifact, whether it's a source file or a derived output file.
+type artifact struct {
+ Id string
+ ExecPath string
+}
+
+// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
+type KeyValuePair struct {
+ Key string
+ Value string
+}
+
+// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
+// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
+// data structure for storing large numbers of file paths.
+type depSetOfFiles struct {
+ Id string
+ // TODO(cparsons): Handle non-flat depsets.
+ DirectArtifactIds []string
+}
+
+// action contains relevant portions of Bazel's aquery proto, Action.
+// Represents a single command line invocation in the Bazel build graph.
+type action struct {
+ Arguments []string
+ EnvironmentVariables []KeyValuePair
+ InputDepSetIds []string
+ Mnemonic string
+ OutputIds []string
+}
+
+// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
+// An aquery response from Bazel contains a single ActionGraphContainer proto.
+type actionGraphContainer struct {
+ Artifacts []artifact
+ Actions []action
+ DepSetOfFiles []depSetOfFiles
+}
+
+// BuildStatement contains information to register a build statement corresponding (one to one)
+// with a Bazel action from Bazel's action graph.
+type BuildStatement struct {
+ Command string
+ OutputPaths []string
+ InputPaths []string
+ Env []KeyValuePair
+ Mnemonic string
+}
+
+// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
+// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
+// aquery invocation).
+func AqueryBuildStatements(aqueryJsonProto []byte) []BuildStatement {
+ buildStatements := []BuildStatement{}
+
+ var aqueryResult actionGraphContainer
+ json.Unmarshal(aqueryJsonProto, &aqueryResult)
+
+ artifactIdToPath := map[string]string{}
+ for _, artifact := range aqueryResult.Artifacts {
+ artifactIdToPath[artifact.Id] = artifact.ExecPath
+ }
+ depsetIdToArtifactIds := map[string][]string{}
+ for _, depset := range aqueryResult.DepSetOfFiles {
+ depsetIdToArtifactIds[depset.Id] = depset.DirectArtifactIds
+ }
+
+ for _, actionEntry := range aqueryResult.Actions {
+ outputPaths := []string{}
+ for _, outputId := range actionEntry.OutputIds {
+ // TODO(cparsons): Validate the id is present.
+ outputPaths = append(outputPaths, artifactIdToPath[outputId])
+ }
+ inputPaths := []string{}
+ for _, inputDepSetId := range actionEntry.InputDepSetIds {
+ // TODO(cparsons): Validate the id is present.
+ for _, inputId := range depsetIdToArtifactIds[inputDepSetId] {
+ // TODO(cparsons): Validate the id is present.
+ inputPaths = append(inputPaths, artifactIdToPath[inputId])
+ }
+ }
+ buildStatement := BuildStatement{
+ Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ OutputPaths: outputPaths,
+ InputPaths: inputPaths,
+ Env: actionEntry.EnvironmentVariables,
+ Mnemonic: actionEntry.Mnemonic}
+ buildStatements = append(buildStatements, buildStatement)
+ }
+
+ return buildStatements
+}
diff --git a/bazel/bazelenv.sh b/bazel/bazelenv.sh
deleted file mode 100755
index fcf71f1..0000000
--- a/bazel/bazelenv.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-# Helper script for setting environment variables required for Bazel/Soong
-# mixed builds prototype. For development use only.
-#
-# Usage:
-# export BAZEL_PATH=[some_bazel_path] && source bazelenv.sh
-#
-# If BAZEL_PATH is not set, `which bazel` will be used
-# to locate the appropriate bazel to use.
-
-
-# Function to find top of the source tree (if $TOP isn't set) by walking up the
-# tree.
-function gettop
-{
- local TOPFILE=build/soong/root.bp
- if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
- # The following circumlocution ensures we remove symlinks from TOP.
- (cd $TOP; PWD= /bin/pwd)
- else
- if [ -f $TOPFILE ] ; then
- # The following circumlocution (repeated below as well) ensures
- # that we record the true directory name and not one that is
- # faked up with symlink names.
- PWD= /bin/pwd
- else
- local HERE=$PWD
- T=
- while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
- \cd ..
- T=`PWD= /bin/pwd -P`
- done
- \cd $HERE
- if [ -f "$T/$TOPFILE" ]; then
- echo $T
- fi
- fi
- fi
-}
-
-BASE_DIR="$(mktemp -d)"
-
-if [ -z "$BAZEL_PATH" ] ; then
- export BAZEL_PATH="$(which bazel)"
-fi
-
-# TODO(cparsons): Use USE_BAZEL=1 instead once "mixed Soong/Bazel builds" are
-# production ready.
-export USE_BAZEL_ANALYSIS=1
-# TODO(cparsons): Retrieve this information in either envsetup.sh or
-# bazel.sh.
-export BAZEL_HOME="$BASE_DIR/bazelhome"
-export BAZEL_OUTPUT_BASE="$BASE_DIR/output"
-export BAZEL_WORKSPACE="$(gettop)"
-
-echo "USE_BAZEL_ANALYSIS=${USE_BAZEL_ANALYSIS}"
-echo "BAZEL_PATH=${BAZEL_PATH}"
-echo "BAZEL_HOME=${BAZEL_HOME}"
-echo "BAZEL_OUTPUT_BASE=${BAZEL_OUTPUT_BASE}"
-echo "BAZEL_WORKSPACE=${BAZEL_WORKSPACE}"
-
-mkdir -p $BAZEL_HOME
-mkdir -p $BAZEL_OUTPUT_BASE
diff --git a/bazel/constants.go b/bazel/constants.go
new file mode 100644
index 0000000..15c75cf
--- /dev/null
+++ b/bazel/constants.go
@@ -0,0 +1,26 @@
+package bazel
+
+type RunName string
+
+// Below is a list bazel execution run names used through out the
+// Platform Build systems. Each run name represents an unique key
+// to query the bazel metrics.
+const (
+ // Perform a bazel build of the phony root to generate symlink forests
+ // for dependencies of the bazel build.
+ BazelBuildPhonyRootRunName = RunName("bazel-build-phony-root")
+
+ // Perform aquery of the bazel build root to retrieve action information.
+ AqueryBuildRootRunName = RunName("aquery-buildroot")
+
+ // Perform cquery of the Bazel build root and its dependencies.
+ CqueryBuildRootRunName = RunName("cquery-buildroot")
+
+ // Run bazel as a ninja executer
+ BazelNinjaExecRunName = RunName("bazel-ninja-exec")
+)
+
+// String returns the name of the run.
+func (c RunName) String() string {
+ return string(c)
+}
diff --git a/bazel/master.WORKSPACE.bazel b/bazel/master.WORKSPACE.bazel
deleted file mode 100644
index e69de29..0000000
--- a/bazel/master.WORKSPACE.bazel
+++ /dev/null
diff --git a/bazel/properties.go b/bazel/properties.go
new file mode 100644
index 0000000..8bb1956
--- /dev/null
+++ b/bazel/properties.go
@@ -0,0 +1,27 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bazel
+
+type bazelModuleProperties struct {
+ // The label of the Bazel target replacing this Soong module.
+ Label string
+}
+
+// Properties contains common module properties for migration purposes.
+type Properties struct {
+ // In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
+ // this Soong module.
+ Bazel_module bazelModuleProperties
+}
diff --git a/bpf/bpf_test.go b/bpf/bpf_test.go
index d06d7d1..be9e36e 100644
--- a/bpf/bpf_test.go
+++ b/bpf/bpf_test.go
@@ -58,9 +58,9 @@
}
func testContext(config android.Config) *android.TestContext {
- ctx := cc.CreateTestContext()
+ ctx := cc.CreateTestContext(config)
ctx.RegisterModuleType("bpf", BpfFactory)
- ctx.Register(config)
+ ctx.Register()
return ctx
}
diff --git a/build_test.bash b/build_test.bash
index a53a585..accca0f 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -43,14 +43,9 @@
;;
esac
-function bazel_cleanup {
- "${TOP}/tools/bazel" shutdown
-}
-trap bazel_cleanup EXIT
-
echo
echo "Running Bazel smoke test..."
-"${TOP}/tools/bazel" info
+"${TOP}/tools/bazel" --batch --max_idle_secs=1 info
echo
echo "Running Soong test..."
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 38269cb..ddacb70 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
@@ -273,7 +273,7 @@
entries.SubName = "." + library.stubsVersion()
}
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- // library.makeUninstallable() depends on this to bypass SkipInstall() for
+ // library.makeUninstallable() depends on this to bypass HideFromMake() for
// static libraries.
entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
if library.buildStubs() {
@@ -281,10 +281,15 @@
}
})
}
- if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.AnyVariantDirectlyInAnyApex() &&
+ // If a library providing a stub is included in an APEX, the private APIs of the library
+ // is accessible only inside the APEX. From outside of the APEX, clients can only use the
+ // public APIs via the stub. To enforce this, the (latest version of the) stub gets the
+ // name of the library. The impl library instead gets the `.bootstrap` suffix to so that
+ // they can be exceptionally used directly when APEXes are not available (e.g. during the
+ // very early stage in the boot process).
+ if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() &&
!ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() && !ctx.static() {
if library.buildStubs() && library.isLatestStubVersion() {
- // reference the latest version via its name without suffix when it is provided by apex
entries.SubName = ""
}
if !library.buildStubs() {
@@ -296,7 +301,7 @@
func (object *objectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
entries.ExtraFooters = append(entries.ExtraFooters,
- func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+ func(w io.Writer, name, prefix, moduleDir string) {
out := entries.OutputFile.Path()
varname := fmt.Sprintf("SOONG_%sOBJECT_%s%s", prefix, name, entries.SubName)
@@ -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())
@@ -370,6 +373,9 @@
entries.SetBool("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", true)
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", test.Properties.Test_mainline_modules...)
+ if Bool(test.Properties.Test_options.Unit_test) {
+ entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+ }
})
androidMkWriteTestData(test.data, ctx, entries)
@@ -505,7 +511,7 @@
})
}
-func (c *vendorSnapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
// Each vendor snapshot is exported to androidMk only when BOARD_VNDK_VERSION != current
// and the version of the prebuilt is same as BOARD_VNDK_VERSION.
if c.shared() {
@@ -549,7 +555,7 @@
})
}
-func (c *vendorSnapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "EXECUTABLES"
if c.androidMkVendorSuffix {
@@ -563,7 +569,7 @@
})
}
-func (c *vendorSnapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
entries.Class = "STATIC_LIBRARIES"
if c.androidMkVendorSuffix {
@@ -573,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 70cf5ff..fa3966f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -42,6 +42,7 @@
// extension (if any) appended
Symlinks []string `android:"arch_variant"`
+ // override the dynamic linker
DynamicLinker string `blueprint:"mutated"`
// Names of modules to be overridden. Listed modules can only be other binaries
@@ -80,6 +81,7 @@
// Executables
//
+// binaryDecorator is a decorator containing information for C++ binary modules.
type binaryDecorator struct {
*baseLinker
*baseInstaller
@@ -95,17 +97,25 @@
// Names of symlinks to be installed for use in LOCAL_MODULE_SYMLINKS
symlinks []string
+ // If the module has symlink_preferred_arch set, the name of the symlink to the
+ // binary for the preferred arch.
+ preferredArchSymlink string
+
// Output archive of gcno coverage information
coverageOutputFile android.OptionalPath
// Location of the files that should be copied to dist dir when requested
distFiles android.TaggedDistFiles
+ // Action command lines to run directly after the binary is installed. For example,
+ // may be used to symlink runtime dependencies (such as bionic) alongside installation.
post_install_cmds []string
}
var _ linker = (*binaryDecorator)(nil)
+// linkerProps returns the list of individual properties objects relevant
+// for this binary.
func (binary *binaryDecorator) linkerProps() []interface{} {
return append(binary.baseLinker.linkerProps(),
&binary.Properties,
@@ -113,6 +123,10 @@
}
+// getStemWithoutSuffix returns the main section of the name to use for the symlink of
+// the main output file of this binary module. This may be derived from the module name
+// or other property overrides.
+// For the full symlink name, the `Suffix` property of a binary module must be appended.
func (binary *binaryDecorator) getStemWithoutSuffix(ctx BaseModuleContext) string {
stem := ctx.baseModuleName()
if String(binary.Properties.Stem) != "" {
@@ -122,10 +136,14 @@
return stem
}
+// getStem returns the full name to use for the symlink of the main output file of this binary
+// module. This may be derived from the module name and/or other property overrides.
func (binary *binaryDecorator) getStem(ctx BaseModuleContext) string {
return binary.getStemWithoutSuffix(ctx) + String(binary.Properties.Suffix)
}
+// linkerDeps augments and returns the given `deps` to contain dependencies on
+// modules common to most binaries, such as bionic libraries.
func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
deps = binary.baseLinker.linkerDeps(ctx, deps)
if ctx.toolchain().Bionic() {
@@ -151,6 +169,13 @@
deps.LateStaticLibs = append(groupLibs, deps.LateStaticLibs...)
}
+ // Embed the linker into host bionic binaries. This is needed to support host bionic,
+ // as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
+ // either an absolute path, or relative from CWD. To work around this, we extract
+ // the load sections from the runtime linker ELF binary and embed them into each host
+ // bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
+ // binary, and then we use a special entry point to fix up the arguments passed by
+ // the kernel before jumping to the embedded linker.
if ctx.Os() == android.LinuxBionic && !binary.static() {
deps.DynamicLinker = "linker"
deps.LinkerFlagsFile = "host_bionic_linker_flags"
@@ -166,9 +191,13 @@
}
func (binary *binaryDecorator) isDependencyRoot() bool {
+ // Binaries are always the dependency root.
return true
}
+// NewBinary builds and returns a new Module corresponding to a C++ binary.
+// Individual module implementations which comprise a C++ binary should call this function,
+// set some fields on the result, and then call the Init function.
func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
module := newModule(hod, android.MultilibFirst)
binary := &binaryDecorator{
@@ -186,11 +215,15 @@
return module, binary
}
+// linkerInit initializes dynamic properties of the linker (such as runpath) based
+// on properties of this binary.
func (binary *binaryDecorator) linkerInit(ctx BaseModuleContext) {
binary.baseLinker.linkerInit(ctx)
if !ctx.toolchain().Bionic() {
if ctx.Os() == android.Linux {
+ // Unless explicitly specified otherwise, host static binaries are built with -static
+ // if HostStaticBinaries is true for the product configuration.
if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
binary.Properties.Static_executable = BoolPtr(true)
}
@@ -213,9 +246,13 @@
return true
}
+// linkerFlags returns a Flags object containing linker flags that are defined
+// by this binary, or that are implied by attributes of this binary. These flags are
+// combined with the given flags.
func (binary *binaryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
flags = binary.baseLinker.linkerFlags(ctx, flags)
+ // Passing -pie to clang for Windows binaries causes a warning that -pie is unused.
if ctx.Host() && !ctx.Windows() && !binary.static() {
if !ctx.Config().IsEnvTrue("DISABLE_HOST_PIE") {
flags.Global.LdFlags = append(flags.Global.LdFlags, "-pie")
@@ -244,7 +281,7 @@
"-Bstatic",
"-Wl,--gc-sections",
)
- } else {
+ } else { // not static
if flags.DynamicLinker == "" {
if binary.Properties.DynamicLinker != "" {
flags.DynamicLinker = binary.Properties.DynamicLinker
@@ -284,7 +321,7 @@
"-Wl,-z,nocopyreloc",
)
}
- } else {
+ } else { // not bionic
if binary.static() {
flags.Global.LdFlags = append(flags.Global.LdFlags, "-static")
}
@@ -296,6 +333,9 @@
return flags
}
+// link registers actions to link this binary, and sets various fields
+// on this binary to reflect information that should be exported up the build
+// tree (for example, exported flags and include paths).
func (binary *binaryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
@@ -305,6 +345,7 @@
var linkerDeps android.Paths
+ // Add flags from linker flags file.
if deps.LinkerFlagsFile.Valid() {
flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
@@ -332,18 +373,21 @@
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)
}
outputFile = maybeInjectBoringSSLHash(ctx, outputFile, binary.Properties.Inject_bssl_hash, fileName)
+ // If use_version_lib is true, make an android::build::GetBuildNumber() function available.
if Bool(binary.baseLinker.Properties.Use_version_lib) {
if ctx.Host() {
versionedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unversioned", fileName)
binary.injectVersionSymbol(ctx, outputFile, versionedOutputFile)
} else {
+ // When dist'ing a library or binary that has use_version_lib set, always
+ // distribute the stamped version, even for the device.
versionedOutputFile := android.PathForModuleOut(ctx, "versioned", fileName)
binary.distFiles = android.MakeDefaultDistFiles(versionedOutputFile)
@@ -357,6 +401,7 @@
}
}
+ // Handle host bionic linker symbols.
if ctx.Os() == android.LinuxBionic && !binary.static() {
injectedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "prelinker", fileName)
@@ -382,13 +427,14 @@
linkerDeps = append(linkerDeps, objs.tidyFiles...)
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
+ // Register link action.
+ 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'
@@ -402,7 +448,10 @@
ctx.PropertyErrorf("symlink_preferred_arch", "must also specify suffix")
}
if ctx.TargetPrimary() {
- binary.symlinks = append(binary.symlinks, binary.getStemWithoutSuffix(ctx))
+ // Install a symlink to the preferred architecture
+ symlinkName := binary.getStemWithoutSuffix(ctx)
+ binary.symlinks = append(binary.symlinks, symlinkName)
+ binary.preferredArchSymlink = symlinkName
}
}
@@ -455,12 +504,26 @@
binary.baseInstaller.subDir = "bootstrap"
}
binary.baseInstaller.install(ctx, file)
+
+ var preferredArchSymlinkPath android.OptionalPath
for _, symlink := range binary.symlinks {
- ctx.InstallSymlink(binary.baseInstaller.installDir(ctx), symlink, binary.baseInstaller.path)
+ installedSymlink := ctx.InstallSymlink(binary.baseInstaller.installDir(ctx), symlink,
+ binary.baseInstaller.path)
+ if symlink == binary.preferredArchSymlink {
+ // If this is the preferred arch symlink, save the installed path for use as the
+ // tool path.
+ preferredArchSymlinkPath = android.OptionalPathForPath(installedSymlink)
+ }
}
if ctx.Os().Class == android.Host {
- binary.toolPath = android.OptionalPathForPath(binary.baseInstaller.path)
+ // If the binary is multilib with a symlink to the preferred architecture, use the
+ // symlink instead of the binary because that's the more "canonical" name.
+ if preferredArchSymlinkPath.Valid() {
+ binary.toolPath = preferredArchSymlinkPath
+ } else {
+ binary.toolPath = android.OptionalPathForPath(binary.baseInstaller.path)
+ }
}
}
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 3297e41..1cc2bd5 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()
})
- android.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
+ 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++
@@ -305,35 +334,54 @@
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
- // vendor snapshot, but this property allows a partner to exclude a
- // module normally thought of as a framework module from the vendor
- // snapshot.
- Exclude_from_vendor_snapshot *bool
+ // different snapshots (vendor, recovery, etc.), but these properties
+ // allow a partner to exclude a module normally thought of as a
+ // framework module from a snapshot.
+ Exclude_from_vendor_snapshot *bool
+ 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 {
// whether this module should be allowed to be directly depended by other
// modules with `vendor: true`, `proprietary: true`, or `vendor_available:true`.
- // In addition, this module should be allowed to be directly depended by
- // product modules with `product_specific: true`.
- // If set to true, three variants will be built separately, one like
- // normal, another limited to the set of libraries and headers
- // that are exposed to /vendor modules, and the other to /product modules.
+ // If set to true, two variants will be built separately, one like
+ // normal, and the other limited to the set of libraries and headers
+ // that are exposed to /vendor modules.
//
- // The vendor and product variants may be used with a different (newer) /system,
+ // The vendor variant may be used with a different (newer) /system,
// so it shouldn't have any unversioned runtime dependencies, or
// make assumptions about the system that may not be true in the
// future.
//
- // If set to false, this module becomes inaccessible from /vendor or /product
- // modules.
+ // If set to false, this module becomes inaccessible from /vendor modules.
//
// Default value is true when vndk: {enabled: true} or vendor: true.
//
// Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
- // If PRODUCT_PRODUCT_VNDK_VERSION isn't set, product variant will not be used.
Vendor_available *bool
+ // whether this module should be allowed to be directly depended by other
+ // modules with `product_specific: true` or `product_available: true`.
+ // If set to true, an additional product variant will be built separately
+ // that is limited to the set of libraries and headers that are exposed to
+ // /product modules.
+ //
+ // The product variant may be used with a different (newer) /system,
+ // so it shouldn't have any unversioned runtime dependencies, or
+ // make assumptions about the system that may not be true in the
+ // future.
+ //
+ // It must be set to true by default for vndk: {enabled: true} modules.
+ //
+ // Nothing happens if BOARD_VNDK_VERSION isn't set in the BoardConfig.mk
+ // and PRODUCT_PRODUCT_VNDK_VERSION isn't set.
+ Product_available *bool
+
// whether this module is capable of being loaded with other instance
// (possibly an older version) of the same module in the same process.
// Currently, a shared library that is a member of VNDK (vndk: {enabled: true})
@@ -344,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
@@ -355,19 +407,18 @@
useSdk() bool
sdkVersion() string
useVndk() bool
- isNdk() bool
+ isNdk(config android.Config) bool
isLlndk(config android.Config) bool
isLlndkPublic(config android.Config) bool
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
@@ -381,6 +432,7 @@
mustUseVendorVariant() bool
nativeCoverage() bool
directlyInAnyApex() bool
+ isPreventInstall() bool
}
type ModuleContext interface {
@@ -398,6 +450,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
@@ -405,6 +459,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
@@ -416,6 +473,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
@@ -431,15 +491,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)
@@ -520,6 +584,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.
@@ -537,7 +604,15 @@
return d.Kind == staticLibraryDependency
}
-// dependencyTag is used for tagging miscellanous dependency types that don't fit into
+// InstallDepNeeded returns true for shared libraries so that shared library dependencies of
+// binaries or other shared libraries are installed as dependencies.
+func (d libraryDependencyTag) InstallDepNeeded() bool {
+ return d.shared()
+}
+
+var _ android.InstallNeededDependencyTag = libraryDependencyTag{}
+
+// dependencyTag is used for tagging miscellaneous dependency types that don't fit into
// libraryDependencyTag. Each tag object is created globally and reused for multiple
// dependencies (although since the object contains no references, assigning a tag to a
// variable and modifying it will not modify the original). Users can compare the tag
@@ -547,18 +622,27 @@
name string
}
+// installDependencyTag is used for tagging miscellaneous dependency types that don't fit into
+// libraryDependencyTag, but where the dependency needs to be installed when the parent is
+// installed.
+type installDependencyTag struct {
+ blueprint.BaseDependencyTag
+ android.InstallAlwaysNeededDependencyTag
+ name string
+}
+
var (
genSourceDepTag = dependencyTag{name: "gen source"}
genHeaderDepTag = dependencyTag{name: "gen header"}
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"}
dataLibDepTag = dependencyTag{name: "data lib"}
- runtimeDepTag = dependencyTag{name: "runtime lib"}
+ runtimeDepTag = installDependencyTag{name: "runtime lib"}
testPerSrcDepTag = dependencyTag{name: "test_per_src"}
stubImplDepTag = dependencyTag{name: "stub_impl"}
)
@@ -579,9 +663,13 @@
return ok && ccLibDepTag.static()
}
+func IsHeaderDepTag(depTag blueprint.DependencyTag) bool {
+ ccLibDepTag, ok := depTag.(libraryDependencyTag)
+ return ok && ccLibDepTag.header()
+}
+
func IsRuntimeDepTag(depTag blueprint.DependencyTag) bool {
- ccDepTag, ok := depTag.(dependencyTag)
- return ok && ccDepTag == runtimeDepTag
+ return depTag == runtimeDepTag
}
func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
@@ -591,7 +679,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
@@ -608,18 +707,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
@@ -899,7 +1003,7 @@
// "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
+// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "product_available: true" or
// "product_specific: true" modules are included here.
func (c *Module) UseVndk() bool {
return c.Properties.VndkVersion != ""
@@ -920,8 +1024,8 @@
return c.coverage.Properties.IsCoverageVariant
}
-func (c *Module) IsNdk() bool {
- return inList(c.BaseModuleName(), ndkKnownLibs)
+func (c *Module) IsNdk(config android.Config) bool {
+ return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
}
func (c *Module) isLlndk(config android.Config) bool {
@@ -935,7 +1039,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)
}
@@ -968,7 +1072,7 @@
return false
}
-func (c *Module) isVndkExt() bool {
+func (c *Module) IsVndkExt() bool {
if vndkdep := c.vndkdep; vndkdep != nil {
return vndkdep.isVndkExt()
}
@@ -1010,6 +1114,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)
}
@@ -1033,9 +1150,13 @@
return Bool(c.Properties.Exclude_from_vendor_snapshot)
}
+func (c *Module) ExcludeFromRecoverySnapshot() bool {
+ return Bool(c.Properties.Exclude_from_recovery_snapshot)
+}
+
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
@@ -1122,8 +1243,8 @@
return ctx.mod.UseVndk()
}
-func (ctx *moduleContextImpl) isNdk() bool {
- return ctx.mod.IsNdk()
+func (ctx *moduleContextImpl) isNdk(config android.Config) bool {
+ return ctx.mod.IsNdk(config)
}
func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
@@ -1135,7 +1256,7 @@
}
func (ctx *moduleContextImpl) isVndkPrivate(config android.Config) bool {
- return ctx.mod.isVndkPrivate(config)
+ return ctx.mod.IsVndkPrivate(config)
}
func (ctx *moduleContextImpl) isVndk() bool {
@@ -1154,50 +1275,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
@@ -1241,6 +1326,10 @@
return ctx.mod.DirectlyInAnyApex()
}
+func (ctx *moduleContextImpl) isPreventInstall() bool {
+ return ctx.mod.Properties.PreventInstall
+}
+
func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
return &Module{
hod: hod,
@@ -1308,7 +1397,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 {
@@ -1342,7 +1431,7 @@
c.hideApexVariantFromMake = true
}
- c.makeLinkType = c.getMakeLinkType(actx)
+ c.makeLinkType = GetMakeLinkType(actx, c)
c.Properties.SubName = ""
@@ -1352,7 +1441,7 @@
_, llndk := c.linker.(*llndkStubDecorator)
_, llndkHeader := c.linker.(*llndkHeadersDecorator)
- if llndk || llndkHeader || (c.UseVndk() && c.HasVendorVariant()) {
+ if llndk || llndkHeader || (c.UseVndk() && c.HasNonSystemVariants()) {
// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
// added for product variant only when we have vendor and product variants with core
// variant. The suffix is not added for vendor-only or product-only module.
@@ -1469,13 +1558,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
@@ -1484,24 +1574,30 @@
// 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)
}
}
}
- if c.installable(apexInfo) {
+ if !proptools.BoolDefault(c.Properties.Installable, true) {
+ // If the module has been specifically configure to not be installed then
+ // hide from make as otherwise it will break when running inside make
+ // as the output path to install will not be specified. Not all uninstallable
+ // modules can be hidden from make as some are needed for resolving make side
+ // dependencies.
+ c.HideFromMake()
+ } else if !c.installable(apexInfo) {
+ c.SkipInstall()
+ }
+
+ // Still call c.installer.install though, the installs will be stored as PackageSpecs
+ // to allow using the outputs in a genrule.
+ if c.installer != nil && c.outputFile.Valid() {
c.installer.install(ctx, c.outputFile.Path())
if ctx.Failed() {
return
}
- } else if !proptools.BoolDefault(c.Properties.Installable, true) {
- // If the module has been specifically configure to not be installed then
- // skip the installation as otherwise it will break when running inside make
- // as the output path to install will not be specified. Not all uninstallable
- // modules can skip installation as some are needed for resolving make side
- // dependencies.
- c.SkipInstall()
}
}
@@ -1743,7 +1839,7 @@
for _, entry := range list {
// strip #version suffix out
name, _ := StubsLibNameAndVersion(entry)
- if ctx.useSdk() && inList(name, ndkKnownLibs) {
+ if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
variantLibs = append(variantLibs, name+ndkLibrarySuffix)
} else if ctx.useVndk() {
nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry))
@@ -1775,13 +1871,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() {
@@ -1804,7 +1893,7 @@
lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
- if buildStubs {
+ if c.IsStubs() {
actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
depTag, lib)
} else {
@@ -1812,12 +1901,11 @@
}
}
- if buildStubs {
- // Stubs lib does not have dependency to other static/shared libraries.
- // Don't proceed.
- return
- }
-
+ // sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
+ // C++ implementation library and one Java implementation library. When a module links against
+ // sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
+ // map from sysprop_library to implementation library; it will be used in whole_static_libs,
+ // static_libs, and shared_libs.
syspropImplLibraries := syspropImplLibraries(actx.Config())
vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
@@ -1839,6 +1927,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
@@ -1876,6 +1967,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
@@ -1986,20 +2080,25 @@
return
}
- if from.Module().Target().Os != android.Android {
+ if from.Target().Os != android.Android {
// Host code is not restricted
return
}
// VNDK is cc.Module supported only for now.
if ccFrom, ok := from.(*Module); ok && from.UseVndk() {
- // Though vendor code is limited by the vendor mutator,
- // each vendor-available module needs to check
- // link-type for VNDK.
+ // Though allowed dependency is limited by the image mutator,
+ // each vendor and product module needs to check link-type
+ // for VNDK.
if ccTo, ok := to.(*Module); ok {
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")
}
@@ -2126,9 +2225,15 @@
return false
}
- // Even if target lib has no vendor variant, keep checking dependency graph
- // in case it depends on vendor_available but not double_loadable transtively.
- if !to.HasVendorVariant() {
+ 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.
+ if !to.HasNonSystemVariants() {
return true
}
@@ -2158,8 +2263,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]
@@ -2170,7 +2275,7 @@
var err error
ver, err = android.ApiLevelFromUser(ctx, stubInfo.Version)
if err != nil {
- return SharedLibraryStubsInfo{}, err
+ return SharedStubLibrary{}, err
}
}
if ver.LessThanOrEqualTo(maxSdkVersion) {
@@ -2181,7 +2286,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
@@ -2304,6 +2409,10 @@
return
}
+ if !apexInfo.IsForPlatform() && libDepTag.excludeInApex {
+ return
+ }
+
depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
var ptr *android.Paths
@@ -2313,7 +2422,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() {
@@ -2323,10 +2439,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
@@ -2341,7 +2458,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.
@@ -2361,7 +2478,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
@@ -2371,6 +2488,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
@@ -2398,6 +2521,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 {
@@ -2525,7 +2655,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(
@@ -2603,7 +2735,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
}
@@ -2650,7 +2782,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
@@ -2762,21 +2894,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() {
- 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"
@@ -2786,7 +2921,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)
@@ -2822,13 +2957,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 {
@@ -2881,6 +3010,9 @@
}
}
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
depTag := ctx.OtherModuleDependencyTag(dep)
libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
@@ -2902,6 +3034,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.
@@ -2910,6 +3046,7 @@
return true
}
+// Implements android.ApexModule
func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
@@ -3008,38 +3145,6 @@
return module
}
-func squashVendorSrcs(m *Module) {
- if lib, ok := m.compiler.(*libraryDecorator); ok {
- lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
- lib.baseCompiler.Properties.Target.Vendor.Srcs...)
-
- lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
- lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
-
- lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
- lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
- }
-}
-
-func squashRecoverySrcs(m *Module) {
- if lib, ok := m.compiler.(*libraryDecorator); ok {
- lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
- lib.baseCompiler.Properties.Target.Recovery.Srcs...)
-
- lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
- lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
-
- lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
- lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
- }
-}
-
-func squashVendorRamdiskSrcs(m *Module) {
- if lib, ok := m.compiler.(*libraryDecorator); ok {
- lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs...)
- }
-}
-
func (c *Module) IsSdkVariant() bool {
return c.Properties.IsSdkVariant || c.AlwaysSdk()
}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3982c0c..c16cce8 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -53,8 +53,8 @@
func testCcWithConfig(t *testing.T, config android.Config) *android.TestContext {
t.Helper()
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -84,8 +84,8 @@
func testCcErrorWithConfig(t *testing.T, pattern string, config android.Config) {
t.Helper()
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
@@ -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 {
@@ -296,8 +296,8 @@
func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
t.Helper()
- assertString(t, params.Rule.String(), android.WriteFile.String())
- actual := strings.FieldsFunc(strings.ReplaceAll(params.Args["content"], "\\n", "\n"), func(r rune) bool { return r == '\n' })
+ content := android.ContentFromFileRuleForTests(t, params)
+ actual := strings.FieldsFunc(content, func(r rune) bool { return r == '\n' })
assertArrayString(t, actual, expected)
}
@@ -326,6 +326,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -335,6 +336,7 @@
cc_library {
name: "libvndk_private",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -345,6 +347,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -356,6 +359,7 @@
cc_library {
name: "libvndk_sp_private",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
support_system_process: true,
@@ -386,6 +390,7 @@
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.ProductVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
ctx := testCcWithConfig(t, config)
@@ -397,6 +402,11 @@
checkVndkModule(t, ctx, "libvndk_sp", "", true, "", vendorVariant)
checkVndkModule(t, ctx, "libvndk_sp_private", "", true, "", vendorVariant)
+ checkVndkModule(t, ctx, "libvndk", "", false, "", productVariant)
+ checkVndkModule(t, ctx, "libvndk_private", "", false, "", productVariant)
+ checkVndkModule(t, ctx, "libvndk_sp", "", true, "", productVariant)
+ checkVndkModule(t, ctx, "libvndk_sp_private", "", true, "", productVariant)
+
// Check VNDK snapshot output.
snapshotDir := "vndk-snapshot"
@@ -454,6 +464,7 @@
cc_library {
name: "libvndk_host_supported",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -463,6 +474,7 @@
cc_library {
name: "libvndk_host_supported_but_disabled_on_device",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -503,6 +515,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -512,6 +525,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -522,6 +536,7 @@
cc_library {
name: "libvndk2",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -647,6 +662,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -665,12 +681,28 @@
})
}
+func TestVndkModuleError(t *testing.T) {
+ // Check the error message for vendor_available and product_available properties.
+ testCcError(t, "product_available: may not have different value than `vendor_available`", `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ product_available: false,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+ `)
+}
+
func TestVndkDepError(t *testing.T) {
// Check whether an error is emitted when a VNDK lib depends on a system lib.
testCcError(t, "dependency \".*\" of \".*\" missing variant", `
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -689,6 +721,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -708,6 +741,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -727,6 +761,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -747,6 +782,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -758,6 +794,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -770,6 +807,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -789,6 +827,7 @@
cc_library {
name: "libvndkprivate",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -808,6 +847,7 @@
cc_library {
name: "libvndksp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -828,6 +868,7 @@
cc_library {
name: "libvndkspprivate",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
support_system_process: true,
@@ -861,6 +902,7 @@
cc_library {
name: "libdoubleloadable",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -883,6 +925,7 @@
cc_library {
name: "libvndksp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -909,6 +952,7 @@
cc_library {
name: "libdoubleloadable",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -919,6 +963,7 @@
cc_library {
name: "libnondoubleloadable",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -957,6 +1002,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1090,6 +1136,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1124,6 +1171,7 @@
version: "BOARD",
target_arch: "arm64",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1245,8 +1293,8 @@
config := TestConfig(buildDir, android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
android.FailIfErrored(t, errs)
@@ -1403,8 +1451,8 @@
config := TestConfig(buildDir, android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
android.FailIfErrored(t, errs)
@@ -1493,8 +1541,8 @@
config := TestConfig(buildDir, android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
android.FailIfErrored(t, errs)
@@ -1503,6 +1551,8 @@
android.CheckErrorsAgainstExpectations(t, errs, []string{
`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+ `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
})
}
@@ -1534,8 +1584,8 @@
config := TestConfig(buildDir, android.Android, nil, "", mockFS)
config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp"})
android.FailIfErrored(t, errs)
@@ -1549,6 +1599,132 @@
})
}
+func TestRecoverySnapshotCapture(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libvndk",
+ vendor_available: true,
+ recovery_available: true,
+ product_available: true,
+ vndk: {
+ enabled: true,
+ },
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_library {
+ name: "librecovery_available",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_library_headers {
+ name: "librecovery_headers",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_bin",
+ recovery: true,
+ nocrt: true,
+ }
+
+ cc_binary {
+ name: "recovery_available_bin",
+ recovery_available: true,
+ nocrt: true,
+ }
+
+ toolchain_library {
+ name: "libb",
+ recovery_available: true,
+ src: "libb.a",
+ }
+
+ cc_object {
+ name: "obj",
+ recovery_available: true,
+ }
+`
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+ config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+ ctx := testCcWithConfig(t, config)
+
+ // Check Recovery snapshot output.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, only recovery_available modules are captured.
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "libvndk.so.json"),
+ filepath.Join(sharedDir, "librecovery.so.json"),
+ filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+ // For static libraries, all recovery:true and recovery_available modules are captured.
+ staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "libb.a.json"),
+ filepath.Join(staticDir, "librecovery.a.json"),
+ filepath.Join(staticDir, "librecovery_available.a.json"))
+
+ // For binary executables, all recovery:true and recovery_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+ checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "recovery_bin.json"),
+ filepath.Join(binaryDir, "recovery_available_bin.json"))
+ }
+
+ // For header libraries, all vendor:true and vendor_available modules are captured.
+ headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+ jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
+
+ // For object modules, all vendor:true and vendor_available modules are captured.
+ objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+ checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+ jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+}
+
func TestDoubleLoadableDepError(t *testing.T) {
// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
@@ -1566,6 +1742,7 @@
cc_library {
name: "libnondoubleloadable",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1619,6 +1796,7 @@
cc_library {
name: "libnondoubleloadable",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1630,6 +1808,7 @@
cc_library {
name: "libdoubleloadable",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1640,6 +1819,7 @@
cc_library {
name: "libnondoubleloadable",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -1678,6 +1858,7 @@
name: "libvndksp",
shared_libs: ["libanothervndksp"],
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -1707,6 +1888,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1715,6 +1897,7 @@
cc_library {
name: "libvndk2",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1722,6 +1905,9 @@
vendor: {
suffix: "-suffix",
},
+ product: {
+ suffix: "-suffix",
+ },
},
nocrt: true,
}
@@ -1789,6 +1975,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1819,6 +2006,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1849,6 +2037,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1869,6 +2058,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1889,6 +2079,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1909,6 +2100,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1919,6 +2111,7 @@
name: "libvndk_ext_product",
product_specific: true,
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
extends: "libvndk",
@@ -1934,6 +2127,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -1956,6 +2150,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -1982,6 +2177,7 @@
cc_library {
name: "libvndk",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -2003,6 +2199,7 @@
cc_library {
name: "libvndk",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -2027,6 +2224,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2046,6 +2244,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2079,6 +2278,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2108,6 +2308,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2140,6 +2341,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2160,6 +2362,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2207,6 +2410,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2216,6 +2420,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2242,6 +2447,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2261,6 +2467,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2289,6 +2496,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2308,6 +2516,7 @@
cc_library {
name: "libvndk2",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2320,6 +2529,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2339,6 +2549,7 @@
cc_library {
name: "libvndk2",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2355,6 +2566,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2376,6 +2588,7 @@
cc_library {
name: "libvndk_sp_2",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2389,6 +2602,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2408,6 +2622,7 @@
cc_library {
name: "libvndk_sp2",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2434,6 +2649,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2442,6 +2658,7 @@
cc_library {
name: "libvndk_sp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2454,6 +2671,11 @@
nocrt: true,
}
cc_library {
+ name: "libpa",
+ product_available: true,
+ nocrt: true,
+ }
+ cc_library {
name: "libproduct_va",
product_specific: true,
vendor_available: true,
@@ -2466,7 +2688,7 @@
"libllndk",
"libvndk",
"libvndk_sp",
- "libva",
+ "libpa",
"libproduct_va",
],
nocrt: true,
@@ -2538,6 +2760,7 @@
cc_library {
name: "libvndk_private",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -2581,6 +2804,7 @@
cc_library {
name: "libvndk",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2588,6 +2812,7 @@
cc_library {
name: "libvndksp",
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
support_system_process: true,
@@ -2596,6 +2821,7 @@
cc_library {
name: "libvndkprivate",
vendor_available: false,
+ product_available: false,
vndk: {
enabled: true,
},
@@ -2618,6 +2844,7 @@
target_arch: "arm",
binder32bit: true,
vendor_available: true,
+ product_available: true,
vndk: {
enabled: true,
},
@@ -2686,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.
@@ -3783,11 +3902,11 @@
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.Debuggable = BoolPtr(true)
- ctx := CreateTestContext()
+ ctx := CreateTestContext(config)
ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("variable", android.VariableMutator).Parallel()
})
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -3818,9 +3937,9 @@
config := TestConfig(buildDir, android.Android, nil, bp, nil)
config.TestProductVariables.Allow_missing_dependencies = BoolPtr(true)
- ctx := CreateTestContext()
+ ctx := CreateTestContext(config)
ctx.SetAllowMissingDependencies(true)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -3842,3 +3961,130 @@
}
}
+
+func TestInstallSharedLibs(t *testing.T) {
+ bp := `
+ cc_binary {
+ name: "bin",
+ host_supported: true,
+ shared_libs: ["libshared"],
+ runtime_libs: ["libruntime"],
+ srcs: [":gen"],
+ }
+
+ cc_library_shared {
+ name: "libshared",
+ host_supported: true,
+ shared_libs: ["libtransitive"],
+ }
+
+ cc_library_shared {
+ name: "libtransitive",
+ host_supported: true,
+ }
+
+ cc_library_shared {
+ name: "libruntime",
+ host_supported: true,
+ }
+
+ cc_binary_host {
+ name: "tool",
+ srcs: ["foo.cpp"],
+ }
+
+ genrule {
+ name: "gen",
+ tools: ["tool"],
+ out: ["gen.cpp"],
+ cmd: "$(location tool) $(out)",
+ }
+ `
+
+ config := TestConfig(buildDir, android.Android, nil, bp, nil)
+ ctx := testCcWithConfig(t, config)
+
+ hostBin := ctx.ModuleForTests("bin", config.BuildOSTarget.String()).Description("install")
+ hostShared := ctx.ModuleForTests("libshared", config.BuildOSTarget.String()+"_shared").Description("install")
+ hostRuntime := ctx.ModuleForTests("libruntime", config.BuildOSTarget.String()+"_shared").Description("install")
+ hostTransitive := ctx.ModuleForTests("libtransitive", config.BuildOSTarget.String()+"_shared").Description("install")
+ hostTool := ctx.ModuleForTests("tool", config.BuildOSTarget.String()).Description("install")
+
+ if g, w := hostBin.Implicits.Strings(), hostShared.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected host bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostBin.Implicits.Strings(), hostTransitive.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected host bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostShared.Implicits.Strings(), hostTransitive.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected host bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostBin.Implicits.Strings(), hostRuntime.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected host bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := hostBin.Implicits.Strings(), hostTool.Output.String(); android.InList(w, g) {
+ t.Errorf("expected no host bin dependency %q, got %q", w, g)
+ }
+
+ deviceBin := ctx.ModuleForTests("bin", "android_arm64_armv8-a").Description("install")
+ deviceShared := ctx.ModuleForTests("libshared", "android_arm64_armv8-a_shared").Description("install")
+ deviceTransitive := ctx.ModuleForTests("libtransitive", "android_arm64_armv8-a_shared").Description("install")
+ deviceRuntime := ctx.ModuleForTests("libruntime", "android_arm64_armv8-a_shared").Description("install")
+
+ if g, w := deviceBin.OrderOnly.Strings(), deviceShared.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected device bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceBin.OrderOnly.Strings(), deviceTransitive.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected device bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceShared.OrderOnly.Strings(), deviceTransitive.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected device bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceBin.OrderOnly.Strings(), deviceRuntime.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected device bin dependency %q, got %q", w, g)
+ }
+
+ if g, w := deviceBin.OrderOnly.Strings(), hostTool.Output.String(); android.InList(w, g) {
+ t.Errorf("expected no device bin dependency %q, got %q", w, g)
+ }
+
+}
+
+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 e2a33d7..b78bb6c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -143,21 +143,21 @@
} `android:"arch_variant"`
Target struct {
- Vendor struct {
- // list of source files that should only be used in the
- // vendor variant of the C/C++ module.
+ Vendor, Product struct {
+ // list of source files that should only be used in vendor or
+ // product variant of the C/C++ module.
Srcs []string `android:"path"`
- // list of source files that should not be used to
- // build the vendor variant of the C/C++ module.
+ // list of source files that should not be used to build vendor
+ // or product variant of the C/C++ module.
Exclude_srcs []string `android:"path"`
- // List of additional cflags that should be used to build the vendor
- // variant of the C/C++ module.
+ // List of additional cflags that should be used to build vendor
+ // or product variant of the C/C++ module.
Cflags []string
- // list of generated sources that should not be used to
- // build the vendor variant of the C/C++ module.
+ // list of generated sources that should not be used to build
+ // vendor or product variant of the C/C++ module.
Exclude_generated_sources []string
}
Recovery struct {
@@ -298,6 +298,7 @@
CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
+ CheckBadCompilerFlags(ctx, "product.cflags", compiler.Properties.Target.Product.Cflags)
CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
CheckBadCompilerFlags(ctx, "vendor_ramdisk.cflags", compiler.Properties.Target.Vendor_ramdisk.Cflags)
@@ -365,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()))
}
}
@@ -473,7 +474,12 @@
flags.Local.ConlyFlags = append([]string{"-std=" + cStd}, flags.Local.ConlyFlags...)
flags.Local.CppFlags = append([]string{"-std=" + cppStd}, flags.Local.CppFlags...)
- if ctx.useVndk() {
+ if ctx.inVendor() {
+ flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
+ }
+
+ if ctx.inProduct() {
+ // TODO(b/150902910): must use 'compiler.Properties.Target.Product.Cflags'
flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
}
@@ -643,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{
@@ -681,6 +687,9 @@
// list of shared libraries that provide headers for this binding.
Shared_libs []string `android:"arch_variant"`
+ // List of libraries which export include paths required for this module
+ Header_libs []string `android:"arch_variant,variant_prepend"`
+
// list of clang flags required to correctly interpret the headers.
Cflags []string `android:"arch_variant"`
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 74642c2..59c52d1 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -29,6 +29,10 @@
// which stands for "Android device target". Keeping PIC on is required because
// many modules we have (e.g. Bionic) assume PIC.
"-fpic",
+
+ // This is normally in ClangExtraTargetCflags, but that's for device and we need
+ // the same for host
+ "-nostdlibinc",
))
linuxCrossLdflags = ClangFilterUnknownCflags([]string{
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 d18ae25..8a0d7bd 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -24,6 +24,7 @@
"android.hardware.nfc@1.2",
"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/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index 81c907d..d7ff580 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -135,7 +135,7 @@
func getMacTools(ctx android.PackageVarContext) *macPlatformTools {
macTools.once.Do(func() {
- xcrunTool := ctx.Config().HostSystemTool("xcrun")
+ xcrunTool := ctx.Config().NonHermeticHostSystemTool("xcrun")
xcrun := func(args ...string) string {
if macTools.err != nil {
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 fe3c12b..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 {
@@ -278,14 +278,7 @@
if fuzz.Properties.Fuzz_config != nil {
configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Description: "fuzzer infrastructure configuration",
- Output: configPath,
- Args: map[string]string{
- "content": fuzz.Properties.Fuzz_config.String(),
- },
- })
+ android.WriteFileRule(ctx, configPath, fuzz.Properties.Fuzz_config.String())
fuzz.config = configPath
}
@@ -426,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)
@@ -441,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())
@@ -499,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 {
@@ -511,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
@@ -538,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.
@@ -556,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 ccc3d0e..75b9e49 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -75,7 +75,10 @@
cmd := rule.Command()
// Fix up #line markers to not use the sbox temporary directory
- sedCmd := "sed -i.bak 's#__SBOX_OUT_DIR__#" + outDir.String() + "#'"
+ // android.sboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
+ // directory itself, without any filename appended.
+ sboxOutDir := cmd.PathForOutput(outDir)
+ sedCmd := "sed -i.bak 's#" + sboxOutDir + "#" + outDir.String() + "#'"
rule.Command().Text(sedCmd).Input(outFile)
rule.Command().Text(sedCmd).Input(headerFile)
@@ -133,7 +136,7 @@
}
cmd := rule.Command()
- cmd.BuiltTool(ctx, "aidl-cpp").
+ cmd.BuiltTool("aidl-cpp").
FlagWithDepFile("-d", depFile).
Flag("--ninja").
Flag(aidlFlags).
@@ -228,7 +231,8 @@
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_
}
@@ -257,7 +261,8 @@
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")
depFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp.d")
@@ -279,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/gen_test.go b/cc/gen_test.go
index 4b9a36e..41ef95c 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -18,6 +18,8 @@
"path/filepath"
"strings"
"testing"
+
+ "android/soong/android"
)
func TestGen(t *testing.T) {
@@ -56,13 +58,14 @@
}`)
aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
+ aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
t.Errorf("missing aidl includes in global flags")
}
- aidlCommand := aidl.RuleParams.Command
+ aidlCommand := android.RuleBuilderSboxProtoForTests(t, aidlManifest).Commands[0].GetCommand()
if !strings.Contains(aidlCommand, "-Isub") {
t.Errorf("aidl command for c.aidl should contain \"-Isub\", but was %q", aidlCommand)
}
diff --git a/cc/genrule.go b/cc/genrule.go
index a5a58d2..3668e2b 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -25,6 +25,7 @@
type GenruleExtraProperties struct {
Vendor_available *bool
+ Product_available *bool
Ramdisk_available *bool
Vendor_ramdisk_available *bool
Recovery_available *bool
@@ -62,7 +63,7 @@
return false
}
- return Bool(g.Vendor_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
+ return Bool(g.Vendor_available) || Bool(g.Product_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
}
func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -100,7 +101,8 @@
return variants
}
- if Bool(g.Vendor_available) || ctx.ProductSpecific() {
+ // TODO(b/150902910): vendor_available will not create product variant. Remove Bool(g.Vendor_available)
+ if Bool(g.Vendor_available) || Bool(g.Product_available) || ctx.ProductSpecific() {
variants = append(variants, ProductVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
if vndkVersion := ctx.DeviceConfig().ProductVndkVersion(); vndkVersion != "current" {
variants = append(variants, ProductVariationPrefix+vndkVersion)
diff --git a/cc/genrule_test.go b/cc/genrule_test.go
index a366f76..fa0c6f2 100644
--- a/cc/genrule_test.go
+++ b/cc/genrule_test.go
@@ -22,9 +22,9 @@
)
func testGenruleContext(config android.Config) *android.TestContext {
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("cc_genrule", genRuleFactory)
- ctx.Register(config)
+ ctx.Register()
return ctx
}
@@ -66,14 +66,14 @@
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out_arm")
expected := []string{"foo"}
- if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
- t.Errorf(`want arm inputs %v, got %v`, expected, gen.Inputs.Strings())
+ if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+ t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
}
gen = ctx.ModuleForTests("gen", "android_arm64_armv8-a").Output("out_arm64")
expected = []string{"bar"}
- if !reflect.DeepEqual(expected, gen.Inputs.Strings()) {
- t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Inputs.Strings())
+ if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+ t.Errorf(`want arm64 inputs %v, got %v`, expected, gen.Implicits.Strings())
}
}
@@ -108,10 +108,10 @@
gen := ctx.ModuleForTests("gen", "android_arm_armv7-a-neon").Output("out")
expected := []string{"libboth.so", "libshared.so", "libstatic.a"}
var got []string
- for _, input := range gen.Inputs {
+ for _, input := range gen.Implicits {
got = append(got, input.Base())
}
- if !reflect.DeepEqual(expected, got) {
+ if !reflect.DeepEqual(expected, got[:len(expected)]) {
t.Errorf(`want inputs %v, got %v`, expected, got)
}
}
diff --git a/cc/image.go b/cc/image.go
index 7b9425f..cca454a 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
@@ -65,8 +65,9 @@
)
func (ctx *moduleContext) ProductSpecific() bool {
+ //TODO(b/150902910): Replace HasNonSystemVariants() with HasProductVariant()
return ctx.ModuleContext.ProductSpecific() ||
- (ctx.mod.HasVendorVariant() && ctx.mod.inProduct())
+ (ctx.mod.HasNonSystemVariants() && ctx.mod.InProduct())
}
func (ctx *moduleContext) SocSpecific() bool {
@@ -75,7 +76,7 @@
}
func (ctx *moduleContextImpl) inProduct() bool {
- return ctx.mod.inProduct()
+ return ctx.mod.InProduct()
}
func (ctx *moduleContextImpl) inVendor() bool {
@@ -94,14 +95,23 @@
return ctx.mod.InRecovery()
}
-// Returns true only when this module is configured to have core, product and vendor
-// variants.
+// Returns true when this module is configured to have core and vendor variants.
func (c *Module) HasVendorVariant() bool {
return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
}
+// Returns true when this module is configured to have core and product variants.
+func (c *Module) HasProductVariant() bool {
+ return c.IsVndk() || Bool(c.VendorProperties.Product_available)
+}
+
+// Returns true when this module is configured to have core and either product or vendor variants.
+func (c *Module) HasNonSystemVariants() bool {
+ return c.IsVndk() || Bool(c.VendorProperties.Vendor_available) || Bool(c.VendorProperties.Product_available)
+}
+
// 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
}
@@ -139,9 +149,30 @@
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
productSpecific := mctx.ProductSpecific()
- if m.VendorProperties.Vendor_available != nil && vendorSpecific {
- mctx.PropertyErrorf("vendor_available",
- "doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+ if m.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 defined, make sure vendor_available and product_available has the
+ // same value since `false` for these properties means the module is
+ // for system only but provides the variant.
+ if m.VendorProperties.Product_available != nil {
+ if Bool(m.VendorProperties.Vendor_available) != Bool(m.VendorProperties.Product_available) {
+ mctx.PropertyErrorf("product_available", "may not have different value than `vendor_available`")
+ }
+ }
+ }
+
+ if m.VendorProperties.Product_available != nil {
+ if productSpecific {
+ mctx.PropertyErrorf("product_available",
+ "doesn't make sense at the same time as `product_specific: true`")
+ }
+ if vendorSpecific {
+ mctx.PropertyErrorf("product_available",
+ "cannot provide product variant from a vendor module. Please use `product_specific: true` with `vendor_available: true`")
+ }
}
if vndkdep := m.vndkdep; vndkdep != nil {
@@ -153,6 +184,9 @@
} else if m.VendorProperties.Vendor_available != nil {
mctx.PropertyErrorf("vendor_available",
"must not set at the same time as `vndk: {extends: \"...\"}`")
+ } else if m.VendorProperties.Product_available != nil {
+ mctx.PropertyErrorf("product_available",
+ "must not set at the same time as `vndk: {extends: \"...\"}`")
}
} else {
if vndkdep.isVndkExt() {
@@ -231,21 +265,25 @@
} else {
mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
}
- } else if m.HasVendorVariant() && !m.isVndkExt() {
- // This will be available in /system, /vendor and /product
- // or a /system directory that is available to vendor and product.
+ } else if m.HasNonSystemVariants() && !m.IsVndkExt() {
+ // This will be available to /system unless it is product_specific
+ // which will be handled later.
coreVariantNeeded = true
// We assume that modules under proprietary paths are compatible for
// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
// PLATFORM_VNDK_VERSION.
- if isVendorProprietaryModule(mctx) {
- vendorVariants = append(vendorVariants, boardVndkVersion)
- } else {
- vendorVariants = append(vendorVariants, platformVndkVersion)
+ if m.HasVendorVariant() {
+ if isVendorProprietaryModule(mctx) {
+ vendorVariants = append(vendorVariants, boardVndkVersion)
+ } else {
+ vendorVariants = append(vendorVariants, platformVndkVersion)
+ }
}
// vendor_available modules are also available to /product.
+ // TODO(b/150902910): product variant will be created only if
+ // m.HasProductVariant() is true.
productVariants = append(productVariants, platformVndkVersion)
// VNDK is always PLATFORM_VNDK_VERSION
if !m.IsVndk() {
@@ -351,6 +389,51 @@
return c.Properties.ExtraVariants
}
+func squashVendorSrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+ lib.baseCompiler.Properties.Target.Vendor.Srcs...)
+
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+ lib.baseCompiler.Properties.Target.Vendor.Exclude_srcs...)
+
+ lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
+ lib.baseCompiler.Properties.Target.Vendor.Exclude_generated_sources...)
+ }
+}
+
+func squashProductSrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+ lib.baseCompiler.Properties.Target.Product.Srcs...)
+
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+ lib.baseCompiler.Properties.Target.Product.Exclude_srcs...)
+
+ lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
+ lib.baseCompiler.Properties.Target.Product.Exclude_generated_sources...)
+ }
+}
+
+func squashRecoverySrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Srcs...)
+
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+ lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+
+ lib.baseCompiler.Properties.Exclude_generated_sources = append(lib.baseCompiler.Properties.Exclude_generated_sources,
+ lib.baseCompiler.Properties.Target.Recovery.Exclude_generated_sources...)
+ }
+}
+
+func squashVendorRamdiskSrcs(m *Module) {
+ if lib, ok := m.compiler.(*libraryDecorator); ok {
+ lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs, lib.baseCompiler.Properties.Target.Vendor_ramdisk.Exclude_srcs...)
+ }
+}
+
func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
m := module.(*Module)
if variant == android.RamdiskVariation {
@@ -371,11 +454,12 @@
vndkVersion := ctx.DeviceConfig().VndkVersion()
if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
m.Properties.HideFromMake = true
- m.SkipInstall()
+ m.HideFromMake()
}
} else if strings.HasPrefix(variant, ProductVariationPrefix) {
m.Properties.ImageVariationPrefix = ProductVariationPrefix
m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+ // TODO (b/150902910): This will be replaced with squashProductSrcs(m).
squashVendorSrcs(m)
}
}
diff --git a/cc/library.go b/cc/library.go
index d946629..11ee7dd 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"`
@@ -72,7 +73,7 @@
Suffix *string `android:"arch_variant"`
Target struct {
- Vendor struct {
+ Vendor, Product struct {
// set suffix of the name of the output
Suffix *string `android:"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"`
@@ -172,11 +182,11 @@
Export_system_include_dirs []string `android:"arch_variant"`
Target struct {
- Vendor struct {
+ Vendor, Product struct {
// list of exported include directories, like
- // export_include_dirs, that will be applied to the
- // vendor variant of this library. This will overwrite
- // any other declarations.
+ // export_include_dirs, that will be applied to
+ // vendor or product variant of this library.
+ // This will overwrite any other declarations.
Override_export_include_dirs []string
}
}
@@ -242,17 +252,25 @@
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 {
return android.PathsForModuleSrc(ctx, f.Properties.Target.Vendor.Override_export_include_dirs)
} else {
@@ -260,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: "+
@@ -456,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()...)
@@ -475,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)
@@ -525,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 {
@@ -558,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() {
- 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 {
@@ -632,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() {
@@ -682,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)
@@ -716,6 +709,7 @@
suffix := ""
if useVndk {
+ // TODO(b/150902910): product variant must use Target.Product
suffix = String(library.Properties.Target.Vendor.Suffix)
}
if suffix == "" {
@@ -725,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()
@@ -812,6 +808,7 @@
deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.SharedProperties.Shared.Export_shared_lib_headers...)
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.SharedProperties.Shared.Export_static_lib_headers...)
}
+ // TODO(b/150902910): product variant must use Target.Product
if ctx.useVndk() {
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
@@ -894,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
}
@@ -971,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) {
@@ -1016,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)
@@ -1026,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)
@@ -1036,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
@@ -1055,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()),
})
@@ -1096,7 +1099,7 @@
func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
// The logic must be consistent with classifySourceAbiDump.
- isNdk := ctx.isNdk()
+ isNdk := ctx.isNdk(ctx.Config())
isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || (ctx.useVndk() && ctx.isVndk())
refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
@@ -1112,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() {
@@ -1138,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.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() {
@@ -1166,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...)
@@ -1173,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")
@@ -1184,6 +1195,7 @@
}
}
+ // Optionally export proto headers.
if Bool(library.Properties.Proto.Export_proto_headers) {
if library.baseCompiler.hasSrcExt(".proto") {
var includes android.Paths
@@ -1199,38 +1211,55 @@
}
}
+ // If the library is sysprop_library, expose either public or internal header selectively.
if library.baseCompiler.hasSrcExt(".sysprop") {
dir := android.PathForModuleGen(ctx, "sysprop", "include")
if library.Properties.Sysprop.Platform != nil {
- isProduct := ctx.ProductSpecific() && !ctx.useVndk()
- isVendor := ctx.useVndk()
+ isClientProduct := ctx.ProductSpecific() && !ctx.useVndk()
+ isClientVendor := ctx.useVndk()
isOwnerPlatform := Bool(library.Properties.Sysprop.Platform)
+ // If the owner is different from the user, expose public header. That is,
+ // 1) if the user is product (as owner can only be platform / vendor)
+ // 2) if one is platform and the other is vendor
+ // Exceptions are ramdisk and recovery. They are not enforced at all. So
+ // they always use internal header.
if !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() &&
- (isProduct || (isOwnerPlatform == isVendor)) {
+ (isClientProduct || (isOwnerPlatform == isClientVendor)) {
dir = android.PathForModuleGen(ctx, "sysprop/public", "include")
}
}
+ // Add sysprop-related directories to the exported directories of this library.
library.reexportDirs(dir)
library.reexportDeps(library.baseCompiler.pathDeps...)
library.addExportedGeneratedHeaders(library.baseCompiler.pathDeps...)
}
- if library.buildStubs() && !library.skipAPIDefine {
- library.reexportFlags("-D" + versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx)) + "=" + library.stubsVersion())
- }
+ // Add stub-related flags if this library is a stub library.
+ library.exportVersioningMacroIfNeeded(ctx)
+ // Propagate a Provider containing information about exported flags, deps, and include paths.
library.flagExporter.setProvider(ctx)
return out
}
+func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
+ if library.buildStubs() && !library.skipAPIDefine {
+ name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
+ ver := library.stubsVersion()
+ library.reexportFlags("-D" + name + "=" + ver)
+ }
+}
+
+// buildStatic returns true if this library should be built as a static library.
func (library *libraryDecorator) buildStatic() bool {
return library.MutatedProperties.BuildStatic &&
BoolDefault(library.StaticProperties.Static.Enabled, true)
}
+// buildShared returns true if this library should be built as a shared library.
func (library *libraryDecorator) buildShared() bool {
return library.MutatedProperties.BuildShared &&
BoolDefault(library.SharedProperties.Shared.Enabled, true)
@@ -1260,7 +1289,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 {
@@ -1269,7 +1298,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() {
@@ -1290,7 +1319,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() {
@@ -1307,7 +1336,7 @@
}
} else if ctx.directlyInAnyApex() && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
// Skip installing LLNDK (non-bionic) libraries moved to APEX.
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
}
library.baseInstaller.install(ctx, file)
@@ -1316,7 +1345,7 @@
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
library.baseLinker.sanitize.isUnsanitizedVariant() &&
- !library.buildStubs() && ctx.sdkVersion() == "" {
+ ctx.isForPlatform() && !ctx.isPreventInstall() {
installPath := getNdkSysrootBase(ctx).Join(
ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
@@ -1337,36 +1366,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
@@ -1449,6 +1488,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)
@@ -1460,12 +1510,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)
@@ -1521,6 +1576,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 {
@@ -1598,6 +1655,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 {
@@ -1745,13 +1805,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/library_sdk_member.go b/cc/library_sdk_member.go
index 106b2eb..3f2cf49 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -130,6 +130,10 @@
pbm.AddProperty("vendor_available", true)
}
+ if proptools.Bool(ccModule.VendorProperties.Product_available) {
+ pbm.AddProperty("product_available", true)
+ }
+
sdkVersion := ccModule.SdkVersion()
if sdkVersion != "" {
pbm.AddProperty("sdk_version", sdkVersion)
diff --git a/cc/linkable.go b/cc/linkable.go
index 60ab6df..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,19 +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
@@ -76,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
@@ -105,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 7f13e28..7bc4105 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -96,32 +96,32 @@
Runtime_libs []string `android:"arch_variant"`
Target struct {
- Vendor struct {
- // list of shared libs that only should be used to build the vendor
- // variant of the C/C++ module.
+ Vendor, Product struct {
+ // list of shared libs that only should be used to build vendor or
+ // product variant of the C/C++ module.
Shared_libs []string
- // list of static libs that only should be used to build the vendor
- // variant of the C/C++ module.
+ // list of static libs that only should be used to build vendor or
+ // product variant of the C/C++ module.
Static_libs []string
- // list of shared libs that should not be used to build the vendor variant
- // of the C/C++ module.
+ // list of shared libs that should not be used to build vendor or
+ // product variant of the C/C++ module.
Exclude_shared_libs []string
- // list of static libs that should not be used to build the vendor variant
- // of the C/C++ module.
+ // list of static libs that should not be used to build vendor or
+ // product variant of the C/C++ module.
Exclude_static_libs []string
- // list of header libs that should not be used to build the vendor variant
- // of the C/C++ module.
+ // list of header libs that should not be used to build vendor or
+ // product variant of the C/C++ module.
Exclude_header_libs []string
- // list of runtime libs that should not be installed along with the vendor
- // variant of the C/C++ module.
+ // list of runtime libs that should not be installed along with
+ // vendor or variant of the C/C++ module.
Exclude_runtime_libs []string
- // version script for this vendor variant
+ // version script for vendor or product variant
Version_script *string `android:"arch_variant"`
}
Recovery struct {
@@ -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.
@@ -211,6 +220,7 @@
linker.Properties.Ldflags = append(linker.Properties.Ldflags, flags...)
}
+// linkerInit initializes dynamic properties of the linker (such as runpath).
func (linker *baseLinker) linkerInit(ctx BaseModuleContext) {
if ctx.toolchain().Is64Bit() {
linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "../lib64", "lib64")
@@ -239,10 +249,21 @@
deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Exclude_static_libs)
deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+ // Record the libraries that need to be excluded when building for APEX. Unlike other
+ // target.*.exclude_* properties, SharedLibs and StaticLibs are not modified here because
+ // this module hasn't yet passed the apexMutator. Therefore, we can't tell whether this is
+ // an apex variant of not. Record the exclude list in the deps struct for now. The info is
+ // used to mark the dependency tag when adding dependencies to the deps. Then inside
+ // GenerateAndroidBuildActions, the marked dependencies are ignored (i.e. not used) for APEX
+ // variants.
+ deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_shared_libs...)
+ deps.ExcludeLibsForApex = append(deps.ExcludeLibsForApex, linker.Properties.Target.Apex.Exclude_static_libs...)
+
if Bool(linker.Properties.Use_version_lib) {
deps.WholeStaticLibs = append(deps.WholeStaticLibs, "libbuildversion")
}
+ // TODO(b/150902910): product variant must use Target.Product
if ctx.useVndk() {
deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Vendor.Shared_libs...)
deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Vendor.Exclude_shared_libs)
@@ -479,6 +500,7 @@
versionScript := ctx.ExpandOptionalSource(
linker.Properties.Version_script, "version_script")
+ // TODO(b/150902910): product variant must use Target.Product
if ctx.useVndk() && linker.Properties.Target.Vendor.Version_script != nil {
versionScript = ctx.ExpandOptionalSource(
linker.Properties.Target.Vendor.Version_script,
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 3b1d2f4..212720b 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -55,9 +55,11 @@
// Whether the system library uses symbol versions.
Unversioned *bool
- // whether this module can be directly depended upon by libs that are installed to /vendor.
- // When set to false, this module can only be depended on by VNDK libraries, not vendor
- // libraries. This effectively hides this module from vendors. Default value is true.
+ // whether this module can be directly depended upon by libs that are installed
+ // to /vendor and /product.
+ // When set to false, this module can only be depended on by VNDK libraries, not
+ // vendor nor product libraries. This effectively hides this module from
+ // non-system modules. Default value is true.
Vendor_available *bool
// list of llndk headers to re-export include directories from.
diff --git a/cc/makevars.go b/cc/makevars.go
index dcfd6d8..bd8aab5 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -171,6 +171,7 @@
ctx.StrictRaw("SRC_HEADERS", strings.Join(includes, " "))
ctx.StrictRaw("SRC_SYSTEM_HEADERS", strings.Join(systemIncludes, " "))
+ ndkKnownLibs := *getNDKKnownLibs(ctx.Config())
sort.Strings(ndkKnownLibs)
ctx.Strict("NDK_KNOWN_LIBS", strings.Join(ndkKnownLibs, " "))
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 9097e7b..a5c43fe 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -45,8 +45,7 @@
ndkLibrarySuffix = ".ndk"
- // Added as a variation dependency via depsMutator.
- ndkKnownLibs = []string{}
+ ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
// protects ndkKnownLibs writes during parallel BeginMutator.
ndkKnownLibsLock sync.Mutex
)
@@ -158,6 +157,12 @@
return true
}
+func getNDKKnownLibs(config android.Config) *[]string {
+ return config.Once(ndkKnownLibsKey, func() interface{} {
+ return &[]string{}
+ }).(*[]string)
+}
+
func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
c.baseCompiler.compilerInit(ctx)
@@ -168,12 +173,13 @@
ndkKnownLibsLock.Lock()
defer ndkKnownLibsLock.Unlock()
- for _, lib := range ndkKnownLibs {
+ ndkKnownLibs := getNDKKnownLibs(ctx.Config())
+ for _, lib := range *ndkKnownLibs {
if lib == name {
return
}
}
- ndkKnownLibs = append(ndkKnownLibs, name)
+ *ndkKnownLibs = append(*ndkKnownLibs, name)
}
func addStubLibraryCompilerFlags(flags Flags) Flags {
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..2cd18cb 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -17,6 +17,7 @@
import (
"android/soong/android"
"path/filepath"
+ "strings"
)
func init() {
@@ -117,6 +118,8 @@
return nil
}
+ p.libraryDecorator.exportVersioningMacroIfNeeded(ctx)
+
in := android.PathForModuleSrc(ctx, srcs[0])
if p.static() {
@@ -146,7 +149,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 +193,12 @@
}
}
+ if p.header() {
+ ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
+
+ return nil
+ }
+
return nil
}
@@ -220,6 +229,11 @@
p.properties.Srcs = nil
}
+// Implements versionedInterface
+func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
+ return strings.TrimPrefix(name, "prebuilt_")
+}
+
func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module, library := NewLibrary(hod)
module.compiler = nil
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 1f070a5..ee4de6e 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -25,20 +25,20 @@
func testPrebuilt(t *testing.T, bp string, fs map[string][]byte, handlers ...configCustomizer) *android.TestContext {
config := TestConfig(buildDir, android.Android, nil, bp, fs)
- ctx := CreateTestContext()
+ ctx := CreateTestContext(config)
// Enable androidmk support.
// * Register the singleton
// * Configure that we are inside make
// * Add CommonOS to ensure that androidmk processing works.
android.RegisterAndroidMkBuildComponents(ctx)
- android.SetInMakeForTests(config)
+ android.SetKatiEnabledForTests(config)
for _, handler := range handlers {
handler(config)
}
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
diff --git a/cc/proto.go b/cc/proto.go
index ae988ec..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
}
@@ -130,6 +130,8 @@
flags.protoC = true
flags.protoOptionsFile = true
flags.proto.OutTypeFlag = "--nanopb_out"
+ // Disable nanopb timestamps to support remote caching.
+ flags.proto.OutParams = append(flags.proto.OutParams, "-T")
plugin = "protoc-gen-nanopb"
case "full":
flags.proto.OutTypeFlag = "--cpp_out"
diff --git a/cc/sabi.go b/cc/sabi.go
index ef6bead..c357f8d 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.IsHideFromMake() {
+ return false
+ }
+
+ // Don't check ramdisk or recovery variants. Only check core, vendor or product variants.
+ if m.InRamdisk() || m.InVendorRamdisk() || m.InRecovery() {
+ return false
+ }
+
+ // Don't create ABI dump for prebuilts.
+ if m.Prebuilt() != nil || m.isSnapshotPrebuilt() {
+ return false
+ }
+
+ // Coverage builds have extra symbols.
+ if m.isCoverageVariant() {
+ return false
+ }
+
+ // Some sanitizer variants may have different ABI.
+ if m.sanitize != nil && !m.sanitize.isVariantOnProductionDevice() {
+ return false
+ }
+
+ // Don't create ABI dump for stubs.
+ if m.isNDKStubLibrary() || m.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 b1326d9..22ee25f 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -169,8 +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
@@ -543,6 +549,9 @@
flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
+ if Bool(sanitize.Properties.Sanitize.Config.Cfi_assembly_support) {
+ flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-cfi-canonical-jump-tables")
+ }
// Only append the default visibility flag if -fvisibility has not already been set
// to hidden.
if !inList("-fvisibility=hidden", flags.Local.CFlags) {
@@ -847,7 +856,7 @@
return true
}
- if p, ok := d.linker.(*vendorSnapshotLibraryDecorator); ok {
+ if p, ok := d.linker.(*snapshotLibraryDecorator); ok {
if Bool(p.properties.Sanitize_minimal_dep) {
c.sanitize.Properties.MinimalRuntimeDep = true
}
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
new file mode 100644
index 0000000..f5f8121
--- /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.HideFromMake()
+ module.Properties.HideFromMake = true
+ } else {
+ module.Disable()
+ }
+}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 238508d..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,49 +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) {
+ } 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)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: outPath,
- Description: "WriteFile " + out,
- Args: map[string]string{
- "content": content,
- },
- })
- return outPath
-}
diff --git a/cc/strip.go b/cc/strip.go
index 18150dc..b1f34bb 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -20,29 +20,49 @@
"android/soong/android"
)
+// StripProperties defines the type of stripping applied to the module.
type StripProperties struct {
Strip struct {
- None *bool `android:"arch_variant"`
- All *bool `android:"arch_variant"`
- Keep_symbols *bool `android:"arch_variant"`
- Keep_symbols_list []string `android:"arch_variant"`
- Keep_symbols_and_debug_frame *bool `android:"arch_variant"`
+ // 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"`
+
+ // all forces stripping everything, including the mini debug info.
+ All *bool `android:"arch_variant"`
+
+ // keep_symbols enables stripping but keeps all symbols.
+ Keep_symbols *bool `android:"arch_variant"`
+
+ // 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"`
+
+ // 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"`
}
+// Stripper defines the stripping actions and properties for a module.
type Stripper struct {
StripProperties StripProperties
}
+// NeedsStrip determines if stripping is required for a module.
func (stripper *Stripper) NeedsStrip(actx android.ModuleContext) bool {
- // TODO(ccross): enable host stripping when embedded in make? Make never had support for stripping host binaries.
- return (!actx.Config().EmbeddedInMake() || 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
@@ -56,15 +76,21 @@
if actx.Config().Debuggable() && !flags.StripKeepMiniDebugInfo && !isStaticLib {
flags.StripAddGnuDebuglink = true
}
- TransformStrip(actx, in, out, flags)
+ transformStrip(actx, in, out, flags)
}
}
+// StripExecutableOrSharedLib strips a binary or shared library from its debug
+// symbols and other debugging information. The helper function
+// flagsToStripFlags may be used to generate the flags argument.
func (stripper *Stripper) StripExecutableOrSharedLib(actx android.ModuleContext, in android.Path,
out android.ModuleOutPath, flags StripFlags) {
stripper.strip(actx, in, out, flags, false)
}
+// StripStaticLib strips a static library from its debug symbols and other
+// debugging information. The helper function flagsToStripFlags may be used to
+// generate the flags argument.
func (stripper *Stripper) StripStaticLib(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
flags StripFlags) {
stripper.strip(actx, in, out, flags, true)
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/sysprop.go b/cc/sysprop.go
index 6cac7fb..f578b50 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -14,6 +14,25 @@
package cc
+// This file contains a map to redirect dependencies towards sysprop_library.
+// As sysprop_library has to support both Java and C++, sysprop_library internally
+// generates cc_library and java_library. For example, the following sysprop_library
+//
+// sysprop_library {
+// name: "foo",
+// }
+//
+// will internally generate with prefix "lib"
+//
+// cc_library {
+// name: "libfoo",
+// }
+//
+// When a cc module links against "foo", build system will redirect the
+// dependency to "libfoo". To do that, SyspropMutator gathers all sysprop_library,
+// records their cc implementation library names to a map. The map will be used in
+// cc.Module.DepsMutator.
+
import (
"sync"
@@ -22,7 +41,7 @@
type syspropLibraryInterface interface {
BaseModuleName() string
- CcModuleName() string
+ CcImplementationModuleName() string
}
var (
@@ -43,6 +62,8 @@
syspropImplLibrariesLock.Lock()
defer syspropImplLibrariesLock.Unlock()
- syspropImplLibraries[m.BaseModuleName()] = m.CcModuleName()
+ // BaseModuleName is the name of sysprop_library
+ // CcImplementationModuleName is the name of cc_library generated by sysprop_library
+ syspropImplLibraries[m.BaseModuleName()] = m.CcImplementationModuleName()
}
}
diff --git a/cc/test.go b/cc/test.go
index 619dc4d..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.
@@ -46,6 +41,9 @@
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
+
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
}
type TestBinaryProperties struct {
@@ -238,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/test_data_test.go b/cc/test_data_test.go
index ae59e2f..426dfc5 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -122,10 +122,10 @@
"dir/baz": nil,
"dir/bar/baz": nil,
})
- ctx := android.NewTestContext()
+ ctx := android.NewTestContext(config)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("test", newTest)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Blueprints")
android.FailIfErrored(t, errs)
diff --git a/cc/testing.go b/cc/testing.go
index 019a186..fc5b030 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
+ "android/soong/genrule"
)
func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -24,9 +25,11 @@
RegisterBinaryBuildComponents(ctx)
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
+ genrule.RegisterGenruleBuildComponents(ctx)
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)
@@ -40,6 +43,7 @@
name: "libatomic",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
src: "",
@@ -48,6 +52,7 @@
toolchain_library {
name: "libcompiler_rt-extras",
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -55,6 +60,7 @@
toolchain_library {
name: "libclang_rt.builtins-arm-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
src: "",
@@ -63,6 +69,7 @@
toolchain_library {
name: "libclang_rt.builtins-aarch64-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
src: "",
@@ -72,6 +79,7 @@
name: "libclang_rt.hwasan-aarch64-android",
nocrt: true,
vendor_available: true,
+ product_available: true,
recovery_available: true,
system_shared_libs: [],
stl: "none",
@@ -85,6 +93,7 @@
toolchain_library {
name: "libclang_rt.builtins-i686-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
src: "",
@@ -94,6 +103,7 @@
name: "libclang_rt.builtins-x86_64-android",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
src: "",
@@ -102,6 +112,7 @@
toolchain_library {
name: "libclang_rt.fuzzer-arm-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -109,6 +120,7 @@
toolchain_library {
name: "libclang_rt.fuzzer-aarch64-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -116,6 +128,7 @@
toolchain_library {
name: "libclang_rt.fuzzer-i686-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -124,6 +137,7 @@
name: "libclang_rt.fuzzer-x86_64-android",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -131,6 +145,7 @@
toolchain_library {
name: "libclang_rt.fuzzer-x86_64",
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
}
@@ -139,6 +154,7 @@
cc_prebuilt_library_shared {
name: "libclang_rt.ubsan_standalone-aarch64-android",
vendor_available: true,
+ product_available: true,
recovery_available: true,
system_shared_libs: [],
srcs: [""],
@@ -148,14 +164,20 @@
name: "libgcc",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
src: "",
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
}
toolchain_library {
name: "libgcc_stripped",
defaults: ["linux_bionic_supported"],
vendor_available: true,
+ product_available: true,
recovery_available: true,
sdk_version: "current",
src: "",
@@ -173,7 +195,7 @@
versions: ["27", "28", "29"],
},
llndk_stubs: "libc.llndk",
-}
+ }
llndk_library {
name: "libc.llndk",
symbol_file: "",
@@ -206,6 +228,7 @@
cc_library {
name: "libprofile-extras",
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
@@ -215,6 +238,7 @@
cc_library {
name: "libprofile-clang-extras",
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_coverage: false,
system_shared_libs: [],
@@ -224,6 +248,7 @@
cc_library {
name: "libprofile-extras_ndk",
vendor_available: true,
+ product_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
@@ -233,6 +258,7 @@
cc_library {
name: "libprofile-clang-extras_ndk",
vendor_available: true,
+ product_available: true,
native_coverage: false,
system_shared_libs: [],
stl: "none",
@@ -283,6 +309,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ product_available: true,
recovery_available: true,
host_supported: true,
min_sdk_version: "29",
@@ -298,6 +325,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ product_available: true,
recovery_available: true,
host_supported: true,
min_sdk_version: "29",
@@ -307,7 +335,7 @@
},
apex_available: [
"//apex_available:platform",
- "myapex"
+ "//apex_available:anyapex",
],
}
cc_library {
@@ -318,6 +346,7 @@
stl: "none",
host_supported: false,
vendor_available: true,
+ product_available: true,
recovery_available: true,
min_sdk_version: "29",
apex_available: [
@@ -332,6 +361,7 @@
system_shared_libs: [],
stl: "none",
vendor_available: true,
+ product_available: true,
recovery_available: true,
}
@@ -340,6 +370,7 @@
defaults: ["linux_bionic_supported"],
recovery_available: true,
vendor_available: true,
+ product_available: true,
native_bridge_supported: true,
stl: "none",
min_sdk_version: "16",
@@ -407,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
@@ -521,8 +559,8 @@
return config
}
-func CreateTestContext() *android.TestContext {
- ctx := android.NewTestArchContext()
+func CreateTestContext(config android.Config) *android.TestContext {
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("cc_fuzz", FuzzFactory)
ctx.RegisterModuleType("cc_test", TestFactory)
ctx.RegisterModuleType("cc_test_library", TestLibraryFactory)
@@ -539,6 +577,7 @@
RegisterRequiredBuildComponentsForTest(ctx)
ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+ ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
return ctx
}
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 78bde38..bca76dc 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -13,477 +13,68 @@
// 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"
)
-const (
- vendorSnapshotHeaderSuffix = ".vendor_header."
- vendorSnapshotSharedSuffix = ".vendor_shared."
- vendorSnapshotStaticSuffix = ".vendor_static."
- vendorSnapshotBinarySuffix = ".vendor_binary."
- vendorSnapshotObjectSuffix = ".vendor_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)
+var vendorSnapshotSingleton = snapshotSingleton{
+ "vendor",
+ "SOONG_VENDOR_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ true,
+ vendorSnapshotImageSingleton,
}
-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 vendorSnapshotLibraryProperties 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 vendorSnapshotLibraryDecorator struct {
- vendorSnapshotModuleBase
- *libraryDecorator
- properties vendorSnapshotLibraryProperties
- sanitizerProperties struct {
- CfiEnabled bool `blueprint:"mutated"`
-
- // Library flags for cfi variant.
- Cfi vendorSnapshotLibraryProperties `android:"arch_variant"`
- }
- androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
- p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
- return p.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (p *vendorSnapshotLibraryDecorator) 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 *vendorSnapshotLibraryDecorator) 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 *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
- if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
- p.baseInstaller.install(ctx, file)
- }
-}
-
-func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
- return false
-}
-
-func (p *vendorSnapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
- switch t {
- case cfi:
- return p.sanitizerProperties.Cfi.Src != nil
- default:
- return false
- }
-}
-
-func (p *vendorSnapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
- if !enabled {
- return
- }
- switch t {
- case cfi:
- p.sanitizerProperties.CfiEnabled = true
- default:
- return
- }
-}
-
-func vendorSnapshotLibrary(suffix string) (*Module, *vendorSnapshotLibraryDecorator) {
- module, library := NewLibrary(android.DeviceSupported)
-
- module.stl = nil
- module.sanitize = nil
- library.disableStripping()
-
- prebuilt := &vendorSnapshotLibraryDecorator{
- 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 := vendorSnapshotLibrary(vendorSnapshotSharedSuffix)
- prebuilt.libraryDecorator.BuildOnlyShared()
- return module.Init()
-}
-
-func VendorSnapshotStaticFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary(vendorSnapshotStaticSuffix)
- prebuilt.libraryDecorator.BuildOnlyStatic()
- return module.Init()
-}
-
-func VendorSnapshotHeaderFactory() android.Module {
- module, prebuilt := vendorSnapshotLibrary(vendorSnapshotHeaderSuffix)
- prebuilt.libraryDecorator.HeaderOnly()
- return module.Init()
-}
-
-var _ snapshotSanitizer = (*vendorSnapshotLibraryDecorator)(nil)
-
-type vendorSnapshotBinaryProperties struct {
- // Prebuilt file for each arch.
- Src *string `android:"arch_variant"`
-}
-
-type vendorSnapshotBinaryDecorator struct {
- vendorSnapshotModuleBase
- *binaryDecorator
- properties vendorSnapshotBinaryProperties
- androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
- if config.DeviceArch() != p.arch() {
- return false
- }
- if p.properties.Src == nil {
- return false
- }
- return true
-}
-
-func (p *vendorSnapshotBinaryDecorator) 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 *vendorSnapshotBinaryDecorator) nativeCoverage() bool {
- return false
-}
-
-func VendorSnapshotBinaryFactory() 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 := &vendorSnapshotBinaryDecorator{
- binaryDecorator: binary,
- }
-
- module.compiler = nil
- module.sanitize = nil
- module.stl = nil
- module.linker = prebuilt
-
- prebuilt.init(module, vendorSnapshotBinarySuffix)
- module.AddProperties(&prebuilt.properties)
- return module.Init()
-}
-
-type vendorSnapshotObjectProperties struct {
- // Prebuilt file for each arch.
- Src *string `android:"arch_variant"`
-}
-
-type vendorSnapshotObjectLinker struct {
- vendorSnapshotModuleBase
- objectLinker
- properties vendorSnapshotObjectProperties
- androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
- if config.DeviceArch() != p.arch() {
- return false
- }
- if p.properties.Src == nil {
- return false
- }
- return true
-}
-
-func (p *vendorSnapshotObjectLinker) 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 *vendorSnapshotObjectLinker) nativeCoverage() bool {
- return false
-}
-
-func VendorSnapshotObjectFactory() android.Module {
- module := newObject()
-
- prebuilt := &vendorSnapshotObjectLinker{
- objectLinker: objectLinker{
- baseLinker: NewBaseLinker(nil),
- },
- }
- module.linker = prebuilt
-
- prebuilt.init(module, vendorSnapshotObjectSuffix)
- module.AddProperties(&prebuilt.properties)
- return module.Init()
-}
-
-func 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)
+var recoverySnapshotSingleton = snapshotSingleton{
+ "recovery",
+ "SOONG_RECOVERY_SNAPSHOT_ZIP",
+ android.OptionalPath{},
+ false,
+ recoverySnapshotImageSingleton,
}
func VendorSnapshotSingleton() android.Singleton {
- return &vendorSnapshotSingleton{}
+ return &vendorSnapshotSingleton
}
-type vendorSnapshotSingleton struct {
- vendorSnapshotZipFile android.OptionalPath
+func RecoverySnapshotSingleton() android.Singleton {
+ return &recoverySnapshotSingleton
+}
+
+type snapshotSingleton struct {
+ // Name, e.g., "vendor", "recovery", "ramdisk".
+ name string
+
+ // Make variable that points to the snapshot file, e.g.,
+ // "SOONG_RECOVERY_SNAPSHOT_ZIP".
+ makeVar string
+
+ // Path to the snapshot zip file.
+ snapshotZipFile android.OptionalPath
+
+ // Whether the image supports VNDK extension modules.
+ supportsVndkExt bool
+
+ // Implementation of the image interface specific to the image
+ // associated with this snapshot (e.g., specific to the vendor image,
+ // recovery image, etc.).
+ 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",
@@ -491,9 +82,18 @@
"hardware",
}
+ // Modules under following directories are ignored. They are OEM's and vendor's
+ // proprietary modules(device/, kernel/, vendor/, and hardware/).
+ recoveryProprietaryDirs = []string{
+ "bootable/recovery",
+ "device",
+ "hardware",
+ "kernel",
+ "vendor",
+ }
+
// 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",
@@ -508,7 +108,17 @@
// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
// device/, vendor/, etc.
func isVendorProprietaryPath(dir string) bool {
- for _, p := range vendorProprietaryDirs {
+ return isProprietaryPath(dir, vendorProprietaryDirs)
+}
+
+func isRecoveryProprietaryPath(dir string) bool {
+ return isProprietaryPath(dir, recoveryProprietaryDirs)
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
+// device/, vendor/, etc.
+func isProprietaryPath(dir string, proprietaryDirs []string) bool {
+ for _, p := range proprietaryDirs {
if strings.HasPrefix(dir, p) {
// filter out AOSP defined directories, e.g. hardware/interfaces/
aosp := false
@@ -527,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
}
@@ -539,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
@@ -555,17 +162,32 @@
// 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 {
+func isVendorSnapshotAware(m *Module, inVendorProprietaryPath bool, apexInfo android.ApexInfo) bool {
+ return isSnapshotAware(m, inVendorProprietaryPath, apexInfo, vendorSnapshotImageSingleton)
+}
+
+// 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)
+}
+
+// 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
}
// When android/prebuilt.go selects between source and prebuilt, it sets
- // SkipInstall on the other one to avoid duplicate install rules in make.
- if m.IsSkipInstall() {
+ // HideFromMake on the other one to avoid duplicate install rules in make.
+ if m.IsHideFromMake() {
return false
}
- // skip proprietary modules, but include all VNDK (static)
- if inVendorProprietaryPath && !m.IsVndk() {
+ // skip proprietary modules, but (for the vendor snapshot only)
+ // include all VNDK (static)
+ if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) {
return false
}
// If the module would be included based on its path, check to see if
@@ -579,8 +201,8 @@
if m.Target().NativeBridge == android.NativeBridgeEnabled {
return false
}
- // the module must be installed in /vendor
- if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !m.inVendor() {
+ // the module must be installed in target image
+ if !apexInfo.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
return false
}
// skip kernel_headers which always depend on vendor
@@ -597,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.
@@ -607,34 +228,65 @@
}
}
// cfi also exports both variants. But for static, we capture both.
+ // This is because cfi static libraries can't be linked from non-cfi modules,
+ // and vice versa. This isn't the case for scs and hwasan sanitizers.
if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
return false
}
}
if l.static() {
- return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+ return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
}
if l.shared() {
if !m.outputFile.Valid() {
return false
}
- if !m.IsVndk() {
- return true
+ if image.includeVndk() {
+ if !m.IsVndk() {
+ return true
+ }
+ return m.IsVndkExt()
}
- return m.isVndkExt()
}
return true
}
// Binaries and Objects
if m.binary() || m.object() {
- return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+ return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
}
return false
}
-func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+// 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" {
return
@@ -675,7 +327,7 @@
(header files of same directory structure with source tree)
*/
- snapshotDir := "vendor-snapshot"
+ snapshotDir := c.name + "-snapshot"
snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
includeDir := filepath.Join(snapshotArchDir, "include")
@@ -687,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 != "" {
@@ -695,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 m.isVndkExt() {
+ if c.supportsVndkExt && m.IsVndkExt() {
// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
if m.isVndkSp() {
prop.RelativeInstallPath = "vndk-sp"
@@ -746,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))
}
}
@@ -797,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)
}
@@ -811,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.
@@ -819,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())
@@ -831,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
}
@@ -843,30 +474,37 @@
}
moduleDir := ctx.ModuleDir(module)
- inVendorProprietaryPath := isVendorProprietaryPath(moduleDir)
+ inProprietaryPath := c.image.isProprietaryPath(moduleDir)
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
if m.ExcludeFromVendorSnapshot() {
- if inVendorProprietaryPath {
+ if inProprietaryPath {
// Error: exclude_from_vendor_snapshot applies
// to framework-path modules only.
ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
return
}
- if Bool(m.VendorProperties.Vendor_available) {
+ if Bool(c.image.available(m)) {
// Error: may not combine "vendor_available:
// true" with "exclude_from_vendor_snapshot:
// true".
- ctx.Errorf("module %q may not use both \"vendor_available: true\" and \"exclude_from_vendor_snapshot: true\"", m.String())
+ ctx.Errorf(
+ "module %q may not use both \""+
+ c.name+
+ "_available: true\" and \"exclude_from_vendor_snapshot: true\"",
+ m.String())
return
}
}
- if !isVendorSnapshotModule(m, inVendorProprietaryPath, apexInfo) {
+ 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()...)
}
@@ -877,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))
}
}
@@ -885,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())))
}
@@ -894,11 +532,17 @@
return snapshotOutputs[i].String() < snapshotOutputs[j].String()
})
- zipPath := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+".zip")
- zipRule := android.NewRuleBuilder()
+ zipPath := android.PathForOutput(
+ ctx,
+ snapshotDir,
+ c.name+"-"+ctx.Config().DeviceName()+".zip")
+ zipRule := android.NewRuleBuilder(pctx, ctx)
// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
- snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+"_list")
+ snapshotOutputList := android.PathForOutput(
+ ctx,
+ snapshotDir,
+ c.name+"-"+ctx.Config().DeviceName()+"_list")
zipRule.Command().
Text("tr").
FlagWithArg("-d ", "\\'").
@@ -908,154 +552,18 @@
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(), "vendor snapshot "+zipPath.String())
+ zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
- c.vendorSnapshotZipFile = android.OptionalPathForPath(zipPath)
+ c.snapshotZipFile = android.OptionalPathForPath(zipPath)
}
-func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
- ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String())
-}
-
-type snapshotInterface interface {
- matchesWithDevice(config android.DeviceConfig) bool
-}
-
-var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotObjectLinker)(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.(*vendorSnapshotBinaryDecorator); ok {
- snapshotMap = vendorSnapshotBinaries(ctx.Config())
- } else if _, ok := module.linker.(*vendorSnapshotObjectLinker); 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()
- }
+func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+ ctx.Strict(
+ c.makeVar,
+ c.snapshotZipFile.String())
}
diff --git a/cc/vndk.go b/cc/vndk.go
index b2614c5..1529ac5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -62,8 +62,8 @@
// declared as a VNDK or VNDK-SP module. The vendor variant
// will be installed in /system instead of /vendor partition.
//
- // `vendor_available` must be explicitly set to either true or
- // false together with `vndk: {enabled: true}`.
+ // `vendor_available` and `product_available` must be explicitly
+ // set to either true or false together with `vndk: {enabled: true}`.
Enabled *bool
// declared as a VNDK-SP module, which is a subset of VNDK.
@@ -129,13 +129,16 @@
return "native:vendor:vndkspext"
}
+// VNDK link type check from a module with UseVndk() == true.
func (vndk *vndkdep) vndkCheckLinkType(ctx android.BaseModuleContext, to *Module, tag blueprint.DependencyTag) {
if to.linker == nil {
return
}
if !vndk.isVndk() {
- // Non-VNDK modules (those installed to /vendor, /product, or /system/product) can't depend
- // on modules marked with vendor_available: false.
+ // Non-VNDK modules those installed to /vendor or /system/vendor
+ // can't depend on modules marked with vendor_available: false;
+ // or those installed to /product or /system/product can't depend
+ // on modules marked with product_available: false.
violation := false
if lib, ok := to.linker.(*llndkStubDecorator); ok && !Bool(lib.Properties.Vendor_available) {
violation = true
@@ -174,6 +177,7 @@
to.Name())
return
}
+ // TODO(b/150902910): vndk-ext for product must check product_available.
if !Bool(to.VendorProperties.Vendor_available) {
ctx.ModuleErrorf(
"`extends` refers module %q which does not have `vendor_available: true`",
@@ -338,6 +342,8 @@
} else {
vndkCoreLibraries(mctx.Config())[name] = filename
}
+ // As `vendor_available` and `product_available` has the same value for VNDK modules,
+ // we don't need to check both values.
if !Bool(m.VendorProperties.Vendor_available) {
vndkPrivateLibraries(mctx.Config())[name] = filename
}
@@ -383,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
}
@@ -481,14 +487,7 @@
}
txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: txt.outputFile,
- Description: "Writing " + txt.outputFile.String(),
- Args: map[string]string{
- "content": strings.Join(list, "\\n"),
- },
- })
+ android.WriteFileRule(ctx, txt.outputFile, strings.Join(list, "\n"))
installPath := android.PathForModuleInstall(ctx, "etc")
ctx.InstallFile(installPath, filename, txt.outputFile)
@@ -534,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 {
@@ -550,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 {
@@ -623,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
@@ -633,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 {
@@ -655,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
}
@@ -668,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)
@@ -691,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)))
}
}
@@ -703,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())))
}
@@ -713,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,
@@ -755,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 {
@@ -763,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").
@@ -776,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)
}
@@ -819,14 +811,7 @@
merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: c.vndkLibrariesFile,
- Description: "Writing " + c.vndkLibrariesFile.String(),
- Args: map[string]string{
- "content": strings.Join(merged, "\\n"),
- },
- })
+ android.WriteFileRule(ctx, c.vndkLibrariesFile, strings.Join(merged, "\n"))
}
func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index dddd5ac..04162cd 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -34,6 +34,7 @@
// version: "27",
// target_arch: "arm64",
// vendor_available: true,
+// product_available: true,
// vndk: {
// enabled: true,
// },
@@ -52,7 +53,7 @@
// VNDK snapshot version.
Version *string
- // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
+ // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
Target_arch *string
// If the prebuilt snapshot lib is built with 32 bit binder, this must be set to true.
@@ -129,7 +130,7 @@
flags Flags, deps PathDeps, objs Objects) android.Path {
if !p.matchesWithDevice(ctx.DeviceConfig()) {
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
return nil
}
@@ -153,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()
@@ -174,7 +175,7 @@
return in
}
- ctx.Module().SkipInstall()
+ ctx.Module().HideFromMake()
return nil
}
@@ -251,6 +252,7 @@
// version: "27",
// target_arch: "arm64",
// vendor_available: true,
+// product_available: true,
// vndk: {
// enabled: true,
// },
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/Android.bp b/cmd/sbox/Android.bp
index 6fa304e..f5e87c0 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -14,8 +14,20 @@
blueprint_go_binary {
name: "sbox",
- deps: ["soong-makedeps"],
+ deps: [
+ "sbox_proto",
+ "soong-makedeps",
+ ],
srcs: [
"sbox.go",
],
}
+
+bootstrap_go_package {
+ name: "sbox_proto",
+ pkgPath: "android/soong/cmd/sbox/sbox_proto",
+ deps: ["golang-protobuf-proto"],
+ srcs: [
+ "sbox_proto/sbox.pb.go",
+ ],
+}
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 65a34fd..f8919a4 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -16,44 +16,44 @@
import (
"bytes"
+ "crypto/sha1"
+ "encoding/hex"
"errors"
"flag"
"fmt"
+ "io"
"io/ioutil"
"os"
"os/exec"
- "path"
"path/filepath"
+ "strconv"
"strings"
"time"
+ "android/soong/cmd/sbox/sbox_proto"
"android/soong/makedeps"
+
+ "github.com/golang/protobuf/proto"
)
var (
sandboxesRoot string
- rawCommand string
- outputRoot string
+ manifestFile string
keepOutDir bool
- depfileOut string
- inputHash string
+)
+
+const (
+ depFilePlaceholder = "__SBOX_DEPFILE__"
+ sandboxDirPlaceholder = "__SBOX_SANDBOX_DIR__"
)
func init() {
flag.StringVar(&sandboxesRoot, "sandbox-path", "",
"root of temp directory to put the sandbox into")
- flag.StringVar(&rawCommand, "c", "",
- "command to run")
- flag.StringVar(&outputRoot, "output-root", "",
- "root of directory to copy outputs into")
+ flag.StringVar(&manifestFile, "manifest", "",
+ "textproto manifest describing the sandboxed command(s)")
flag.BoolVar(&keepOutDir, "keep-out-dir", false,
"whether to keep the sandbox directory when done")
-
- flag.StringVar(&depfileOut, "depfile-out", "",
- "file path of the depfile to generate. This value will replace '__SBOX_DEPFILE__' in the command and will be treated as an output but won't be added to __SBOX_OUT_FILES__")
-
- flag.StringVar(&inputHash, "input-hash", "",
- "This option is ignored. Typical usage is to supply a hash of the list of input names so that the module will be rebuilt if the list (and thus the hash) changes.")
}
func usageViolation(violation string) {
@@ -62,11 +62,7 @@
}
fmt.Fprintf(os.Stderr,
- "Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] [--input-hash hash] <outputFile> [<outputFile>...]\n"+
- "\n"+
- "Deletes <outputRoot>,"+
- "runs <commandToRun>,"+
- "and moves each <outputFile> out of <sandboxPath> and into <outputRoot>\n")
+ "Usage: sbox --manifest <manifest> --sandbox-path <sandboxPath>\n")
flag.PrintDefaults()
@@ -103,8 +99,8 @@
}
func run() error {
- if rawCommand == "" {
- usageViolation("-c <commandToRun> is required and must be non-empty")
+ if manifestFile == "" {
+ usageViolation("--manifest <manifest> is required and must be non-empty")
}
if sandboxesRoot == "" {
// In practice, the value of sandboxesRoot will mostly likely be at a fixed location relative to OUT_DIR,
@@ -114,61 +110,43 @@
// and by passing it as a parameter we don't need to duplicate its value
usageViolation("--sandbox-path <sandboxPath> is required and must be non-empty")
}
- if len(outputRoot) == 0 {
- usageViolation("--output-root <outputRoot> is required and must be non-empty")
+
+ manifest, err := readManifest(manifestFile)
+
+ if len(manifest.Commands) == 0 {
+ return fmt.Errorf("at least one commands entry is required in %q", manifestFile)
}
- // the contents of the __SBOX_OUT_FILES__ variable
- outputsVarEntries := flag.Args()
- if len(outputsVarEntries) == 0 {
- usageViolation("at least one output file must be given")
+ // setup sandbox directory
+ err = os.MkdirAll(sandboxesRoot, 0777)
+ if err != nil {
+ return fmt.Errorf("failed to create %q: %w", sandboxesRoot, err)
}
- // all outputs
- var allOutputs []string
+ // This tool assumes that there are no two concurrent runs with the same
+ // manifestFile. It should therefore be safe to use the hash of the
+ // manifestFile as the temporary directory name. We do this because it
+ // makes the temporary directory name deterministic. There are some
+ // tools that embed the name of the temporary output in the output, and
+ // they otherwise cause non-determinism, which then poisons actions
+ // depending on this one.
+ hash := sha1.New()
+ hash.Write([]byte(manifestFile))
+ tempDir := filepath.Join(sandboxesRoot, "sbox", hex.EncodeToString(hash.Sum(nil)))
- // setup directories
- err := os.MkdirAll(sandboxesRoot, 0777)
+ err = os.RemoveAll(tempDir)
if err != nil {
return err
}
- err = os.RemoveAll(outputRoot)
+ err = os.MkdirAll(tempDir, 0777)
if err != nil {
- return err
- }
- err = os.MkdirAll(outputRoot, 0777)
- if err != nil {
- return err
- }
-
- tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox")
-
- for i, filePath := range outputsVarEntries {
- if !strings.HasPrefix(filePath, "__SBOX_OUT_DIR__/") {
- return fmt.Errorf("output files must start with `__SBOX_OUT_DIR__/`")
- }
- outputsVarEntries[i] = strings.TrimPrefix(filePath, "__SBOX_OUT_DIR__/")
- }
-
- allOutputs = append([]string(nil), outputsVarEntries...)
-
- if depfileOut != "" {
- sandboxedDepfile, err := filepath.Rel(outputRoot, depfileOut)
- if err != nil {
- return err
- }
- allOutputs = append(allOutputs, sandboxedDepfile)
- rawCommand = strings.Replace(rawCommand, "__SBOX_DEPFILE__", filepath.Join(tempDir, sandboxedDepfile), -1)
-
- }
-
- if err != nil {
- return fmt.Errorf("Failed to create temp dir: %s", err)
+ return fmt.Errorf("failed to create temporary dir in %q: %w", sandboxesRoot, err)
}
// In the common case, the following line of code is what removes the sandbox
// If a fatal error occurs (such as if our Go process is killed unexpectedly),
- // then at the beginning of the next build, Soong will retry the cleanup
+ // then at the beginning of the next build, Soong will wipe the temporary
+ // directory.
defer func() {
// in some cases we decline to remove the temp dir, to facilitate debugging
if !keepOutDir {
@@ -176,27 +154,95 @@
}
}()
- if strings.Contains(rawCommand, "__SBOX_OUT_DIR__") {
- rawCommand = strings.Replace(rawCommand, "__SBOX_OUT_DIR__", tempDir, -1)
- }
+ // If there is more than one command in the manifest use a separate directory for each one.
+ useSubDir := len(manifest.Commands) > 1
+ var commandDepFiles []string
- if strings.Contains(rawCommand, "__SBOX_OUT_FILES__") {
- // expands into a space-separated list of output files to be generated into the sandbox directory
- tempOutPaths := []string{}
- for _, outputPath := range outputsVarEntries {
- tempOutPath := path.Join(tempDir, outputPath)
- tempOutPaths = append(tempOutPaths, tempOutPath)
+ for i, command := range manifest.Commands {
+ localTempDir := tempDir
+ if useSubDir {
+ localTempDir = filepath.Join(localTempDir, strconv.Itoa(i))
}
- pathsText := strings.Join(tempOutPaths, " ")
- rawCommand = strings.Replace(rawCommand, "__SBOX_OUT_FILES__", pathsText, -1)
- }
-
- for _, filePath := range allOutputs {
- dir := path.Join(tempDir, filepath.Dir(filePath))
- err = os.MkdirAll(dir, 0777)
+ depFile, err := runCommand(command, localTempDir)
if err != nil {
+ // Running the command failed, keep the temporary output directory around in
+ // case a user wants to inspect it for debugging purposes. Soong will delete
+ // it at the beginning of the next build anyway.
+ keepOutDir = true
return err
}
+ if depFile != "" {
+ commandDepFiles = append(commandDepFiles, depFile)
+ }
+ }
+
+ outputDepFile := manifest.GetOutputDepfile()
+ if len(commandDepFiles) > 0 && outputDepFile == "" {
+ return fmt.Errorf("Sandboxed commands used %s but output depfile is not set in manifest file",
+ depFilePlaceholder)
+ }
+
+ if outputDepFile != "" {
+ // Merge the depfiles from each command in the manifest to a single output depfile.
+ err = rewriteDepFiles(commandDepFiles, outputDepFile)
+ if err != nil {
+ return fmt.Errorf("failed merging depfiles: %w", err)
+ }
+ }
+
+ return nil
+}
+
+// readManifest reads an sbox manifest from a textproto file.
+func readManifest(file string) (*sbox_proto.Manifest, error) {
+ manifestData, err := ioutil.ReadFile(file)
+ if err != nil {
+ return nil, fmt.Errorf("error reading manifest %q: %w", file, err)
+ }
+
+ manifest := sbox_proto.Manifest{}
+
+ err = proto.UnmarshalText(string(manifestData), &manifest)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing manifest %q: %w", file, err)
+ }
+
+ return &manifest, nil
+}
+
+// runCommand runs a single command from a manifest. If the command references the
+// __SBOX_DEPFILE__ placeholder it returns the name of the depfile that was used.
+func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, err error) {
+ rawCommand := command.GetCommand()
+ if rawCommand == "" {
+ return "", fmt.Errorf("command is required")
+ }
+
+ err = os.MkdirAll(tempDir, 0777)
+ if err != nil {
+ return "", fmt.Errorf("failed to create %q: %w", tempDir, err)
+ }
+
+ // Copy in any files specified by the manifest.
+ err = copyFiles(command.CopyBefore, "", tempDir)
+ if err != nil {
+ return "", err
+ }
+
+ if strings.Contains(rawCommand, depFilePlaceholder) {
+ depFile = filepath.Join(tempDir, "deps.d")
+ rawCommand = strings.Replace(rawCommand, depFilePlaceholder, depFile, -1)
+ }
+
+ if strings.Contains(rawCommand, sandboxDirPlaceholder) {
+ rawCommand = strings.Replace(rawCommand, sandboxDirPlaceholder, tempDir, -1)
+ }
+
+ // Emulate ninja's behavior of creating the directories for any output files before
+ // running the command.
+ err = makeOutputDirs(command.CopyAfter, tempDir)
+ if err != nil {
+ return "", err
}
commandDescription := rawCommand
@@ -205,27 +251,20 @@
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
+
+ if command.GetChdir() {
+ cmd.Dir = tempDir
+ }
err = cmd.Run()
if exit, ok := err.(*exec.ExitError); ok && !exit.Success() {
- return fmt.Errorf("sbox command (%s) failed with err %#v\n", commandDescription, err.Error())
+ return "", fmt.Errorf("sbox command failed with err:\n%s\n%w\n", commandDescription, err)
} else if err != nil {
- return err
+ return "", err
}
- // validate that all files are created properly
- var missingOutputErrors []string
- for _, filePath := range allOutputs {
- tempPath := filepath.Join(tempDir, filePath)
- fileInfo, err := os.Stat(tempPath)
- if err != nil {
- missingOutputErrors = append(missingOutputErrors, fmt.Sprintf("%s: does not exist", filePath))
- continue
- }
- if fileInfo.IsDir() {
- missingOutputErrors = append(missingOutputErrors, fmt.Sprintf("%s: not a file", filePath))
- }
- }
+ missingOutputErrors := validateOutputFiles(command.CopyAfter, tempDir)
+
if len(missingOutputErrors) > 0 {
// find all created files for making a more informative error message
createdFiles := findAllFilesUnder(tempDir)
@@ -236,7 +275,7 @@
errorMessage += "in sandbox " + tempDir + ",\n"
errorMessage += fmt.Sprintf("failed to create %v files:\n", len(missingOutputErrors))
for _, missingOutputError := range missingOutputErrors {
- errorMessage += " " + missingOutputError + "\n"
+ errorMessage += " " + missingOutputError.Error() + "\n"
}
if len(createdFiles) < 1 {
errorMessage += "created 0 files."
@@ -253,19 +292,123 @@
}
}
- // Keep the temporary output directory around in case a user wants to inspect it for debugging purposes.
- // Soong will delete it later anyway.
- keepOutDir = true
- return errors.New(errorMessage)
+ return "", errors.New(errorMessage)
}
// the created files match the declared files; now move them
- for _, filePath := range allOutputs {
- tempPath := filepath.Join(tempDir, filePath)
- destPath := filePath
- if len(outputRoot) != 0 {
- destPath = filepath.Join(outputRoot, filePath)
+ err = moveFiles(command.CopyAfter, tempDir, "")
+
+ return depFile, nil
+}
+
+// makeOutputDirs creates directories in the sandbox dir for every file that has a rule to be copied
+// out of the sandbox. This emulate's Ninja's behavior of creating directories for output files
+// so that the tools don't have to.
+func makeOutputDirs(copies []*sbox_proto.Copy, sandboxDir string) error {
+ for _, copyPair := range copies {
+ dir := joinPath(sandboxDir, filepath.Dir(copyPair.GetFrom()))
+ err := os.MkdirAll(dir, 0777)
+ if err != nil {
+ return err
}
- err := os.MkdirAll(filepath.Dir(destPath), 0777)
+ }
+ return nil
+}
+
+// validateOutputFiles verifies that all files that have a rule to be copied out of the sandbox
+// were created by the command.
+func validateOutputFiles(copies []*sbox_proto.Copy, sandboxDir string) []error {
+ var missingOutputErrors []error
+ for _, copyPair := range copies {
+ fromPath := joinPath(sandboxDir, copyPair.GetFrom())
+ fileInfo, err := os.Stat(fromPath)
+ if err != nil {
+ missingOutputErrors = append(missingOutputErrors, fmt.Errorf("%s: does not exist", fromPath))
+ continue
+ }
+ if fileInfo.IsDir() {
+ missingOutputErrors = append(missingOutputErrors, fmt.Errorf("%s: not a file", fromPath))
+ }
+ }
+ return missingOutputErrors
+}
+
+// copyFiles copies files in or out of the sandbox.
+func copyFiles(copies []*sbox_proto.Copy, fromDir, toDir string) error {
+ for _, copyPair := range copies {
+ fromPath := joinPath(fromDir, copyPair.GetFrom())
+ toPath := joinPath(toDir, copyPair.GetTo())
+ err := copyOneFile(fromPath, toPath, copyPair.GetExecutable())
+ if err != nil {
+ return fmt.Errorf("error copying %q to %q: %w", fromPath, toPath, err)
+ }
+ }
+ return nil
+}
+
+// copyOneFile copies a file.
+func copyOneFile(from string, to string, executable bool) error {
+ err := os.MkdirAll(filepath.Dir(to), 0777)
+ if err != nil {
+ return err
+ }
+
+ stat, err := os.Stat(from)
+ if err != nil {
+ return err
+ }
+
+ perm := stat.Mode()
+ if executable {
+ perm = perm | 0100 // u+x
+ }
+
+ in, err := os.Open(from)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ out, err := os.Create(to)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ out.Close()
+ if err != nil {
+ os.Remove(to)
+ }
+ }()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return err
+ }
+
+ if err = out.Close(); err != nil {
+ return err
+ }
+
+ if err = os.Chmod(to, perm); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// moveFiles moves files specified by a set of copy rules. It uses os.Rename, so it is restricted
+// to moving files where the source and destination are in the same filesystem. This is OK for
+// sbox because the temporary directory is inside the out directory. It updates the timestamp
+// of the new file.
+func moveFiles(copies []*sbox_proto.Copy, fromDir, toDir string) error {
+ for _, copyPair := range copies {
+ fromPath := joinPath(fromDir, copyPair.GetFrom())
+ toPath := joinPath(toDir, copyPair.GetTo())
+ err := os.MkdirAll(filepath.Dir(toPath), 0777)
+ if err != nil {
+ return err
+ }
+
+ err = os.Rename(fromPath, toPath)
if err != nil {
return err
}
@@ -273,37 +416,53 @@
// Update the timestamp of the output file in case the tool wrote an old timestamp (for example, tar can extract
// files with old timestamps).
now := time.Now()
- err = os.Chtimes(tempPath, now, now)
- if err != nil {
- return err
- }
-
- err = os.Rename(tempPath, destPath)
+ err = os.Chtimes(toPath, now, now)
if err != nil {
return err
}
}
-
- // Rewrite the depfile so that it doesn't include the (randomized) sandbox directory
- if depfileOut != "" {
- in, err := ioutil.ReadFile(depfileOut)
- if err != nil {
- return err
- }
-
- deps, err := makedeps.Parse(depfileOut, bytes.NewBuffer(in))
- if err != nil {
- return err
- }
-
- deps.Output = "outputfile"
-
- err = ioutil.WriteFile(depfileOut, deps.Print(), 0666)
- if err != nil {
- return err
- }
- }
-
- // TODO(jeffrygaston) if a process creates more output files than it declares, should there be a warning?
return nil
}
+
+// Rewrite one or more depfiles so that it doesn't include the (randomized) sandbox directory
+// to an output file.
+func rewriteDepFiles(ins []string, out string) error {
+ var mergedDeps []string
+ for _, in := range ins {
+ data, err := ioutil.ReadFile(in)
+ if err != nil {
+ return err
+ }
+
+ deps, err := makedeps.Parse(in, bytes.NewBuffer(data))
+ if err != nil {
+ return err
+ }
+ mergedDeps = append(mergedDeps, deps.Inputs...)
+ }
+
+ deps := makedeps.Deps{
+ // Ninja doesn't care what the output file is, so we can use any string here.
+ Output: "outputfile",
+ Inputs: mergedDeps,
+ }
+
+ // Make the directory for the output depfile in case it is in a different directory
+ // than any of the output files.
+ outDir := filepath.Dir(out)
+ err := os.MkdirAll(outDir, 0777)
+ if err != nil {
+ return fmt.Errorf("failed to create %q: %w", outDir, err)
+ }
+
+ return ioutil.WriteFile(out, deps.Print(), 0666)
+}
+
+// joinPath wraps filepath.Join but returns file without appending to dir if file is
+// absolute.
+func joinPath(dir, file string) string {
+ if filepath.IsAbs(file) {
+ return file
+ }
+ return filepath.Join(dir, file)
+}
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
new file mode 100644
index 0000000..79bb90c
--- /dev/null
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -0,0 +1,243 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: sbox.proto
+
+package sbox_proto
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+// A set of commands to run in a sandbox.
+type Manifest struct {
+ // A list of commands to run in the sandbox.
+ Commands []*Command `protobuf:"bytes,1,rep,name=commands" json:"commands,omitempty"`
+ // If set, GCC-style dependency files from any command that references __SBOX_DEPFILE__ will be
+ // merged into the given output file relative to the $PWD when sbox was started.
+ OutputDepfile *string `protobuf:"bytes,2,opt,name=output_depfile,json=outputDepfile" json:"output_depfile,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Manifest) Reset() { *m = Manifest{} }
+func (m *Manifest) String() string { return proto.CompactTextString(m) }
+func (*Manifest) ProtoMessage() {}
+func (*Manifest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{0}
+}
+
+func (m *Manifest) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Manifest.Unmarshal(m, b)
+}
+func (m *Manifest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Manifest.Marshal(b, m, deterministic)
+}
+func (m *Manifest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Manifest.Merge(m, src)
+}
+func (m *Manifest) XXX_Size() int {
+ return xxx_messageInfo_Manifest.Size(m)
+}
+func (m *Manifest) XXX_DiscardUnknown() {
+ xxx_messageInfo_Manifest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Manifest proto.InternalMessageInfo
+
+func (m *Manifest) GetCommands() []*Command {
+ if m != nil {
+ return m.Commands
+ }
+ return nil
+}
+
+func (m *Manifest) GetOutputDepfile() string {
+ if m != nil && m.OutputDepfile != nil {
+ return *m.OutputDepfile
+ }
+ return ""
+}
+
+// SandboxManifest describes a command to run in the sandbox.
+type Command struct {
+ // A list of copy rules to run before the sandboxed command. The from field is relative to the
+ // $PWD when sbox was run, the to field is relative to the top of the temporary sandbox directory.
+ CopyBefore []*Copy `protobuf:"bytes,1,rep,name=copy_before,json=copyBefore" json:"copy_before,omitempty"`
+ // If true, change the working directory to the top of the temporary sandbox directory before
+ // running the command. If false, leave the working directory where it was when sbox was started.
+ Chdir *bool `protobuf:"varint,2,opt,name=chdir" json:"chdir,omitempty"`
+ // The command to run.
+ Command *string `protobuf:"bytes,3,req,name=command" json:"command,omitempty"`
+ // A list of copy rules to run after the sandboxed command. The from field is relative to the
+ // top of the temporary sandbox directory, the to field is relative to the $PWD when sbox was run.
+ CopyAfter []*Copy `protobuf:"bytes,4,rep,name=copy_after,json=copyAfter" json:"copy_after,omitempty"`
+ // An optional hash of the input files to ensure the textproto files and the sbox rule reruns
+ // when the lists of inputs changes, even if the inputs are not on the command line.
+ InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Command) Reset() { *m = Command{} }
+func (m *Command) String() string { return proto.CompactTextString(m) }
+func (*Command) ProtoMessage() {}
+func (*Command) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{1}
+}
+
+func (m *Command) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Command.Unmarshal(m, b)
+}
+func (m *Command) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Command.Marshal(b, m, deterministic)
+}
+func (m *Command) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Command.Merge(m, src)
+}
+func (m *Command) XXX_Size() int {
+ return xxx_messageInfo_Command.Size(m)
+}
+func (m *Command) XXX_DiscardUnknown() {
+ xxx_messageInfo_Command.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Command proto.InternalMessageInfo
+
+func (m *Command) GetCopyBefore() []*Copy {
+ if m != nil {
+ return m.CopyBefore
+ }
+ return nil
+}
+
+func (m *Command) GetChdir() bool {
+ if m != nil && m.Chdir != nil {
+ return *m.Chdir
+ }
+ return false
+}
+
+func (m *Command) GetCommand() string {
+ if m != nil && m.Command != nil {
+ return *m.Command
+ }
+ return ""
+}
+
+func (m *Command) GetCopyAfter() []*Copy {
+ if m != nil {
+ return m.CopyAfter
+ }
+ return nil
+}
+
+func (m *Command) GetInputHash() string {
+ if m != nil && m.InputHash != nil {
+ return *m.InputHash
+ }
+ return ""
+}
+
+// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
+// 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"`
+ // 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:"-"`
+}
+
+func (m *Copy) Reset() { *m = Copy{} }
+func (m *Copy) String() string { return proto.CompactTextString(m) }
+func (*Copy) ProtoMessage() {}
+func (*Copy) Descriptor() ([]byte, []int) {
+ return fileDescriptor_9d0425bf0de86ed1, []int{2}
+}
+
+func (m *Copy) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Copy.Unmarshal(m, b)
+}
+func (m *Copy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Copy.Marshal(b, m, deterministic)
+}
+func (m *Copy) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Copy.Merge(m, src)
+}
+func (m *Copy) XXX_Size() int {
+ return xxx_messageInfo_Copy.Size(m)
+}
+func (m *Copy) XXX_DiscardUnknown() {
+ xxx_messageInfo_Copy.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Copy proto.InternalMessageInfo
+
+func (m *Copy) GetFrom() string {
+ if m != nil && m.From != nil {
+ return *m.From
+ }
+ return ""
+}
+
+func (m *Copy) GetTo() string {
+ if m != nil && m.To != nil {
+ return *m.To
+ }
+ 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")
+ proto.RegisterType((*Copy)(nil), "sbox.Copy")
+}
+
+func init() {
+ proto.RegisterFile("sbox.proto", fileDescriptor_9d0425bf0de86ed1)
+}
+
+var fileDescriptor_9d0425bf0de86ed1 = []byte{
+ // 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
new file mode 100644
index 0000000..695b0e8
--- /dev/null
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -0,0 +1,61 @@
+// 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.
+
+syntax = "proto2";
+
+package sbox;
+option go_package = "sbox_proto";
+
+// A set of commands to run in a sandbox.
+message Manifest {
+ // A list of commands to run in the sandbox.
+ repeated Command commands = 1;
+
+ // If set, GCC-style dependency files from any command that references __SBOX_DEPFILE__ will be
+ // merged into the given output file relative to the $PWD when sbox was started.
+ optional string output_depfile = 2;
+}
+
+// SandboxManifest describes a command to run in the sandbox.
+message Command {
+ // A list of copy rules to run before the sandboxed command. The from field is relative to the
+ // $PWD when sbox was run, the to field is relative to the top of the temporary sandbox directory.
+ repeated Copy copy_before = 1;
+
+ // If true, change the working directory to the top of the temporary sandbox directory before
+ // running the command. If false, leave the working directory where it was when sbox was started.
+ optional bool chdir = 2;
+
+ // The command to run.
+ required string command = 3;
+
+ // A list of copy rules to run after the sandboxed command. The from field is relative to the
+ // top of the temporary sandbox directory, the to field is relative to the $PWD when sbox was run.
+ repeated Copy copy_after = 4;
+
+ // An optional hash of the input files to ensure the textproto files and the sbox rule reruns
+ // when the lists of inputs changes, even if the inputs are not on the command line.
+ optional string input_hash = 5;
+}
+
+// Copy describes a from-to pair of files to copy. The paths may be relative, the root that they
+// are relative to is specific to the context the Copy is used in and will be different for
+// from and to.
+message Copy {
+ required string from = 1;
+ required string to = 2;
+
+ // If true, make the file executable after copying it.
+ optional bool executable = 3;
+}
\ No newline at end of file
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 4ebbe68..441ea0d 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -26,10 +26,11 @@
srcs: [
"main.go",
"writedocs.go",
- "bazel_overlay.go",
+ "queryview.go",
+ "queryview_templates.go",
],
testSrcs: [
- "bazel_overlay_test.go",
+ "queryview_test.go",
],
primaryBuilder: true,
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 7ae1c37..907bed3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,13 +26,13 @@
)
var (
- docFile string
- bazelOverlayDir string
+ docFile string
+ bazelQueryViewDir string
)
func init() {
flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
- flag.StringVar(&bazelOverlayDir, "bazel_overlay_dir", "", "path to the bazel overlay directory")
+ flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory")
}
func newNameResolver(config android.Config) *android.NameResolver {
@@ -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()
- ctx.Register()
- if !shouldPrepareBuildActions() {
+ ctx := android.NewContext(configuration)
+ if bazelConversionRequested(configuration) {
+ // Register an alternate set of singletons and mutators for bazel
+ // conversion for Bazel conversion.
+ ctx.RegisterForBazelConversion()
+ } else {
+ ctx.Register()
+ }
+ 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.
@@ -95,6 +107,7 @@
// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
// the incorrect results from the first pass, and file I/O is expensive.
firstCtx := newContext(srcDir, configuration)
+ configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
bootstrap.Main(firstCtx.Context, configuration, extraNinjaDeps...)
// Invoke bazel commands and save results for second pass.
if err := configuration.BazelContext.InvokeBazel(); err != nil {
@@ -113,8 +126,10 @@
ctx = newContext(srcDir, configuration)
bootstrap.Main(ctx.Context, configuration, extraNinjaDeps...)
}
- if bazelOverlayDir != "" {
- if err := createBazelOverlay(ctx, bazelOverlayDir); err != nil {
+
+ // Convert the Soong module graph into Bazel BUILD files.
+ if bazelQueryViewDir != "" {
+ if err := createBazelQueryView(ctx, bazelQueryViewDir); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
@@ -129,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 {
@@ -139,8 +154,19 @@
}
}
-func shouldPrepareBuildActions() bool {
- // If we're writing soong_docs or bazel_overlay, don't write build.ninja or
- // collect metrics.
- return docFile == "" && bazelOverlayDir == ""
+// 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/bazel_overlay.go b/cmd/soong_build/queryview.go
similarity index 80%
rename from cmd/soong_build/bazel_overlay.go
rename to cmd/soong_build/queryview.go
index cdc5775..f5aa685 100644
--- a/cmd/soong_build/bazel_overlay.go
+++ b/cmd/soong_build/queryview.go
@@ -28,115 +28,6 @@
"github.com/google/blueprint/proptools"
)
-const (
- // The default `load` preamble for every generated BUILD file.
- soongModuleLoad = `package(default_visibility = ["//visibility:public"])
-load("//build/bazel/overlay_rules:soong_module.bzl", "soong_module")
-
-`
-
- // A macro call in the BUILD file representing a Soong module, with space
- // for expanding more attributes.
- soongModuleTarget = `soong_module(
- name = "%s",
- module_name = "%s",
- module_type = "%s",
- module_variant = "%s",
- module_deps = %s,
-%s)`
-
- // A simple provider to mark and differentiate Soong module rule shims from
- // regular Bazel rules. Every Soong module rule shim returns a
- // SoongModuleInfo provider, and can only depend on rules returning
- // SoongModuleInfo in the `module_deps` attribute.
- providersBzl = `SoongModuleInfo = provider(
- fields = {
- "name": "Name of module",
- "type": "Type of module",
- "variant": "Variant of module",
- },
-)
-`
-
- // The soong_module rule implementation in a .bzl file.
- soongModuleBzl = `
-%s
-
-load("//build/bazel/overlay_rules:providers.bzl", "SoongModuleInfo")
-
-def _generic_soong_module_impl(ctx):
- return [
- SoongModuleInfo(
- name = ctx.attr.module_name,
- type = ctx.attr.module_type,
- variant = ctx.attr.module_variant,
- ),
- ]
-
-generic_soong_module = rule(
- implementation = _generic_soong_module_impl,
- attrs = {
- "module_name": attr.string(mandatory = True),
- "module_type": attr.string(mandatory = True),
- "module_variant": attr.string(),
- "module_deps": attr.label_list(providers = [SoongModuleInfo]),
- },
-)
-
-soong_module_rule_map = {
-%s}
-
-_SUPPORTED_TYPES = ["bool", "int", "string"]
-
-def _is_supported_type(value):
- if type(value) in _SUPPORTED_TYPES:
- return True
- elif type(value) == "list":
- supported = True
- for v in value:
- supported = supported and type(v) in _SUPPORTED_TYPES
- return supported
- else:
- return False
-
-# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
-# expand to the right underlying shim.
-def soong_module(name, module_type, **kwargs):
- soong_module_rule = soong_module_rule_map.get(module_type)
-
- if soong_module_rule == None:
- # This module type does not have an existing rule to map to, so use the
- # generic_soong_module rule instead.
- generic_soong_module(
- name = name,
- module_type = module_type,
- module_name = kwargs.pop("module_name", ""),
- module_variant = kwargs.pop("module_variant", ""),
- module_deps = kwargs.pop("module_deps", []),
- )
- else:
- supported_kwargs = dict()
- for key, value in kwargs.items():
- if _is_supported_type(value):
- supported_kwargs[key] = value
- soong_module_rule(
- name = name,
- **supported_kwargs,
- )
-`
-
- // A rule shim for representing a Soong module type and its properties.
- moduleRuleShim = `
-def _%[1]s_impl(ctx):
- return [SoongModuleInfo()]
-
-%[1]s = rule(
- implementation = _%[1]s_impl,
- attrs = %[2]s
-)
-`
-)
-
var (
// An allowlist of prop types that are surfaced from module props to rule
// attributes. (nested) dictionaries are notably absent here, because while
@@ -370,7 +261,7 @@
// Canonicalize and normalize module property types to Bazel attribute types
starlarkAttrType := prop.Type
- if starlarkAttrType == "list of strings" {
+ if starlarkAttrType == "list of string" {
starlarkAttrType = "string_list"
} else if starlarkAttrType == "int64" {
starlarkAttrType = "int"
@@ -396,7 +287,7 @@
ruleShims := map[string]RuleShim{}
for _, pkg := range packages {
- content := "load(\"//build/bazel/overlay_rules:providers.bzl\", \"SoongModuleInfo\")\n"
+ content := "load(\"//build/bazel/queryview_rules:providers.bzl\", \"SoongModuleInfo\")\n"
bzlFileName := strings.ReplaceAll(pkg.Path, "android/soong/", "")
bzlFileName = strings.ReplaceAll(bzlFileName, ".", "_")
@@ -441,10 +332,10 @@
return ruleShims, nil
}
-func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
+func createBazelQueryView(ctx *android.Context, bazelQueryViewDir string) error {
blueprintCtx := ctx.Context
blueprintCtx.VisitAllModules(func(module blueprint.Module) {
- buildFile, err := buildFileForModule(blueprintCtx, module)
+ buildFile, err := buildFileForModule(blueprintCtx, module, bazelQueryViewDir)
if err != nil {
panic(err)
}
@@ -455,12 +346,12 @@
var err error
// Write top level files: WORKSPACE and BUILD. These files are empty.
- if err = writeReadOnlyFile(bazelOverlayDir, "WORKSPACE", ""); err != nil {
+ if err = writeReadOnlyFile(bazelQueryViewDir, "WORKSPACE", ""); err != nil {
return err
}
// Used to denote that the top level directory is a package.
- if err = writeReadOnlyFile(bazelOverlayDir, "BUILD", ""); err != nil {
+ if err = writeReadOnlyFile(bazelQueryViewDir, "BUILD", ""); err != nil {
return err
}
@@ -474,7 +365,7 @@
}
// Write .bzl Starlark files into the bazel_rules top level directory (provider and rule definitions)
- bazelRulesDir := bazelOverlayDir + "/build/bazel/overlay_rules"
+ bazelRulesDir := bazelQueryViewDir + "/build/bazel/queryview_rules"
if err = writeReadOnlyFile(bazelRulesDir, "BUILD", ""); err != nil {
return err
}
@@ -497,7 +388,7 @@
var loadStmts string
var moduleRuleMap string
for bzlFileName, ruleShim := range bzlLoads {
- loadStmt := "load(\"//build/bazel/overlay_rules:"
+ loadStmt := "load(\"//build/bazel/queryview_rules:"
loadStmt += bzlFileName
loadStmt += ".bzl\""
for _, rule := range ruleShim.rules {
@@ -563,9 +454,10 @@
attributes)
}
-func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
+func buildFileForModule(
+ ctx *blueprint.Context, module blueprint.Module, bazelQueryViewDir string) (*os.File, error) {
// Create nested directories for the BUILD file
- dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
+ dirPath := filepath.Join(bazelQueryViewDir, packagePath(ctx, module))
createDirectoryIfNonexistent(dirPath)
// Open the file for appending, and create it if it doesn't exist
f, err := os.OpenFile(
@@ -594,7 +486,7 @@
}
}
-// The overlay directory should be read-only, sufficient for bazel query. The files
+// The QueryView directory should be read-only, sufficient for bazel query. The files
// are not intended to be edited by end users.
func writeReadOnlyFile(dir string, baseName string, content string) error {
createDirectoryIfNonexistent(dir)
diff --git a/cmd/soong_build/queryview_templates.go b/cmd/soong_build/queryview_templates.go
new file mode 100644
index 0000000..359c0d8
--- /dev/null
+++ b/cmd/soong_build/queryview_templates.go
@@ -0,0 +1,124 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+const (
+ // The default `load` preamble for every generated BUILD file.
+ soongModuleLoad = `package(default_visibility = ["//visibility:public"])
+load("//build/bazel/queryview_rules:soong_module.bzl", "soong_module")
+
+`
+
+ // A macro call in the BUILD file representing a Soong module, with space
+ // for expanding more attributes.
+ soongModuleTarget = `soong_module(
+ name = "%s",
+ module_name = "%s",
+ module_type = "%s",
+ module_variant = "%s",
+ module_deps = %s,
+%s)`
+
+ // A simple provider to mark and differentiate Soong module rule shims from
+ // regular Bazel rules. Every Soong module rule shim returns a
+ // SoongModuleInfo provider, and can only depend on rules returning
+ // SoongModuleInfo in the `module_deps` attribute.
+ providersBzl = `SoongModuleInfo = provider(
+ fields = {
+ "name": "Name of module",
+ "type": "Type of module",
+ "variant": "Variant of module",
+ },
+)
+`
+
+ // The soong_module rule implementation in a .bzl file.
+ soongModuleBzl = `
+%s
+
+load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
+
+def _generic_soong_module_impl(ctx):
+ return [
+ SoongModuleInfo(
+ name = ctx.attr.module_name,
+ type = ctx.attr.module_type,
+ variant = ctx.attr.module_variant,
+ ),
+ ]
+
+generic_soong_module = rule(
+ implementation = _generic_soong_module_impl,
+ attrs = {
+ "module_name": attr.string(mandatory = True),
+ "module_type": attr.string(mandatory = True),
+ "module_variant": attr.string(),
+ "module_deps": attr.label_list(providers = [SoongModuleInfo]),
+ },
+)
+
+soong_module_rule_map = {
+%s}
+
+_SUPPORTED_TYPES = ["bool", "int", "string"]
+
+def _is_supported_type(value):
+ if type(value) in _SUPPORTED_TYPES:
+ return True
+ elif type(value) == "list":
+ supported = True
+ for v in value:
+ supported = supported and type(v) in _SUPPORTED_TYPES
+ return supported
+ else:
+ return False
+
+# soong_module is a macro that supports arbitrary kwargs, and uses module_type to
+# expand to the right underlying shim.
+def soong_module(name, module_type, **kwargs):
+ soong_module_rule = soong_module_rule_map.get(module_type)
+
+ if soong_module_rule == None:
+ # This module type does not have an existing rule to map to, so use the
+ # generic_soong_module rule instead.
+ generic_soong_module(
+ name = name,
+ module_type = module_type,
+ module_name = kwargs.pop("module_name", ""),
+ module_variant = kwargs.pop("module_variant", ""),
+ module_deps = kwargs.pop("module_deps", []),
+ )
+ else:
+ supported_kwargs = dict()
+ for key, value in kwargs.items():
+ if _is_supported_type(value):
+ supported_kwargs[key] = value
+ soong_module_rule(
+ name = name,
+ **supported_kwargs,
+ )
+`
+
+ // A rule shim for representing a Soong module type and its properties.
+ moduleRuleShim = `
+def _%[1]s_impl(ctx):
+ return [SoongModuleInfo()]
+
+%[1]s = rule(
+ implementation = _%[1]s_impl,
+ attrs = %[2]s
+)
+`
+)
diff --git a/cmd/soong_build/bazel_overlay_test.go b/cmd/soong_build/queryview_test.go
similarity index 93%
rename from cmd/soong_build/bazel_overlay_test.go
rename to cmd/soong_build/queryview_test.go
index de060bb..9471a91 100644
--- a/cmd/soong_build/bazel_overlay_test.go
+++ b/cmd/soong_build/queryview_test.go
@@ -28,7 +28,7 @@
func setUp() {
var err error
- buildDir, err = ioutil.TempDir("", "bazel_overlay_test")
+ buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
if err != nil {
panic(err)
}
@@ -53,6 +53,12 @@
android.ModuleBase
}
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+ return android.PathsForTesting("path" + tag), nil
+}
+
func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// nothing for now.
}
@@ -63,7 +69,7 @@
return module
}
-func TestGenerateBazelOverlayFromBlueprint(t *testing.T) {
+func TestGenerateBazelQueryViewFromBlueprint(t *testing.T) {
testCases := []struct {
bp string
expectedBazelTarget string
@@ -234,9 +240,9 @@
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext()
+ ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
@@ -277,7 +283,7 @@
},
bpdoc.Property{
Name: "string_list_prop",
- Type: "list of strings",
+ Type: "list of string",
},
bpdoc.Property{
Name: "nested_prop",
@@ -362,7 +368,7 @@
}
}
- expectedBzl := `load("//build/bazel/overlay_rules:providers.bzl", "SoongModuleInfo")
+ expectedBzl := `load("//build/bazel/queryview_rules:providers.bzl", "SoongModuleInfo")
def _foo_binary_impl(ctx):
return [SoongModuleInfo()]
@@ -440,7 +446,7 @@
}
actualSoongModuleBzl := generateSoongModuleBzl(ruleShims)
- expectedLoad := "load(\"//build/bazel/overlay_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
+ expectedLoad := "load(\"//build/bazel/queryview_rules:foo.bzl\", \"foo_binary\", \"foo_library\", \"foo_test_\")"
expectedRuleMap := `soong_module_rule_map = {
"foo_binary": foo_binary,
"foo_library": foo_library,
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_env/soong_env.go b/cmd/soong_env/soong_env.go
index d305d83..8020b17 100644
--- a/cmd/soong_env/soong_env.go
+++ b/cmd/soong_env/soong_env.go
@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// soong_glob is the command line tool that checks if the list of files matching a glob has
-// changed, and only updates the output file list if it has changed. It is used to optimize
-// out build.ninja regenerations when non-matching files are added. See
-// android/soong/android/glob.go for a longer description.
+// soong_env determines if the given soong environment file (usually ".soong.environment") is stale
+// by comparing its contents to the current corresponding environment variable values.
+// It fails if the file cannot be opened or corrupted, or its contents differ from the current
+// values.
+
package main
import (
@@ -34,6 +35,7 @@
os.Exit(2)
}
+// This is a simple executable packaging, and the real work happens in env.StaleEnvFile.
func main() {
flag.Parse()
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index e778bd6..74ede68 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -18,6 +18,7 @@
"context"
"flag"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -125,27 +126,35 @@
os.Exit(1)
}
+ // Create a terminal output that mimics Ninja's.
output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
+ // Attach a new logger instance to the terminal output.
log := logger.New(output)
defer log.Cleanup()
+ // Create a context to simplify the program termination process.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
+ // Create a new trace file writer, making it log events to the log instance.
trace := tracer.New(log)
defer trace.Close()
+ // Create and start a new metric record.
met := metrics.New()
met.SetBuildDateTime(buildStarted)
met.SetBuildCommand(os.Args)
+ // Create a new Status instance, which manages action counts and event output channels.
stat := &status.Status{}
defer stat.Finish()
+ // Hook up the terminal output and tracer to Status.
stat.AddOutput(output)
stat.AddOutput(trace.StatusTracer())
+ // Set up a cleanup procedure in case the normal termination process doesn't work.
build.SetupSignals(log, cancel, func() {
trace.Close()
log.Cleanup()
@@ -165,15 +174,18 @@
build.SetupOutDir(buildCtx, config)
- logsDir := config.OutDir()
- if config.Dist() {
- logsDir = filepath.Join(config.DistDir(), "logs")
+ if config.UseBazel() && config.Dist() {
+ defer populateExternalDistDir(buildCtx, config)
}
+ // Set up files to be outputted in the log directory.
+ logsDir := config.LogsDir()
+
+ // Common list of metric file definition.
buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
- defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+
build.PrintOutDirWarning(buildCtx, config)
os.MkdirAll(logsDir, 0777)
@@ -189,10 +201,27 @@
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 {
+ // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
+ // which Darwin doesn't support. Check if it was executed properly before parsing the value.
if !strings.HasSuffix(start, "N") {
if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
log.Verbosef("Took %dms to start up.",
@@ -211,6 +240,7 @@
fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
+ // Create a source finder.
f := build.NewSourceFinder(buildCtx, config)
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
@@ -354,6 +384,8 @@
return terminal.StdioImpl{}
}
+// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
+// reporting events to keep stdout clean from noise.
func customStdio() terminal.StdioInterface {
return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
}
@@ -471,11 +503,9 @@
ctx.Fatal("done")
}
- var toBuild int
- if _, ok := config.Environment().Get("USE_BAZEL"); ok {
+ toBuild := build.BuildAll
+ if config.UseBazel() {
toBuild = build.BuildAllWithBazel
- } else {
- toBuild = build.BuildAll
}
if config.Checkbuild() {
@@ -495,18 +525,83 @@
if c.flag == args[1] {
return &c, args[2:], nil
}
-
- // special case for --make-mode: if soong_ui was called from
- // build/make/core/main.mk, the makeparallel with --ninja
- // option specified puts the -j<num> before --make-mode.
- // TODO: Remove this hack once it has been fixed.
- if c.flag == makeModeFlagName {
- if inList(makeModeFlagName, args) {
- return &c, args[1:], nil
- }
- }
}
// command not found
return nil, nil, fmt.Errorf("Command not found: %q", args)
}
+
+// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
+func populateExternalDistDir(ctx build.Context, config build.Config) {
+ // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
+ var err error
+ var internalDistDirPath string
+ var externalDistDirPath string
+ if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
+ ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
+ }
+ if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
+ ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
+ }
+ if externalDistDirPath == internalDistDirPath {
+ return
+ }
+
+ // Make sure the internal DIST_DIR actually exists before trying to read from it
+ if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
+ ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
+ return
+ }
+
+ // Make sure the external DIST_DIR actually exists before trying to write to it
+ if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
+ ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
+ }
+
+ ctx.Println("Populating external DIST_DIR...")
+
+ populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
+}
+
+func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
+ files, err := ioutil.ReadDir(internalDistDirPath)
+ if err != nil {
+ ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
+ }
+ for _, f := range files {
+ internalFilePath := filepath.Join(internalDistDirPath, f.Name())
+ externalFilePath := filepath.Join(externalDistDirPath, f.Name())
+
+ if f.IsDir() {
+ // Moving a directory - check if there is an existing directory to merge with
+ externalLstat, err := os.Lstat(externalFilePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
+ }
+ // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
+ } else {
+ if externalLstat.IsDir() {
+ // Existing dir - try to merge the directories?
+ populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
+ continue
+ } else {
+ // Existing file being replaced with a directory. Delete the existing file...
+ if err := os.RemoveAll(externalFilePath); err != nil {
+ ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
+ }
+ }
+ }
+ } else {
+ // Moving a file (not a dir) - delete any existing file or directory
+ if err := os.RemoveAll(externalFilePath); err != nil {
+ ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
+ }
+ }
+
+ // The actual move - do a rename instead of a copy in order to save disk space.
+ if err := os.Rename(internalFilePath, externalFilePath); err != nil {
+ ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
+ }
+ }
+}
diff --git a/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go
index 294e5ef..aecdc3d 100644
--- a/cmd/zipsync/zipsync.go
+++ b/cmd/zipsync/zipsync.go
@@ -53,6 +53,16 @@
return out.Close()
}
+func writeSymlink(filename string, in io.Reader) error {
+ b, err := ioutil.ReadAll(in)
+ if err != nil {
+ return err
+ }
+ dest := string(b)
+ err = os.Symlink(dest, filename)
+ return err
+}
+
func main() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "usage: zipsync -d <output dir> [-l <output file>] [-f <pattern>] [zip]...")
@@ -122,7 +132,11 @@
if err != nil {
log.Fatal(err)
}
- must(writeFile(filename, in, f.FileInfo().Mode()))
+ if f.FileInfo().Mode()&os.ModeSymlink != 0 {
+ must(writeSymlink(filename, in))
+ } else {
+ must(writeFile(filename, in, f.FileInfo().Mode()))
+ }
in.Close()
files = append(files, filename)
}
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 6d77812..deaf77f 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -16,23 +16,220 @@
import (
"fmt"
- "path/filepath"
+ "strconv"
"strings"
"android/soong/android"
)
-// These libs are added as <uses-library> dependencies for apps if the targetSdkVersion in the
-// app manifest is less than the specified version. This is needed because these libraries haven't
-// existed prior to certain SDK version, but classes in them were in bootclasspath jars, etc.
-// Some of the compatibility libraries are optional (their <uses-library> tag has "required=false"),
-// so that if this library is missing this in not a build or run-time error.
+// This comment describes the following:
+// 1. the concept of class loader context (CLC) and its relation to classpath
+// 2. how PackageManager constructs CLC from shared libraries and their dependencies
+// 3. build-time vs. run-time CLC and why this matters for dexpreopt
+// 4. manifest fixer: a tool that adds missing <uses-library> tags to the manifests
+// 5. build system support for CLC
+//
+// 1. Class loader context
+// -----------------------
+//
+// Java libraries and apps that have run-time dependency on other libraries should list the used
+// libraries in their manifest (AndroidManifest.xml file). Each used library should be specified in
+// a <uses-library> tag that has the library name and an optional attribute specifying if the
+// library is optional or required. Required libraries are necessary for the library/app to run (it
+// will fail at runtime if the library cannot be loaded), and optional libraries are used only if
+// they are present (if not, the library/app can run without them).
+//
+// The libraries listed in <uses-library> tags are in the classpath of a library/app.
+//
+// Besides libraries, an app may also use another APK (for example in the case of split APKs), or
+// anything that gets added by the app dynamically. In general, it is impossible to know at build
+// time what the app may use at runtime. In the build system we focus on the known part: libraries.
+//
+// Class loader context (CLC) is a tree-like structure that describes class loader hierarchy. The
+// build system uses CLC in a more narrow sense: it is a tree of libraries that represents
+// transitive closure of all <uses-library> dependencies of a library/app. The top-level elements of
+// a CLC are the direct <uses-library> dependencies specified in the manifest (aka. classpath). Each
+// node of a CLC tree is a <uses-library> which may have its own <uses-library> sub-nodes.
+//
+// Because <uses-library> dependencies are, in general, a graph and not necessarily a tree, CLC may
+// contain subtrees for the same library multiple times. In other words, CLC is the dependency graph
+// "unfolded" to a tree. The duplication is only on a logical level, and the actual underlying class
+// loaders are not duplicated (at runtime there is a single class loader instance for each library).
+//
+// Example: A has <uses-library> tags B, C and D; C has <uses-library tags> B and D;
+// D has <uses-library> E; B and E have no <uses-library> dependencies. The CLC is:
+// A
+// ├── B
+// ├── C
+// │ ├── B
+// │ └── D
+// │ └── E
+// └── D
+// └── E
+//
+// CLC defines the lookup order of libraries when resolving Java classes used by the library/app.
+// The lookup order is important because libraries may contain duplicate classes, and the class is
+// resolved to the first match.
+//
+// 2. PackageManager and "shared" libraries
+// ----------------------------------------
+//
+// In order to load an APK at runtime, PackageManager (in frameworks/base) creates a CLC. It adds
+// the libraries listed in the <uses-library> tags in the app's manifest as top-level CLC elements.
+// For each of the used libraries PackageManager gets all its <uses-library> dependencies (specified
+// as tags in the manifest of that library) and adds a nested CLC for each dependency. This process
+// continues recursively until all leaf nodes of the constructed CLC tree are libraries that have no
+// <uses-library> dependencies.
+//
+// PackageManager is aware only of "shared" libraries. The definition of "shared" here differs from
+// its usual meaning (as in shared vs. static). In Android, Java "shared" libraries are those listed
+// in /system/etc/permissions/platform.xml file. This file is installed on device. Each entry in it
+// contains the name of a "shared" library, a path to its DEX jar file and a list of dependencies
+// (other "shared" libraries that this one uses at runtime and specifies them in <uses-library> tags
+// in its manifest).
+//
+// In other words, there are two sources of information that allow PackageManager to construct CLC
+// at runtime: <uses-library> tags in the manifests and "shared" library dependencies in
+// /system/etc/permissions/platform.xml.
+//
+// 3. Build-time and run-time CLC and dexpreopt
+// --------------------------------------------
+//
+// CLC is needed not only when loading a library/app, but also when compiling it. Compilation may
+// happen either on device (known as "dexopt") or during the build (known as "dexpreopt"). Since
+// dexopt takes place on device, it has the same information as PackageManager (manifests and
+// shared library dependencies). Dexpreopt, on the other hand, takes place on host and in a totally
+// different environment, and it has to get the same information from the build system (see the
+// section about build system support below).
+//
+// Thus, the build-time CLC used by dexpreopt and the run-time CLC used by PackageManager are
+// the same thing, but computed in two different ways.
+//
+// It is important that build-time and run-time CLCs coincide, otherwise the AOT-compiled code
+// created by dexpreopt will be rejected. In order to check the equality of build-time and
+// run-time CLCs, the dex2oat compiler records build-time CLC in the *.odex files (in the
+// "classpath" field of the OAT file header). To find the stored CLC, use the following command:
+// `oatdump --oat-file=<FILE> | grep '^classpath = '`.
+//
+// Mismatch between build-time and run-time CLC is reported in logcat during boot (search with
+// `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'`. Mismatch is bad for performance, as it
+// forces the library/app to either be dexopted, or to run without any optimizations (e.g. the app's
+// code may need to be extracted in memory from the APK, a very expensive operation).
+//
+// A <uses-library> can be either optional or required. From dexpreopt standpoint, required library
+// must be present at build time (its absence is a build error). An optional library may be either
+// present or absent at build time: if present, it will be added to the CLC, passed to dex2oat and
+// recorded in the *.odex file; otherwise, if the library is absent, it will be skipped and not
+// added to CLC. If there is a mismatch between built-time and run-time status (optional library is
+// present in one case, but not the other), then the build-time and run-time CLCs won't match and
+// the compiled code will be rejected. It is unknown at build time if the library will be present at
+// runtime, therefore either including or excluding it may cause CLC mismatch.
+//
+// 4. Manifest fixer
+// -----------------
+//
+// Sometimes <uses-library> tags are missing from the source manifest of a library/app. This may
+// happen for example if one of the transitive dependencies of the library/app starts using another
+// <uses-library>, and the library/app's manifest isn't updated to include it.
+//
+// Soong can compute some of the missing <uses-library> tags for a given library/app automatically
+// as SDK libraries in the transitive dependency closure of the library/app. The closure is needed
+// because a library/app may depend on a static library that may in turn depend on an SDK library,
+// (possibly transitively via another library).
+//
+// Not all <uses-library> tags can be computed in this way, because some of the <uses-library>
+// dependencies are not SDK libraries, or they are not reachable via transitive dependency closure.
+// But when possible, allowing Soong to calculate the manifest entries is less prone to errors and
+// simplifies maintenance. For example, consider a situation when many apps use some static library
+// that adds a new <uses-library> dependency -- all the apps will have to be updated. That is
+// difficult to maintain.
+//
+// Soong computes the libraries that need to be in the manifest as the top-level libraries in CLC.
+// These libraries are passed to the manifest_fixer.
+//
+// All libraries added to the manifest should be "shared" libraries, so that PackageManager can look
+// up their dependencies and reconstruct the nested subcontexts at runtime. There is no build check
+// to ensure this, it is an assumption.
+//
+// 5. Build system support
+// -----------------------
+//
+// In order to construct CLC for dexpreopt and manifest_fixer, the build system needs to know all
+// <uses-library> dependencies of the dexpreopted library/app (including transitive dependencies).
+// For each <uses-librarry> dependency it needs to know the following information:
+//
+// - the real name of the <uses-library> (it may be different from the module name)
+// - build-time (on host) and run-time (on device) paths to the DEX jar file of the library
+// - whether this library is optional or required
+// - all <uses-library> dependencies
+//
+// Since the build system doesn't have access to the manifest contents (it cannot read manifests at
+// the time of build rule generation), it is necessary to copy this information to the Android.bp
+// and Android.mk files. For blueprints, the relevant properties are `uses_libs` and
+// `optional_uses_libs`. For makefiles, relevant variables are `LOCAL_USES_LIBRARIES` and
+// `LOCAL_OPTIONAL_USES_LIBRARIES`. It is preferable to avoid specifying these properties explicilty
+// when they can be computed automatically by Soong (as the transitive closure of SDK library
+// dependencies).
+//
+// Some of the Java libraries that are used as <uses-library> are not SDK libraries (they are
+// defined as `java_library` rather than `java_sdk_library` in the Android.bp files). In order for
+// the build system to handle them automatically like SDK libraries, it is possible to set a
+// property `provides_uses_lib` or variable `LOCAL_PROVIDES_USES_LIBRARY` on the blueprint/makefile
+// module of such library. This property can also be used to specify real library name in cases
+// when it differs from the module name.
+//
+// Because the information from the manifests has to be duplicated in the Android.bp/Android.mk
+// files, there is a danger that it may get out of sync. To guard against that, the build system
+// generates a rule that checks the metadata in the build files against the contents of a manifest
+// (verify_uses_libraries). The manifest can be available as a source file, or as part of a prebuilt
+// APK. Note that reading the manifests at the Ninja stage of the build is fine, unlike the build
+// rule generation phase.
+//
+// ClassLoaderContext is a structure that represents CLC.
+//
+type ClassLoaderContext struct {
+ // The name of the library.
+ Name string
+
+ // On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
+ Host android.Path
+
+ // On-device install path (used in dex2oat argument --stored-class-loader-context).
+ Device string
+
+ // Nested sub-CLC for dependencies.
+ Subcontexts []*ClassLoaderContext
+}
+
+// ClassLoaderContextMap is a map from SDK version to CLC. There is a special entry with key
+// AnySdkVersion that stores unconditional CLC that is added regardless of the target SDK version.
+//
+// Conditional CLC is for compatibility libraries which didn't exist prior to a certain SDK version
+// (say, N), but classes in them were in the bootclasspath jars, etc., and in version N they have
+// been separated into a standalone <uses-library>. Compatibility libraries should only be in the
+// CLC if the library/app that uses them has `targetSdkVersion` less than N in the manifest.
+//
+// Currently only apps (but not libraries) use conditional CLC.
+//
+// Target SDK version information is unavailable to the build system at rule generation time, so
+// the build system doesn't know whether conditional CLC is needed for a given app or not. So it
+// generates a build rule that includes conditional CLC for all versions, extracts the target SDK
+// version from the manifest, and filters the CLCs based on that version. Exact final CLC that is
+// passed to dex2oat is unknown to the build system, and gets known only at Ninja stage.
+//
+type ClassLoaderContextMap map[int][]*ClassLoaderContext
+
+// Compatibility libraries. Some are optional, and some are required: this is the default that
+// affects how they are handled by the Soong logic that automatically adds implicit SDK libraries
+// to the manifest_fixer, but an explicit `uses_libs`/`optional_uses_libs` can override this.
var OrgApacheHttpLegacy = "org.apache.http.legacy"
var AndroidTestBase = "android.test.base"
var AndroidTestMock = "android.test.mock"
var AndroidHidlBase = "android.hidl.base-V1.0-java"
var AndroidHidlManager = "android.hidl.manager-V1.0-java"
+// Compatibility libraries grouped by version/optionality (for convenience, to avoid repeating the
+// same lists in multiple places).
var OptionalCompatUsesLibs28 = []string{
OrgApacheHttpLegacy,
}
@@ -49,39 +246,15 @@
const UnknownInstallLibraryPath = "error"
-const AnySdkVersion int = 9999 // should go last in class loader context
+// AnySdkVersion means that the class loader context is needed regardless of the targetSdkVersion
+// of the app. The numeric value affects the key order in the map and, as a result, the order of
+// arguments passed to construct_context.py (high value means that the unconditional context goes
+// last). We use the converntional "current" SDK level (10000), but any big number would do as well.
+const AnySdkVersion int = android.FutureApiLevelInt
-// LibraryPath contains paths to the library DEX jar on host and on device.
-type LibraryPath struct {
- Host android.Path
- Device string
-}
-
-// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
-type LibraryPaths map[string]*LibraryPath
-
-type classLoaderContext struct {
- // Library names
- Names []string
-
- // The class loader context using paths in the build.
- Host android.Paths
-
- // The class loader context using paths as they will be on the device.
- Target []string
-}
-
-// A map of class loader contexts for each SDK version.
-// A map entry for "any" version contains libraries that are unconditionally added to class loader
-// context. Map entries for existing versions contains libraries that were in the default classpath
-// until that API version, and should be added to class loader context if and only if the
-// targetSdkVersion in the manifest or APK is less than that API version.
-type classLoaderContextMap map[int]*classLoaderContext
-
-// Add a new library path to the map, unless a path for this library already exists.
-// If necessary, check that the build and install paths exist.
-func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path, strict bool) error {
+// Add class loader context for the given library to the map entry for the given SDK version.
+func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
+ hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
// not found. However, this is likely to result is disabling dexpreopt, as it won't be
@@ -89,263 +262,278 @@
strict = strict && !ctx.Config().AllowMissingDependencies()
if hostPath == nil && strict {
- return fmt.Errorf("unknown build path to <uses-library> '%s'", lib)
+ return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
}
+ devicePath := UnknownInstallLibraryPath
if installPath == nil {
if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
// Assume that compatibility libraries are installed in /system/framework.
installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
} else if strict {
- return fmt.Errorf("unknown install path to <uses-library> '%s'", lib)
- }
- }
-
- // Add a library only if the build and install path to it is known.
- if _, present := libPaths[lib]; !present {
- var devicePath string
- if installPath != nil {
- devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+ return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
} else {
// For some stub libraries the only known thing is the name of their implementation
// library, but the library itself is unavailable (missing or part of a prebuilt). In
// such cases we still need to add the library to <uses-library> tags in the manifest,
- // but we cannot use if for dexpreopt.
- devicePath = UnknownInstallLibraryPath
+ // but we cannot use it for dexpreopt.
}
- libPaths[lib] = &LibraryPath{hostPath, devicePath}
}
+ if installPath != nil {
+ devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+ }
+
+ // Nested class loader context shouldn't have conditional part (it is allowed only at the top level).
+ for ver, _ := range nestedClcMap {
+ if ver != AnySdkVersion {
+ clcStr, _ := ComputeClassLoaderContext(nestedClcMap)
+ return fmt.Errorf("nested class loader context shouldn't have conditional part: %s", clcStr)
+ }
+ }
+ subcontexts := nestedClcMap[AnySdkVersion]
+
+ // If the library with this name is already present as one of the unconditional top-level
+ // components, do not re-add it.
+ for _, clc := range clcMap[sdkVer] {
+ if clc.Name == lib {
+ return nil
+ }
+ }
+
+ clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
+ Name: lib,
+ Host: hostPath,
+ Device: devicePath,
+ Subcontexts: subcontexts,
+ })
return nil
}
-// Wrapper around addLibraryPath that does error reporting.
-func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string,
- hostPath, installPath android.Path, strict bool) {
+// Wrapper around addContext that reports errors.
+func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
+ hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
- err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict)
+ err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
if err != nil {
- android.ReportPathErrorf(ctx, err.Error())
+ ctx.ModuleErrorf(err.Error())
}
}
-// Add a new library path to the map. Enforce checks that the library paths exist.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) {
- libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true)
+// Add class loader context. Fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
+ hostPath, installPath android.Path) {
+
+ clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
}
-// Add a new library path to the map, if the library exists (name is not nil).
-// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
-// but their names still need to be added to <uses-library> tags in the manifest.
-func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
+// Add class loader context if the library exists. Don't fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
+ hostPath, installPath android.Path) {
+
if lib != nil {
- libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false)
+ clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
}
}
-// Add library paths from the second map to the first map (do not override existing entries).
-func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
- for lib, path := range otherPaths {
- if _, present := libPaths[lib]; !present {
- libPaths[lib] = path
+// Add class loader context for the given SDK version. Fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
+ lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+
+ clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
+}
+
+// Merge the other class loader context map into this one, do not override existing entries.
+// The implicitRootLib parameter is the name of the library for which the other class loader
+// context map was constructed. If the implicitRootLib is itself a <uses-library>, it should be
+// already present in the class loader context (with the other context as its subcontext) -- in
+// that case do not re-add the other context. Otherwise add the other context at the top-level.
+func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap, implicitRootLib string) {
+ if otherClcMap == nil {
+ return
+ }
+
+ // If the implicit root of the merged map is already present as one of top-level subtrees, do
+ // not merge it second time.
+ for _, clc := range clcMap[AnySdkVersion] {
+ if clc.Name == implicitRootLib {
+ return
}
}
-}
-func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
- if _, ok := m[sdkVer]; !ok {
- m[sdkVer] = &classLoaderContext{}
- }
- return m[sdkVer]
-}
-
-func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
- clc.Names = append(clc.Names, lib)
- clc.Host = append(clc.Host, hostPath)
- clc.Target = append(clc.Target, targetPath)
-}
-
-func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
- libs ...string) (bool, error) {
-
- clc := m.getValue(sdkVer)
- for _, lib := range libs {
- if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
- clc.addLib(lib, p.Host, p.Device)
- } else {
- if sdkVer == AnySdkVersion {
- // Fail the build 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("dexpreopt cannot find path for <uses-library> '%s'", lib)
- } else {
- // No error for compatibility libraries, as Soong doesn't know if they are needed
- // (this depends on the targetSdkVersion in the manifest).
- return false, nil
+ for sdkVer, otherClcs := range otherClcMap {
+ for _, otherClc := range otherClcs {
+ alreadyHave := false
+ for _, clc := range clcMap[sdkVer] {
+ if clc.Name == otherClc.Name {
+ alreadyHave = true
+ break
+ }
+ }
+ if !alreadyHave {
+ clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
}
}
}
- return true, nil
}
-func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
- clc := m.getValue(sdkVer)
- for _, lib := range libs {
- clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
+// Returns top-level libraries in the CLC (conditional CLC, i.e. compatibility libraries are not
+// included). This is the list of libraries that should be in the <uses-library> tags in the
+// manifest. Some of them may be present in the source manifest, others are added by manifest_fixer.
+func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
+ if clcMap != nil {
+ clcs := clcMap[AnySdkVersion]
+ ulibs = make([]string, 0, len(clcs))
+ for _, clc := range clcs {
+ ulibs = append(ulibs, clc.Name)
+ }
}
-}
-
-func (m classLoaderContextMap) usesLibs() []string {
- if clc, ok := m[AnySdkVersion]; ok {
- return clc.Names
- }
- return nil
-}
-
-// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
-// command for the dexpreopted module. There are three possible cases:
-//
-// 1. System server jars. They have a special class loader context that includes other system
-// server jars.
-//
-// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
-// context includes build and on-device paths to these libs. In some cases it may happen that
-// the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
-// library, whose implementation library is missing from the build altogether). In such case
-// dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
-// as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
-// such cases the function returns nil, which disables dexpreopt.
-//
-// 3. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
-// the unsafe &-classpath workaround that means empty class loader context and absence of runtime
-// check that the class loader context provided by the PackageManager agrees with the stored
-// class loader context recorded in the .odex file.
-//
-func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) {
- classLoaderContexts := make(classLoaderContextMap)
- systemServerJars := NonUpdatableSystemServerJars(ctx, global)
-
- if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
- // System server jars should be dexpreopted together: class loader context of each jar
- // should include all preceding jars on the system server classpath.
- classLoaderContexts.addSystemServerLibs(AnySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
-
- } else if module.EnforceUsesLibraries {
- // Unconditional class loader context.
- usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
- if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
- return nil, err
- }
-
- // Conditional class loader context for API version < 28.
- const httpLegacy = "org.apache.http.legacy"
- if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
- return nil, err
- }
-
- // Conditional class loader context for API version < 29.
- usesLibs29 := []string{
- "android.hidl.base-V1.0-java",
- "android.hidl.manager-V1.0-java",
- }
- if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
- return nil, err
- }
-
- // Conditional class loader context for API version < 30.
- if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
- return nil, err
- }
-
- } else {
- // Pass special class loader context to skip the classpath and collision check.
- // This will get removed once LOCAL_USES_LIBRARIES is enforced.
- // Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
- // to the &.
- }
-
- fixConditionalClassLoaderContext(classLoaderContexts)
-
- return &classLoaderContexts, nil
+ return ulibs
}
// Now that the full unconditional context is known, reconstruct conditional context.
// Apply filters for individual libraries, mirroring what the PackageManager does when it
// constructs class loader context on device.
//
-// TODO(b/132357300):
-// - remove android.hidl.manager and android.hidl.base unless the app is a system app.
+// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
//
-func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
- usesLibs := clcMap.usesLibs()
+func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
+ usesLibs := clcMap.UsesLibs()
- for sdkVer, clc := range clcMap {
+ for sdkVer, clcs := range clcMap {
if sdkVer == AnySdkVersion {
continue
}
- clcMap[sdkVer] = &classLoaderContext{}
- for i, lib := range clc.Names {
- if android.InList(lib, usesLibs) {
+ fixedClcs := []*ClassLoaderContext{}
+ for _, clc := range clcs {
+ if android.InList(clc.Name, usesLibs) {
// skip compatibility libraries that are already included in unconditional context
- } else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
+ } else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
// android.test.mock is only needed as a compatibility library (in conditional class
// loader context) if android.test.runner is used, otherwise skip it
} else {
- clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
+ fixedClcs = append(fixedClcs, clc)
}
+ clcMap[sdkVer] = fixedClcs
}
}
}
-// Return the class loader context as a string and a slice of build paths for all dependencies.
-func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) {
- for _, ver := range android.SortedIntKeys(clcMap) {
- clc := clcMap.getValue(ver)
-
- clcLen := len(clc.Names)
- if clcLen != len(clc.Host) || clcLen != len(clc.Target) {
- android.ReportPathErrorf(ctx, "ill-formed class loader context")
+// Return true if all build/install library paths are valid (including recursive subcontexts),
+// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
+// not equal to a special "error" value.
+func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
+ for sdkVer, clcs := range clcMap {
+ if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
+ return valid, err
}
+ }
+ return true, nil
+}
- var hostClc, targetClc []string
- var hostPaths android.Paths
-
- for i := 0; i < clcLen; i++ {
- hostStr := "PCL[" + clc.Host[i].String() + "]"
- targetStr := "PCL[" + clc.Target[i] + "]"
-
- hostClc = append(hostClc, hostStr)
- targetClc = append(targetClc, targetStr)
- hostPaths = append(hostPaths, clc.Host[i])
+// Helper function for validateClassLoaderContext() that handles recursion.
+func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
+ for _, clc := range clcs {
+ if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
+ 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.
+ 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.
+ return false, nil
+ }
}
+ if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
+ return valid, err
+ }
+ }
+ return true, nil
+}
+// Return the class loader context as a string, and a slice of build paths for all dependencies.
+// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
+// Return the resulting string and a slice of on-host build paths to all library dependencies.
+func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
+ for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
+ sdkVerStr := fmt.Sprintf("%d", sdkVer)
+ if sdkVer == AnySdkVersion {
+ sdkVerStr = "any" // a special keyword that means any SDK version
+ }
+ hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
if hostPaths != nil {
- sdkVerStr := fmt.Sprintf("%d", ver)
- if ver == AnySdkVersion {
- sdkVerStr = "any" // a special keyword that means any SDK version
- }
- clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
- clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
- paths = append(paths, hostPaths...)
+ clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
+ clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
}
+ paths = append(paths, hostPaths...)
}
-
- return clcStr, paths
+ return clcStr, android.FirstUniquePaths(paths)
}
+// Helper function for ComputeClassLoaderContext() that handles recursion.
+func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
+ var paths android.Paths
+ var clcsHost, clcsTarget []string
+
+ for _, clc := range clcs {
+ subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
+ if subPaths != nil {
+ subClcHost = "{" + subClcHost + "}"
+ subClcTarget = "{" + subClcTarget + "}"
+ }
+
+ clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
+ clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
+
+ paths = append(paths, clc.Host)
+ paths = append(paths, subPaths...)
+ }
+
+ clcHost := strings.Join(clcsHost, "#")
+ clcTarget := strings.Join(clcsTarget, "#")
+
+ return clcHost, clcTarget, paths
+}
+
+// JSON representation of <uses-library> paths on host and on device.
type jsonLibraryPath struct {
Host string
Device string
}
-type jsonLibraryPaths map[string]jsonLibraryPath
+// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
+// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
+// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
+// Make implementation.
+type jsonClassLoaderContext map[string]jsonLibraryPath
-// convert JSON map of library paths to LibraryPaths
-func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
- m := LibraryPaths{}
- for lib, path := range paths {
- m[lib] = &LibraryPath{
- constructPath(ctx, path.Host),
- path.Device,
+// A map from SDK version (represented with a JSON string) to JSON class loader context.
+type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
+
+// Convert JSON class loader context map to ClassLoaderContextMap.
+func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
+ clcMap := make(ClassLoaderContextMap)
+ for sdkVerStr, clc := range jClcMap {
+ sdkVer, ok := strconv.Atoi(sdkVerStr)
+ if ok != nil {
+ if sdkVerStr == "any" {
+ sdkVer = AnySdkVersion
+ } else {
+ android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
+ }
+ }
+ for lib, path := range clc {
+ clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
+ Name: lib,
+ Host: constructPath(ctx, path.Host),
+ Device: path.Device,
+ Subcontexts: nil,
+ })
}
}
- return m
+ return clcMap
}
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 51c1a0a..be7d4c6 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -37,91 +37,79 @@
// ├── b
// ├── c
// ├── d
- // ├── a2
- // ├── b2
- // ├── c2
- // ├── a1
- // ├── b1
+ // │ ├── a2
+ // │ ├── b2
+ // │ └── c2
+ // │ ├── a1
+ // │ └── b1
// ├── f
// ├── a3
// └── b3
//
ctx := testContext()
- lp := make(LibraryPaths)
+ m := make(ClassLoaderContextMap)
- lp.AddLibraryPath(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
- lp.AddLibraryPath(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
+ m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
+ m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
// "Maybe" variant in the good case: add as usual.
c := "c"
- lp.MaybeAddLibraryPath(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
+ m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
// "Maybe" variant in the bad case: don't add library with unknown name, keep going.
- lp.MaybeAddLibraryPath(ctx, nil, nil, nil)
+ m.MaybeAddContext(ctx, nil, nil, nil)
// Add some libraries with nested subcontexts.
- lp1 := make(LibraryPaths)
- lp1.AddLibraryPath(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
- lp1.AddLibraryPath(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+ m1 := make(ClassLoaderContextMap)
+ m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
+ m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
- lp2 := make(LibraryPaths)
- lp2.AddLibraryPath(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
- lp2.AddLibraryPath(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
- lp2.AddLibraryPath(ctx, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"))
- lp2.AddLibraryPaths(lp1)
+ m2 := make(ClassLoaderContextMap)
+ m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
+ m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
+ m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
- lp.AddLibraryPath(ctx, "d", buildPath(ctx, "d"), installPath(ctx, "d"))
- lp.AddLibraryPaths(lp2)
+ m3 := make(ClassLoaderContextMap)
+ m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
+ m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
- lp3 := make(LibraryPaths)
- lp3.AddLibraryPath(ctx, "f", buildPath(ctx, "f"), installPath(ctx, "f"))
- lp3.AddLibraryPath(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
- lp3.AddLibraryPath(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
- lp.AddLibraryPaths(lp3)
+ m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+ // When the same library is both in conditional and unconditional context, it should be removed
+ // from conditional context.
+ m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+ m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+
+ // Merge map with implicit root library that is among toplevel contexts => does nothing.
+ m.AddContextMap(m1, "c")
+ // Merge map with implicit root library that is not among toplevel contexts => all subcontexts
+ // of the other map are added as toplevel contexts.
+ m.AddContextMap(m3, "m_g")
// Compatibility libraries with unknown install paths get default paths.
- lp.AddLibraryPath(ctx, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil)
- lp.AddLibraryPath(ctx, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil)
- lp.AddLibraryPath(ctx, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil)
-
- module := testSystemModuleConfig(ctx, "test")
- module.LibraryPaths = lp
-
- m := make(classLoaderContextMap)
- valid := true
-
- ok, err := m.addLibs(ctx, AnySdkVersion, module, "a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3")
- valid = valid && ok && err == nil
-
- // Add compatibility libraries to conditional CLC for SDK level 29.
- ok, err = m.addLibs(ctx, 29, module, AndroidHidlManager, AndroidHidlBase)
- valid = valid && ok && err == nil
+ m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
+ m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
// needed as a compatibility library if "android.test.runner" is in CLC as well.
- ok, err = m.addLibs(ctx, 30, module, AndroidTestMock)
- valid = valid && ok && err == nil
+ m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
- // When the same library is both in conditional and unconditional context, it should be removed
- // from conditional context.
- ok, err = m.addLibs(ctx, 42, module, "f")
- valid = valid && ok && err == nil
+ valid, validationError := validateClassLoaderContext(m)
- fixConditionalClassLoaderContext(m)
+ fixClassLoaderContext(m)
var haveStr string
var havePaths android.Paths
var haveUsesLibs []string
- if valid {
- haveStr, havePaths = computeClassLoaderContext(ctx, m)
- haveUsesLibs = m.usesLibs()
+ if valid && validationError == nil {
+ haveStr, havePaths = ComputeClassLoaderContext(m)
+ haveUsesLibs = m.UsesLibs()
}
// Test that validation is successful (all paths are known).
t.Run("validate", func(t *testing.T) {
- if !valid {
+ if !(valid && validationError == nil) {
t.Errorf("invalid class loader context")
}
})
@@ -135,14 +123,14 @@
"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
" --host-context-for-sdk any " +
- "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]#" +
- "PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]#" +
- "PCL[out/a1.jar]#PCL[out/b1.jar]#" +
+ "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" +
+ "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" +
+ "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" +
"PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
" --target-context-for-sdk any " +
- "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]#" +
- "PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]#" +
- "PCL[/system/a1.jar]#PCL[/system/b1.jar]#" +
+ "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
+ "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
+ "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
if wantStr != haveStr {
t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
@@ -165,7 +153,7 @@
// Test for libraries that are added by the manifest_fixer.
t.Run("uses libs", func(t *testing.T) {
- wantUsesLibs := []string{"a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3"}
+ wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"}
if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
}
@@ -175,32 +163,50 @@
// Test that an unexpected unknown build path causes immediate error.
func TestCLCUnknownBuildPath(t *testing.T) {
ctx := testContext()
- lp := make(LibraryPaths)
- err := lp.addLibraryPath(ctx, "a", nil, nil, true)
- checkError(t, err, "unknown build path to <uses-library> 'a'")
+ m := make(ClassLoaderContextMap)
+ err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
+ checkError(t, err, "unknown build path to <uses-library> \"a\"")
}
// Test that an unexpected unknown install path causes immediate error.
func TestCLCUnknownInstallPath(t *testing.T) {
ctx := testContext()
- lp := make(LibraryPaths)
- err := lp.addLibraryPath(ctx, "a", buildPath(ctx, "a"), nil, true)
- checkError(t, err, "unknown install path to <uses-library> 'a'")
+ m := make(ClassLoaderContextMap)
+ err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
+ checkError(t, err, "unknown install path to <uses-library> \"a\"")
}
func TestCLCMaybeAdd(t *testing.T) {
ctx := testContext()
- lp := make(LibraryPaths)
+ m := make(ClassLoaderContextMap)
a := "a"
- lp.MaybeAddLibraryPath(ctx, &a, nil, nil)
+ m.MaybeAddContext(ctx, &a, nil, nil)
- module := testSystemModuleConfig(ctx, "test")
- module.LibraryPaths = lp
+ // The library should be added to <uses-library> tags by the manifest_fixer.
+ t.Run("maybe add", func(t *testing.T) {
+ haveUsesLibs := m.UsesLibs()
+ wantUsesLibs := []string{"a"}
+ if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
+ t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+ }
+ })
- m := make(classLoaderContextMap)
- _, err := m.addLibs(ctx, AnySdkVersion, module, "a")
- checkError(t, err, "dexpreopt cannot find path for <uses-library> 'a'")
+ // 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 build path for <uses-library> \"a\"")
+ })
+}
+
+// An attempt to add conditional nested subcontext should fail.
+func TestCLCNestedConditional(t *testing.T) {
+ ctx := testContext()
+ m1 := make(ClassLoaderContextMap)
+ m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+ m := make(ClassLoaderContextMap)
+ err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
+ checkError(t, err, "nested class loader context shouldn't have conditional part")
}
func checkError(t *testing.T, have error, want string) {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 03accc8..f52ecb4 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -114,10 +114,8 @@
ProfileIsTextListing bool
ProfileBootListing android.OptionalPath
- EnforceUsesLibraries bool
- OptionalUsesLibraries []string
- UsesLibraries []string
- LibraryPaths LibraryPaths
+ EnforceUsesLibraries bool
+ ClassLoaderContexts ClassLoaderContextMap
Archs []android.ArchType
DexPreoptImages []android.Path
@@ -265,7 +263,7 @@
DexPath string
ManifestPath string
ProfileClassListing string
- LibraryPaths jsonLibraryPaths
+ ClassLoaderContexts jsonClassLoaderContextMap
DexPreoptImages []string
DexPreoptImageLocations []string
PreoptBootClassPathDexFiles []string
@@ -283,7 +281,7 @@
config.ModuleConfig.DexPath = constructPath(ctx, config.DexPath)
config.ModuleConfig.ManifestPath = constructPath(ctx, config.ManifestPath)
config.ModuleConfig.ProfileClassListing = android.OptionalPathForPath(constructPath(ctx, config.ProfileClassListing))
- config.ModuleConfig.LibraryPaths = constructLibraryPaths(ctx, config.LibraryPaths)
+ config.ModuleConfig.ClassLoaderContexts = fromJsonClassLoaderContext(ctx, config.ClassLoaderContexts)
config.ModuleConfig.DexPreoptImages = constructPaths(ctx, config.DexPreoptImages)
config.ModuleConfig.DexPreoptImageLocations = config.DexPreoptImageLocations
config.ModuleConfig.PreoptBootClassPathDexFiles = constructPaths(ctx, config.PreoptBootClassPathDexFiles)
@@ -488,13 +486,7 @@
return
}
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: android.PathForOutput(ctx, "dexpreopt_soong.config"),
- Args: map[string]string{
- "content": string(data),
- },
- })
+ android.WriteFileRule(ctx, android.PathForOutput(ctx, "dexpreopt_soong.config"), string(data))
}
func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) {
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index a07f1fa..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
@@ -81,16 +81,18 @@
}
if !dexpreoptDisabled(ctx, global, module) {
- if clc, err := genClassLoaderContext(ctx, global, module); err != nil {
+ if valid, err := validateClassLoaderContext(module.ClassLoaderContexts); err != nil {
android.ReportPathErrorf(ctx, err.Error())
- } else if clc != nil {
+ } else if valid {
+ fixClassLoaderContext(module.ClassLoaderContexts)
+
appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
!module.NoCreateAppImage
generateDM := shouldGenerateDM(module, global)
for archIdx, _ := range module.Archs {
- dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, *clc, profile, appImage, generateDM)
+ dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
}
}
}
@@ -197,8 +199,8 @@
}
func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
- module *ModuleConfig, rule *android.RuleBuilder, archIdx int, classLoaderContexts classLoaderContextMap,
- profile android.WritablePath, appImage bool, generateDM bool) {
+ module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
+ appImage bool, generateDM bool) {
arch := module.Archs[archIdx]
@@ -235,6 +237,16 @@
rule.Command().FlagWithOutput("rm -f ", odexPath)
if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
+ // System server jars should be dexpreopted together: class loader context of each jar
+ // should include all preceding jars on the system server classpath.
+
+ var clcHost android.Paths
+ var clcTarget []string
+ for _, lib := range systemServerJars[:jarIndex] {
+ clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
+ clcTarget = append(clcTarget, filepath.Join("/system/framework", lib+".jar"))
+ }
+
// Copy the system server jar to a predefined location where dex2oat will find it.
dexPathHost := SystemServerDexJarHostPath(ctx, module.Name)
rule.Command().Text("mkdir -p").Flag(filepath.Dir(dexPathHost.String()))
@@ -242,11 +254,11 @@
checkSystemServerOrder(ctx, jarIndex)
- clc := classLoaderContexts[AnySdkVersion]
rule.Command().
- Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clc.Host.Strings(), ":") + "]").
- Implicits(clc.Host).
- Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clc.Target, ":") + "]")
+ Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]").
+ Implicits(clcHost).
+ Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
+
} else if module.EnforceUsesLibraries {
// Generate command that saves target SDK version in a shell variable.
if module.ManifestPath != nil {
@@ -266,13 +278,15 @@
}
// Generate command that saves host and target class loader context in shell variables.
- clc, paths := computeClassLoaderContext(ctx, classLoaderContexts)
+ clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
cmd := rule.Command().
Text(`eval "$(`).Tool(globalSoong.ConstructContext).
Text(` --target-sdk-version ${target_sdk_version}`).
Text(clc).Implicits(paths)
cmd.Text(`)"`)
+
} else {
+ // Other libraries or APKs for which the exact <uses-library> list is unknown.
// Pass special class loader context to skip the classpath and collision check.
// This will get removed once LOCAL_USES_LIBRARIES is enforced.
// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
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 ec31549..59278fd 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -44,9 +44,7 @@
ProfileClassListing: android.OptionalPath{},
ProfileIsTextListing: false,
EnforceUsesLibraries: false,
- OptionalUsesLibraries: nil,
- UsesLibraries: nil,
- LibraryPaths: nil,
+ ClassLoaderContexts: nil,
Archs: []android.ArchType{android.Arm},
DexPreoptImages: android.Paths{android.PathForTesting("system/framework/arm/boot.art")},
DexPreoptImagesDeps: []android.OutputPaths{android.OutputPaths{}},
@@ -62,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")
@@ -84,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")
@@ -144,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/env/env.go b/env/env.go
index a98e1f6..735a38a 100644
--- a/env/env.go
+++ b/env/env.go
@@ -27,6 +27,15 @@
type envFileEntry struct{ Key, Value string }
type envFileData []envFileEntry
+// Serializes the given environment variable name/value map into JSON formatted bytes by converting
+// to envFileEntry values and marshaling them.
+//
+// e.g. OUT_DIR = "out"
+// is converted to:
+// {
+// "Key": "OUT_DIR",
+// "Value": "out",
+// },
func EnvFileContents(envDeps map[string]string) ([]byte, error) {
contents := make(envFileData, 0, len(envDeps))
for key, value := range envDeps {
@@ -45,8 +54,11 @@
return data, nil
}
-func StaleEnvFile(filename string) (bool, error) {
- data, err := ioutil.ReadFile(filename)
+// Reads and deserializes a Soong environment file located at the given file path to determine its
+// staleness. If any environment variable values have changed, it prints them out and returns true.
+// Failing to read or parse the file also causes it to return true.
+func StaleEnvFile(filepath string) (bool, error) {
+ data, err := ioutil.ReadFile(filepath)
if err != nil {
return true, err
}
@@ -79,6 +91,7 @@
return false, nil
}
+// Implements sort.Interface so that we can use sort.Sort on envFileData arrays.
func (e envFileData) Len() int {
return len(e)
}
@@ -90,3 +103,5 @@
func (e envFileData) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
+
+var _ sort.Interface = envFileData{}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 44b8149..850c8f9 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -14,9 +14,20 @@
package etc
-import (
- "strconv"
+// This file implements module types that install prebuilt artifacts.
+//
+// There exist two classes of prebuilt modules in the Android tree. The first class are the ones
+// based on `android.Prebuilt`, such as `cc_prebuilt_library` and `java_import`. This kind of
+// modules may exist both as prebuilts and source at the same time, though only one would be
+// installed and the other would be marked disabled. The `prebuilt_postdeps` mutator would select
+// the actual modules to be installed. More details in android/prebuilt.go.
+//
+// The second class is described in this file. Unlike `android.Prebuilt` based module types,
+// `prebuilt_etc` exist only as prebuilts and cannot have a same-named source module counterpart.
+// This makes the logic of `prebuilt_etc` to be much simpler as they don't need to go through the
+// various `prebuilt_*` mutators.
+import (
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -42,19 +53,22 @@
}
type prebuiltEtcProperties struct {
- // Source file of this prebuilt.
+ // Source file of this prebuilt. Can reference a genrule type module with the ":module" syntax.
Src *string `android:"path,arch_variant"`
- // optional subdirectory under which this file is installed into, cannot be specified with relative_install_path, prefer relative_install_path
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // relative_install_path, prefer relative_install_path.
Sub_dir *string `android:"arch_variant"`
- // optional subdirectory under which this file is installed into, cannot be specified with sub_dir
+ // Optional subdirectory under which this file is installed into, cannot be specified with
+ // sub_dir.
Relative_install_path *string `android:"arch_variant"`
- // optional name for the installed file. If unspecified, name of the module is used as the file name
+ // Optional name for the installed file. If unspecified, name of the module is used as the file
+ // name.
Filename *string `android:"arch_variant"`
- // when set to true, and filename property is not set, the name for the installed file
+ // When set to true, and filename property is not set, the name for the installed file
// is the same as the file name of the source file.
Filename_from_src *bool `android:"arch_variant"`
@@ -84,8 +98,15 @@
type PrebuiltEtcModule interface {
android.Module
+
+ // Returns the base install directory, such as "etc", "usr/share".
BaseDir() string
+
+ // Returns the sub install directory relative to BaseDir().
SubDir() string
+
+ // Returns an android.OutputPath to the intermeidate file, which is the renamed prebuilt source
+ // file.
OutputFile() android.OutputPath
}
@@ -98,7 +119,8 @@
outputFilePath android.OutputPath
// The base install location, e.g. "etc" for prebuilt_etc, "usr/share" for prebuilt_usr_share.
installDirBase string
- // The base install location when soc_specific property is set to true, e.g. "firmware" for prebuilt_firmware.
+ // The base install location when soc_specific property is set to true, e.g. "firmware" for
+ // prebuilt_firmware.
socInstallDirBase string
installDirPath android.InstallPath
additionalDependencies *android.Paths
@@ -168,14 +190,8 @@
func (p *PrebuiltEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
-func (p *PrebuiltEtc) DepsMutator(ctx android.BottomUpMutatorContext) {
- if p.properties.Src == nil {
- ctx.PropertyErrorf("src", "missing prebuilt source file")
- }
-}
-
func (p *PrebuiltEtc) SourceFilePath(ctx android.ModuleContext) android.Path {
- return android.PathForModuleSrc(ctx, android.String(p.properties.Src))
+ return android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
}
func (p *PrebuiltEtc) InstallDirPath() android.InstallPath {
@@ -204,31 +220,41 @@
}
func (p *PrebuiltEtc) Installable() bool {
- return p.properties.Installable == nil || android.Bool(p.properties.Installable)
+ return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
}
func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- p.sourceFilePath = android.PathForModuleSrc(ctx, android.String(p.properties.Src))
- filename := android.String(p.properties.Filename)
- filename_from_src := android.Bool(p.properties.Filename_from_src)
- if filename == "" {
- if filename_from_src {
- filename = p.sourceFilePath.Base()
- } else {
- filename = ctx.ModuleName()
- }
- } else if filename_from_src {
- ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ if p.properties.Src == nil {
+ ctx.PropertyErrorf("src", "missing prebuilt source file")
return
}
+ p.sourceFilePath = android.PathForModuleSrc(ctx, proptools.String(p.properties.Src))
+
+ // Determine the output file basename.
+ // If Filename is set, use the name specified by the property.
+ // If Filename_from_src is set, use the source file name.
+ // Otherwise use the module name.
+ filename := proptools.String(p.properties.Filename)
+ filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
+ if filename != "" {
+ if filenameFromSrc {
+ ctx.PropertyErrorf("filename_from_src", "filename is set. filename_from_src can't be true")
+ return
+ }
+ } else if filenameFromSrc {
+ filename = p.sourceFilePath.Base()
+ } else {
+ filename = ctx.ModuleName()
+ }
p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+ // Check that `sub_dir` and `relative_install_path` are not set at the same time.
if p.properties.Sub_dir != nil && p.properties.Relative_install_path != nil {
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
- // If soc install dir was specified and SOC specific is set, set the installDirPath to the specified
- // socInstallDirBase.
+ // If soc install dir was specified and SOC specific is set, set the installDirPath to the
+ // specified socInstallDirBase.
installBaseDir := p.installDirBase
if p.SocSpecific() && p.socInstallDirBase != "" {
installBaseDir = p.socInstallDirBase
@@ -274,11 +300,9 @@
if len(p.properties.Symlinks) > 0 {
entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
}
- entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.Installable())
if p.additionalDependencies != nil {
- for _, path := range *p.additionalDependencies {
- entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
- }
+ entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", p.additionalDependencies.Strings()...)
}
},
},
@@ -339,9 +363,10 @@
return module
}
-// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system image.
-// If soc_specific property is set to true, the firmware file is installed to the vendor <partition>/firmware
-// directory for vendor image.
+// prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system
+// image.
+// If soc_specific property is set to true, the firmware file is installed to the
+// vendor <partition>/firmware directory for vendor image.
func PrebuiltFirmwareFactory() android.Module {
module := &PrebuiltEtc{}
module.socInstallDirBase = "firmware"
@@ -352,8 +377,8 @@
}
// prebuilt_dsp installs a DSP related file to <partition>/etc/dsp directory for system image.
-// If soc_specific property is set to true, the DSP related file is installed to the vendor <partition>/dsp
-// directory for vendor image.
+// If soc_specific property is set to true, the DSP related file is installed to the
+// vendor <partition>/dsp directory for vendor image.
func PrebuiltDSPFactory() android.Module {
module := &PrebuiltEtc{}
module.socInstallDirBase = "dsp"
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 8fc36c2..6c4c0b6 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -58,7 +58,7 @@
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
@@ -66,7 +66,7 @@
ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
- ctx.Register(config)
+ ctx.Register()
return ctx, config
}
diff --git a/filesystem/Android.bp b/filesystem/Android.bp
new file mode 100644
index 0000000..926df6e
--- /dev/null
+++ b/filesystem/Android.bp
@@ -0,0 +1,15 @@
+bootstrap_go_package {
+ name: "soong-filesystem",
+ pkgPath: "android/soong/filesystem",
+ deps: [
+ "blueprint",
+ "soong",
+ "soong-android",
+ ],
+ srcs: [
+ "filesystem.go",
+ ],
+ testSrcs: [
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
new file mode 100644
index 0000000..c6181bc
--- /dev/null
+++ b/filesystem/filesystem.go
@@ -0,0 +1,111 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filesystem
+
+import (
+ "fmt"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint"
+)
+
+func init() {
+ android.RegisterModuleType("android_filesystem", filesystemFactory)
+}
+
+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)
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+
+func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
+ 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) {
+ zipFile := android.PathForModuleOut(ctx, "temp.zip").OutputPath
+ f.CopyDepsToZip(ctx, zipFile)
+
+ rootDir := android.PathForModuleOut(ctx, "root").OutputPath
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().
+ BuiltTool("zipsync").
+ FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
+ Input(zipFile)
+
+ mkuserimg := ctx.Config().HostToolPath(ctx, "mkuserimg_mke2fs")
+ propFile := android.PathForModuleOut(ctx, "prop").OutputPath
+ // TODO(jiyong): support more filesystem types other than ext4
+ propsText := fmt.Sprintf(`mount_point=system\n`+
+ `fs_type=ext4\n`+
+ `use_dynamic_partition_size=true\n`+
+ `ext_mkuserimg=%s\n`, mkuserimg.String())
+ builder.Command().Text("echo").Flag("-e").Flag(`"` + propsText + `"`).
+ Text(">").Output(propFile).
+ Implicit(mkuserimg)
+
+ f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ builder.Command().BuiltTool("build_image").
+ Text(rootDir.String()). // input directory
+ Input(propFile).
+ 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("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/Android.bp b/genrule/Android.bp
index ff543a6..0e27d4e 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -4,8 +4,10 @@
deps: [
"blueprint",
"blueprint-pathtools",
+ "sbox_proto",
"soong",
"soong-android",
+ "soong-bazel",
"soong-shared",
],
srcs: [
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 53b9dbe..8df32f2 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// A genrule module takes a list of source files ("srcs" property), an optional
+// list of tools ("tools" property), and a command line ("cmd" property), to
+// generate output files ("out" property).
+
package genrule
import (
"fmt"
"io"
+ "path/filepath"
"strconv"
"strings"
@@ -25,16 +30,14 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/shared"
- "crypto/sha256"
- "path/filepath"
+ "android/soong/bazel"
)
func init() {
- registerGenruleBuildComponents(android.InitRegistrationContext)
+ RegisterGenruleBuildComponents(android.InitRegistrationContext)
}
-func registerGenruleBuildComponents(ctx android.RegistrationContext) {
+func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
@@ -48,6 +51,8 @@
var (
pctx = android.NewPackageContext("android/soong/genrule")
+ // Used by gensrcs when there is more than 1 shard to merge the outputs
+ // of each shard into a zip file.
gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{
Command: "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
CommandDeps: []string{"${soongZip}", "${zipSync}"},
@@ -80,13 +85,6 @@
blueprint.BaseDependencyTag
label string
}
-
-// TODO(cparsons): Move to a common location when there is more than just
-// genrule with a bazel_module property.
-type bazelModuleProperties struct {
- Label string
-}
-
type generatorProperties struct {
// The command to run on one or more input files. Cmd supports substitution of a few variables
//
@@ -120,9 +118,10 @@
// input files to exclude
Exclude_srcs []string `android:"path,arch_variant"`
- // in bazel-enabled mode, the bazel label to evaluate instead of this module
- Bazel_module bazelModuleProperties
+ // Properties for Bazel migration purposes.
+ bazel.Properties
}
+
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -135,9 +134,11 @@
properties generatorProperties
+ // For the different tasks that genrule and gensrc generate. genrule will
+ // generate 1 task, and gensrc will generate 1 or more tasks based on the
+ // number of shards the input files are sharded into.
taskGenerator taskFunc
- deps android.Paths
rule blueprint.Rule
rawCommands []string
@@ -156,14 +157,17 @@
type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
type generateTask struct {
- in android.Paths
- out android.WritablePaths
- copyTo android.WritablePaths
- genDir android.WritablePath
- sandboxOuts []string
- cmd string
- shard int
- shards int
+ in android.Paths
+ out android.WritablePaths
+ depFile android.WritablePath
+ copyTo android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
+ genDir android.WritablePath
+ extraTools android.Paths // dependencies on tools used by the generator
+
+ cmd string
+ // For gensrsc sharding.
+ shard int
+ shards int
}
func (g *Module) GeneratedSourceFiles() android.Paths {
@@ -201,13 +205,14 @@
if ok {
var bazelOutputFiles android.Paths
for _, bazelOutputFile := range filePaths {
- bazelOutputFiles = append(bazelOutputFiles, android.PathForSource(ctx, bazelOutputFile))
+ bazelOutputFiles = append(bazelOutputFiles, android.PathForBazelOut(ctx, bazelOutputFile))
}
c.outputFiles = bazelOutputFiles
c.outputDeps = bazelOutputFiles
}
return ok
}
+
func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
g.subName = ctx.ModuleSubDir()
@@ -238,6 +243,8 @@
}
}
+ var tools android.Paths
+ var packagedTools []android.PackagingSpec
if len(g.properties.Tools) > 0 {
seenTools := make(map[string]bool)
@@ -245,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
}
})
@@ -299,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
@@ -329,20 +355,44 @@
var outputFiles android.WritablePaths
var zipArgs strings.Builder
+ // Generate tasks, either from genrule or gensrcs.
for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
- for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
+ if len(task.out) == 0 {
+ ctx.ModuleErrorf("must have at least one output file")
+ return
}
- referencedIn := false
+ // 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{cmd.PathForOutput(out)})
+ }
+
referencedDepfile := false
- rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
+ rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
- reportError := func(fmt string, args ...interface{}) (string, bool, error) {
+ reportError := func(fmt string, args ...interface{}) (string, error) {
ctx.PropertyErrorf("cmd", fmt, args...)
- return "SOONG_ERROR", false, nil
+ return "SOONG_ERROR", nil
}
switch name {
@@ -357,20 +407,23 @@
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], false, nil
+ return locationLabels[firstLabel][0], nil
case "in":
- referencedIn = true
- return "${in}", true, nil
+ return strings.Join(srcFiles.Strings(), " "), nil
case "out":
- return "__SBOX_OUT_FILES__", false, nil
+ var sandboxOuts []string
+ for _, out := range task.out {
+ sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
+ }
+ return strings.Join(sandboxOuts, " "), nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
return reportError("$(depfile) used without depfile property")
}
- return "__SBOX_DEPFILE__", false, nil
+ return "__SBOX_DEPFILE__", nil
case "genDir":
- return "__SBOX_OUT_DIR__", false, nil
+ return cmd.PathForOutput(task.genDir), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -381,7 +434,7 @@
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
- return paths[0], false, nil
+ return paths[0], nil
} else {
return reportError("unknown location label %q", label)
}
@@ -391,7 +444,7 @@
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
- return strings.Join(paths, " "), false, nil
+ return strings.Join(paths, " "), nil
} else {
return reportError("unknown locations label %q", label)
}
@@ -410,50 +463,28 @@
ctx.PropertyErrorf("cmd", "specified depfile=true but did not include a reference to '${depfile}' in cmd")
return
}
-
- // tell the sbox command which directory to use as its sandbox root
- buildDir := android.PathForOutput(ctx).String()
- sandboxPath := shared.TempDirForOutDir(buildDir)
-
- // recall that Sprintf replaces percent sign expressions, whereas dollar signs expressions remain as written,
- // to be replaced later by ninja_strings.go
- depfilePlaceholder := ""
- if Bool(g.properties.Depfile) {
- depfilePlaceholder = "$depfileArgs"
- }
-
- // Escape the command for the shell
- rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
g.rawCommands = append(g.rawCommands, rawCommand)
- sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s",
- task.genDir, sandboxPath, task.genDir)
-
- if !referencedIn {
- sandboxCommand = sandboxCommand + hashSrcFiles(srcFiles)
- }
-
- sandboxCommand = sandboxCommand + fmt.Sprintf(" -c %s %s $allouts",
- rawCommand, depfilePlaceholder)
-
- ruleParams := blueprint.RuleParams{
- Command: sandboxCommand,
- CommandDeps: []string{"$sboxCmd"},
- }
- args := []string{"allouts"}
+ cmd.Text(rawCommand)
+ cmd.ImplicitOutputs(task.out)
+ cmd.Implicits(task.in)
+ cmd.ImplicitTools(tools)
+ cmd.ImplicitTools(task.extraTools)
+ cmd.ImplicitPackagedTools(packagedTools)
if Bool(g.properties.Depfile) {
- ruleParams.Deps = blueprint.DepsGCC
- args = append(args, "depfileArgs")
+ cmd.ImplicitDepFile(task.depFile)
}
- name := "generator"
- if task.shards > 1 {
- name += strconv.Itoa(task.shard)
- }
- rule := ctx.Rule(pctx, name, ruleParams, args...)
- g.generateSourceFile(ctx, task, rule)
+ // Create the rule to run the genrule command inside sbox.
+ rule.Build(name, desc)
if len(task.copyTo) > 0 {
+ // If copyTo is set, multiple shards need to be copied into a single directory.
+ // task.out contains the per-shard paths, and copyTo contains the corresponding
+ // final path. The files need to be copied into the final directory by a
+ // single rule so it can remove the directory before it starts to ensure no
+ // old files remain. zipsync already does this, so build up zipArgs that
+ // zip all the per-shard directories into a single zip.
outputFiles = append(outputFiles, task.copyTo...)
copyFrom = append(copyFrom, task.out.Paths()...)
zipArgs.WriteString(" -C " + task.genDir.String())
@@ -464,10 +495,13 @@
}
if len(copyFrom) > 0 {
+ // Create a rule that zips all the per-shard directories into a single zip and then
+ // uses zipsync to unzip it into the final directory.
ctx.Build(pctx, android.BuildParams{
- Rule: gensrcsMerge,
- Implicits: copyFrom,
- Outputs: outputFiles,
+ Rule: gensrcsMerge,
+ Implicits: copyFrom,
+ Outputs: outputFiles,
+ Description: "merge shards",
Args: map[string]string{
"zipArgs": zipArgs.String(),
"tmpZip": android.PathForModuleGen(ctx, g.subDir+".zip").String(),
@@ -501,51 +535,6 @@
}
}
}
-func hashSrcFiles(srcFiles android.Paths) string {
- h := sha256.New()
- for _, src := range srcFiles {
- h.Write([]byte(src.String()))
- }
- return fmt.Sprintf(" --input-hash %x", h.Sum(nil))
-}
-
-func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
- desc := "generate"
- if len(task.out) == 0 {
- ctx.ModuleErrorf("must have at least one output file")
- return
- }
- if len(task.out) == 1 {
- desc += " " + task.out[0].Base()
- }
-
- var depFile android.ModuleGenPath
- if Bool(g.properties.Depfile) {
- depFile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
- }
-
- if task.shards > 1 {
- desc += " " + strconv.Itoa(task.shard)
- }
-
- params := android.BuildParams{
- Rule: rule,
- Description: desc,
- Output: task.out[0],
- ImplicitOutputs: task.out[1:],
- Inputs: task.in,
- Implicits: g.deps,
- Args: map[string]string{
- "allouts": strings.Join(task.sandboxOuts, " "),
- },
- }
- if Bool(g.properties.Depfile) {
- params.Depfile = android.PathForModuleGen(ctx, task.out[0].Rel()+".d")
- params.Args["depfileArgs"] = "--depfile-out " + depFile.String()
- }
-
- ctx.Build(pctx, params)
-}
// Collect information for opening IDE project files in java/jdeps.go.
func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
@@ -579,6 +568,9 @@
}
}
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// Because generated outputs are checked by client modules(e.g. cc_library, ...)
@@ -610,61 +602,74 @@
func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
}
-// replace "out" with "__SBOX_OUT_DIR__/<the value of ${out}>"
-func pathToSandboxOut(path android.Path, genDir android.Path) string {
- relOut, err := filepath.Rel(genDir.String(), path.String())
- if err != nil {
- panic(fmt.Sprintf("Could not make ${out} relative: %v", err))
- }
- return filepath.Join("__SBOX_OUT_DIR__", relOut)
-
-}
-
func NewGenSrcs() *Module {
properties := &genSrcsProperties{}
+ // finalSubDir is the name of the subdirectory that output files will be generated into.
+ // It is used so that per-shard directories can be placed alongside it an then finally
+ // merged into it.
+ const finalSubDir = "gensrcs"
+
taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
- genDir := android.PathForModuleGen(ctx, "gensrcs")
shardSize := defaultShardSize
if s := properties.Shard_size; s != nil {
shardSize = int(*s)
}
+ // gensrcs rules can easily hit command line limits by repeating the command for
+ // every input file. Shard the input files into groups.
shards := android.ShardPaths(srcFiles, shardSize)
var generateTasks []generateTask
for i, shard := range shards {
var commands []string
var outFiles android.WritablePaths
+ var commandDepFiles []string
var copyTo android.WritablePaths
- var shardDir android.WritablePath
- var sandboxOuts []string
+ // When sharding is enabled (i.e. len(shards) > 1), the sbox rules for each
+ // shard will be write to their own directories and then be merged together
+ // into finalSubDir. If sharding is not enabled (i.e. len(shards) == 1),
+ // the sbox rule will write directly to finalSubDir.
+ genSubDir := finalSubDir
if len(shards) > 1 {
- shardDir = android.PathForModuleGen(ctx, strconv.Itoa(i))
- } else {
- shardDir = genDir
+ genSubDir = strconv.Itoa(i)
}
- for _, in := range shard {
- outFile := android.GenPathWithExt(ctx, "gensrcs", in, String(properties.Output_extension))
- sandboxOutfile := pathToSandboxOut(outFile, genDir)
+ 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))
+
+ // If sharding is enabled, then outFile is the path to the output file in
+ // the shard directory, and copyTo is the path to the output file in the
+ // final directory.
if len(shards) > 1 {
- shardFile := android.GenPathWithExt(ctx, strconv.Itoa(i), in, String(properties.Output_extension))
+ shardFile := android.GenPathWithExt(ctx, genSubDir, in, String(properties.Output_extension))
copyTo = append(copyTo, outFile)
outFile = shardFile
}
outFiles = append(outFiles, outFile)
- sandboxOuts = append(sandboxOuts, sandboxOutfile)
+ // pre-expand the command line to replace $in and $out with references to
+ // a single input and output file.
command, err := android.Expand(rawCommand, func(name string) (string, error) {
switch name {
case "in":
return in.String(), nil
case "out":
- return sandboxOutfile, 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 := rule.Command().PathForOutput(outFile.ReplaceExtension(ctx, "d"))
+ commandDepFiles = append(commandDepFiles, depFile)
+ return depFile, nil
default:
return "$(" + name + ")", nil
}
@@ -679,15 +684,30 @@
}
fullCommand := strings.Join(commands, " && ")
+ var outputDepfile android.WritablePath
+ var extraTools android.Paths
+ if len(commandDepFiles) > 0 {
+ // Each command wrote to a depfile, but ninja can only handle one
+ // depfile per rule. Use the dep_fixer tool at the end of the
+ // command to combine all the depfiles into a single output depfile.
+ outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
+ depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
+ fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
+ android.SboxPathForTool(ctx, depFixerTool),
+ strings.Join(commandDepFiles, " "))
+ extraTools = append(extraTools, depFixerTool)
+ }
+
generateTasks = append(generateTasks, generateTask{
- in: shard,
- out: outFiles,
- copyTo: copyTo,
- genDir: shardDir,
- sandboxOuts: sandboxOuts,
- cmd: fullCommand,
- shard: i,
- shards: len(shards),
+ in: shard,
+ out: outFiles,
+ depFile: outputDepfile,
+ copyTo: copyTo,
+ genDir: genDir,
+ cmd: fullCommand,
+ shard: i,
+ shards: len(shards),
+ extraTools: extraTools,
})
}
@@ -695,7 +715,7 @@
}
g := generatorFactory(taskGenerator, properties)
- g.subDir = "gensrcs"
+ g.subDir = finalSubDir
return g
}
@@ -713,25 +733,27 @@
Shard_size *int64
}
-const defaultShardSize = 100
+const defaultShardSize = 50
func NewGenRule() *Module {
properties := &genRuleProperties{}
taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
outs := make(android.WritablePaths, len(properties.Out))
- sandboxOuts := make([]string, len(properties.Out))
- genDir := android.PathForModuleGen(ctx)
+ var depFile android.WritablePath
for i, out := range properties.Out {
- outs[i] = android.PathForModuleGen(ctx, out)
- sandboxOuts[i] = pathToSandboxOut(outs[i], genDir)
+ outPath := android.PathForModuleGen(ctx, out)
+ if i == 0 {
+ depFile = outPath.ReplaceExtension(ctx, "d")
+ }
+ outs[i] = outPath
}
return []generateTask{{
- in: srcFiles,
- out: outs,
- genDir: android.PathForModuleGen(ctx),
- sandboxOuts: sandboxOuts,
- cmd: rawCommand,
+ in: srcFiles,
+ out: outs,
+ depFile: depFile,
+ genDir: android.PathForModuleGen(ctx),
+ cmd: rawCommand,
}}
}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index fdbb9d9..2f5605e 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -53,14 +53,14 @@
func testContext(config android.Config) *android.TestContext {
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("tool", toolFactory)
- registerGenruleBuildComponents(ctx)
+ RegisterGenruleBuildComponents(ctx)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.Register(config)
+ ctx.Register()
return ctx
}
@@ -141,7 +141,7 @@
out: ["out"],
cmd: "$(location) > $(out)",
`,
- expect: "out/tool > __SBOX_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ 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_OUT_FILES__",
+ expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "in1",
@@ -232,7 +232,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "in1 fg",
@@ -241,7 +241,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "ins",
@@ -250,7 +250,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "ins fg",
@@ -259,7 +259,7 @@
out: ["out"],
cmd: "cat $(in) > $(out)",
`,
- expect: "cat ${in} > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "location in1",
@@ -268,7 +268,7 @@
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "location in1 fg",
@@ -277,7 +277,7 @@
out: ["out"],
cmd: "cat $(location :1in) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "location ins",
@@ -286,7 +286,7 @@
out: ["out"],
cmd: "cat $(location in1) > $(out)",
`,
- expect: "cat in1 > __SBOX_OUT_FILES__",
+ expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "location ins fg",
@@ -295,7 +295,7 @@
out: ["out"],
cmd: "cat $(locations :ins) > $(out)",
`,
- expect: "cat in1 in2 > __SBOX_OUT_FILES__",
+ expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "outs",
@@ -303,7 +303,7 @@
out: ["out", "out2"],
cmd: "echo foo > $(out)",
`,
- expect: "echo foo > __SBOX_OUT_FILES__",
+ expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out __SBOX_SANDBOX_DIR__/out/out2",
},
{
name: "location out",
@@ -311,7 +311,7 @@
out: ["out", "out2"],
cmd: "echo foo > $(location out2)",
`,
- expect: "echo foo > __SBOX_OUT_DIR__/out2",
+ expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
},
{
name: "depfile",
@@ -320,7 +320,7 @@
depfile: true,
cmd: "echo foo > $(out) && touch $(depfile)",
`,
- expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
+ expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out && touch __SBOX_DEPFILE__",
},
{
name: "gendir",
@@ -328,7 +328,7 @@
out: ["out"],
cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
`,
- expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
+ expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
},
{
@@ -443,7 +443,7 @@
allowMissingDependencies: true,
- expect: "cat ***missing srcs :missing*** > __SBOX_OUT_FILES__",
+ expect: "cat ***missing srcs :missing*** > __SBOX_SANDBOX_DIR__/out/out",
},
{
name: "tool allow missing dependencies",
@@ -455,7 +455,7 @@
allowMissingDependencies: true,
- expect: "***missing tool :missing*** > __SBOX_OUT_FILES__",
+ expect: "***missing tool :missing*** > __SBOX_SANDBOX_DIR__/out/out",
},
}
@@ -495,7 +495,7 @@
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- if g, w := gen.rawCommands[0], "'"+test.expect+"'"; w != g {
+ if g, w := gen.rawCommands[0], test.expect; w != g {
t.Errorf("want %q, got %q", w, g)
}
})
@@ -542,18 +542,18 @@
}{
{
name: "hash0",
- // sha256 value obtained from: echo -n 'in1.txtin2.txt' | sha256sum
- expectedHash: "031097e11e0a8c822c960eb9742474f46336360a515744000d086d94335a9cb9",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
+ expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
},
{
name: "hash1",
- // sha256 value obtained from: echo -n 'in1.txtin2.txtin3.txt' | sha256sum
- expectedHash: "de5d22a4a7ab50d250cc59fcdf7a7e0775790d270bfca3a7a9e1f18a70dd996c",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
{
name: "hash2",
- // $(in) is present, option should not appear
- expectedHash: "",
+ // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
+ expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
},
}
@@ -570,20 +570,11 @@
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
gen := ctx.ModuleForTests(test.name, "")
- command := gen.Rule("generator").RuleParams.Command
+ manifest := android.RuleBuilderSboxProtoForTests(t, gen.Output("genrule.sbox.textproto"))
+ hash := manifest.Commands[0].GetInputHash()
- if len(test.expectedHash) > 0 {
- // We add spaces before and after to make sure that
- // this option doesn't abutt another sbox option.
- expectedInputHashOption := " --input-hash " + test.expectedHash + " "
-
- if !strings.Contains(command, expectedInputHashOption) {
- t.Errorf("Expected command \"%s\" to contain \"%s\"", command, expectedInputHashOption)
- }
- } else {
- if strings.Contains(command, "--input-hash") {
- t.Errorf("Unexpected \"--input-hash\" found in command: \"%s\"", command)
- }
+ if g, w := hash, test.expectedHash; g != w {
+ t.Errorf("Expected has %q, got %q", w, g)
}
})
}
@@ -609,7 +600,7 @@
cmd: "$(location) $(in) > $(out)",
`,
cmds: []string{
- "'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/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"},
@@ -623,8 +614,8 @@
shard_size: 2,
`,
cmds: []string{
- "'bash -c '\\''out/tool in1.txt > __SBOX_OUT_DIR__/in1.h'\\'' && bash -c '\\''out/tool in2.txt > __SBOX_OUT_DIR__/in2.h'\\'''",
- "'bash -c '\\''out/tool in3.txt > __SBOX_OUT_DIR__/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"},
@@ -710,7 +701,7 @@
}
gen := ctx.ModuleForTests("gen", "").Module().(*Module)
- expectedCmd := "'cp ${in} __SBOX_OUT_FILES__'"
+ expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
if gen.rawCommands[0] != expectedCmd {
t.Errorf("Expected cmd: %q, actual: %q", expectedCmd, gen.rawCommands[0])
}
@@ -745,7 +736,8 @@
}
gen := ctx.ModuleForTests("foo", "").Module().(*Module)
- expectedOutputFiles := []string{"bazelone.txt", "bazeltwo.txt"}
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
+ "outputbase/execroot/__main__/bazeltwo.txt"}
if !reflect.DeepEqual(gen.outputFiles.Strings(), expectedOutputFiles) {
t.Errorf("Expected output files: %q, actual: %q", expectedOutputFiles, gen.outputFiles)
}
@@ -766,7 +758,7 @@
}
func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+ t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
}
func (t *testTool) HostToolPath() android.OptionalPath {
diff --git a/java/Android.bp b/java/Android.bp
index 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 157d677..dfcd956 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "strconv"
"strings"
"android/soong/android"
@@ -109,7 +110,6 @@
useEmbeddedNativeLibs bool
useEmbeddedDex bool
usesNonSdkApis bool
- sdkLibraries dexpreopt.LibraryPaths
hasNoCode bool
LoggingParent string
resourceFiles android.Paths
@@ -193,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)
@@ -259,18 +268,17 @@
CommandDeps: []string{"${config.Zip2ZipCmd}"},
})
-func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, extraLinkFlags ...string) {
+func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext,
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
- transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags, sdkLibraries :=
- aaptLibs(ctx, sdkContext)
-
- a.sdkLibraries = sdkLibraries
+ transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
+ aaptLibs(ctx, sdkContext, classLoaderContexts)
// App manifest file
manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
- manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries,
+ manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
a.LoggingParent)
@@ -391,30 +399,33 @@
}
// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths
-func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext) (transitiveStaticLibs, transitiveStaticLibManifests android.Paths,
- staticRRODirs []rroDir, assets, deps android.Paths, flags []string, sdkLibraries dexpreopt.LibraryPaths) {
+func aaptLibs(ctx android.ModuleContext, sdkContext sdkContext, classLoaderContexts dexpreopt.ClassLoaderContextMap) (
+ transitiveStaticLibs, transitiveStaticLibManifests android.Paths, staticRRODirs []rroDir, assets, deps android.Paths, flags []string) {
var sharedLibs android.Paths
+ if classLoaderContexts == nil {
+ // Not all callers need to compute class loader context, those who don't just pass nil.
+ // Create a temporary class loader context here (it will be computed, but not used).
+ classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+ }
+
sdkDep := decodeSdkDep(ctx, sdkContext)
if sdkDep.useFiles {
sharedLibs = append(sharedLibs, sdkDep.jars...)
}
- sdkLibraries = make(dexpreopt.LibraryPaths)
-
ctx.VisitDirectDeps(func(module android.Module) {
+ depName := ctx.OtherModuleName(module)
+ depTag := ctx.OtherModuleDependencyTag(module)
+
var exportPackage android.Path
aarDep, _ := module.(AndroidLibraryDependency)
if aarDep != nil {
exportPackage = aarDep.ExportPackage()
}
- if dep, ok := module.(Dependency); ok {
- sdkLibraries.AddLibraryPaths(dep.ExportedSdkLibs())
- }
-
- switch ctx.OtherModuleDependencyTag(module) {
+ switch depTag {
case instrumentationForTag:
// Nothing, instrumentationForTag is treated as libTag for javac but not for aapt2.
case libTag:
@@ -426,7 +437,7 @@
// (including the java_sdk_library) itself then append any implicit sdk library
// names to the list of sdk libraries to be added to the manifest.
if component, ok := module.(SdkLibraryComponentDependency); ok {
- sdkLibraries.MaybeAddLibraryPath(ctx, component.OptionalImplicitSdkLibrary(),
+ classLoaderContexts.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(),
component.DexJarBuildPath(), component.DexJarInstallPath())
}
@@ -439,7 +450,6 @@
transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...)
transitiveStaticLibs = append(transitiveStaticLibs, exportPackage)
transitiveStaticLibManifests = append(transitiveStaticLibManifests, aarDep.ExportedManifests()...)
- sdkLibraries.AddLibraryPaths(aarDep.ExportedSdkLibs())
if aarDep.ExportedAssets().Valid() {
assets = append(assets, aarDep.ExportedAssets().Path())
}
@@ -457,6 +467,9 @@
}
}
}
+
+ // 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...)
@@ -473,7 +486,7 @@
transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs)
transitiveStaticLibManifests = android.FirstUniquePaths(transitiveStaticLibManifests)
- return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags, sdkLibraries
+ return transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assets, deps, flags
}
type AndroidLibrary struct {
@@ -508,8 +521,8 @@
func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.aapt.isLibrary = true
- a.aapt.buildActions(ctx, sdkContext(a))
- a.exportedSdkLibs = a.aapt.sdkLibraries
+ a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+ a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts)
a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
@@ -781,12 +794,11 @@
linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
linkDeps = append(linkDeps, a.manifest)
- transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags, sdkLibraries :=
- aaptLibs(ctx, sdkContext(a))
+ transitiveStaticLibs, staticLibManifests, staticRRODirs, transitiveAssets, libDeps, libFlags :=
+ aaptLibs(ctx, sdkContext(a), nil)
_ = staticLibManifests
_ = staticRRODirs
- _ = sdkLibraries
linkDeps = append(linkDeps, libDeps...)
linkFlags = append(linkFlags, libFlags...)
@@ -827,22 +839,26 @@
return nil
}
-func (a *AARImport) ExportedSdkLibs() dexpreopt.LibraryPaths {
+func (a *AARImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return nil
}
-func (d *AARImport) ExportedPlugins() (android.Paths, []string) {
- return nil, nil
+func (d *AARImport) ExportedPlugins() (android.Paths, []string, bool) {
+ return nil, nil, false
}
func (a *AARImport) SrcJarArgs() ([]string, android.Paths) {
return nil, nil
}
+var _ android.ApexModule = (*AARImport)(nil)
+
+// Implements android.ApexModule
func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return a.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
return nil
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 62cd112..c76bb2f 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -43,8 +43,9 @@
"args", "libs")
// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries dexpreopt.LibraryPaths,
- isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
+func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext,
+ classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
+ useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
var args []string
if isLibrary {
@@ -70,7 +71,7 @@
args = append(args, "--use-embedded-dex")
}
- for _, usesLib := range android.SortedStringKeys(sdkLibraries) {
+ for _, usesLib := range classLoaderContexts.UsesLibs() {
if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
args = append(args, "--optional-uses-library", usesLib)
} else {
diff --git a/java/android_resources.go b/java/android_resources.go
index 97f7679..4d420cf 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -23,6 +23,7 @@
func init() {
android.RegisterPreSingletonType("overlay", OverlaySingletonFactory)
+
}
var androidResourceIgnoreFilenames = []string{
@@ -37,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 e1a661f..aaad44f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -85,7 +85,6 @@
} else {
entriesList = append(entriesList, android.AndroidMkEntries{
Class: "JAVA_LIBRARIES",
- DistFiles: library.distFiles,
OutputFile: android.OptionalPathForPath(library.outputFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -115,7 +114,7 @@
entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
}
- entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", android.SortedStringKeys(library.exportedSdkLibs)...)
+ entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...)
if len(library.additionalCheckedModules) != 0 {
entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
@@ -140,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")
}
}
@@ -160,6 +159,9 @@
entries.SetString("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", "true")
}
entries.AddStrings("LOCAL_TEST_MAINLINE_MODULES", j.testProperties.Test_mainline_modules...)
+ if Bool(j.testProperties.Test_options.Unit_test) {
+ entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+ }
})
return entriesList
@@ -272,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)")
},
},
@@ -287,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 :=")
@@ -391,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")
@@ -521,17 +523,12 @@
// Note that dstubs.apiFile can be also be nil if WITHOUT_CHECKS_API is true.
// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
- var distFiles android.TaggedDistFiles
- if dstubs.apiFile != nil {
- distFiles = android.MakeDefaultDistFiles(dstubs.apiFile)
- }
outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
if !outputFile.Valid() {
outputFile = android.OptionalPathForPath(dstubs.apiFile)
}
return []android.AndroidMkEntries{android.AndroidMkEntries{
Class: "JAVA_LIBRARIES",
- DistFiles: distFiles,
OutputFile: outputFile,
Include: "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -551,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/androidmk_test.go b/java/androidmk_test.go
index 075b7aa..233e9d5 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -16,7 +16,6 @@
import (
"reflect"
- "strings"
"testing"
"android/soong/android"
@@ -135,182 +134,6 @@
}
}
-func TestDistWithTag(t *testing.T) {
- ctx, config := testJava(t, `
- java_library {
- name: "foo_without_tag",
- srcs: ["a.java"],
- compile_dex: true,
- dist: {
- targets: ["hi"],
- },
- }
- java_library {
- name: "foo_with_tag",
- srcs: ["a.java"],
- compile_dex: true,
- dist: {
- targets: ["hi"],
- tag: ".jar",
- },
- }
- `)
-
- withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_without_tag", "android_common").Module())
- withTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_with_tag", "android_common").Module())
-
- if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
- t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
- }
- if len(withTagEntries[0].DistFiles[".jar"]) != 1 ||
- !strings.Contains(withTagEntries[0].DistFiles[".jar"][0].String(), "/javac/foo_with_tag.jar") {
- t.Errorf("expected DistFiles to contain classes.jar, got %v", withTagEntries[0].DistFiles)
- }
- if len(withoutTagEntries[0].DistFiles[".jar"]) > 0 {
- t.Errorf("did not expect explicit DistFile for .jar tag, got %v", withoutTagEntries[0].DistFiles[".jar"])
- }
-}
-
-func TestDistWithDest(t *testing.T) {
- ctx, config := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java"],
- compile_dex: true,
- dist: {
- targets: ["my_goal"],
- dest: "my/custom/dest/dir",
- },
- }
- `)
-
- module := ctx.ModuleForTests("foo", "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, config, "", module)
- if len(entries) != 2 {
- t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
- }
-
- distStrings := entries[0].GetDistForGoals(module)
-
- if len(distStrings) != 2 {
- t.Errorf("Expected 2 entries for dist: PHONY and dist-for-goals, but got %q", distStrings)
- }
-
- if distStrings[0] != ".PHONY: my_goal\n" {
- t.Errorf("Expected .PHONY entry to declare my_goal, but got: %s", distStrings[0])
- }
-
- if !strings.Contains(distStrings[1], "$(call dist-for-goals,my_goal") ||
- !strings.Contains(distStrings[1], ".intermediates/foo/android_common/dex/foo.jar:my/custom/dest/dir") {
- t.Errorf(
- "Expected dist-for-goals entry to contain my_goal and new dest dir, but got: %s", distStrings[1])
- }
-}
-
-func TestDistsWithAllProperties(t *testing.T) {
- ctx, config := testJava(t, `
- java_library {
- name: "foo",
- srcs: ["a.java"],
- compile_dex: true,
- dist: {
- targets: ["baz"],
- },
- dists: [
- {
- targets: ["bar"],
- tag: ".jar",
- dest: "bar.jar",
- dir: "bar/dir",
- suffix: ".qux",
- },
- ]
- }
- `)
-
- module := ctx.ModuleForTests("foo", "android_common").Module()
- entries := android.AndroidMkEntriesForTest(t, config, "", module)
- if len(entries) != 2 {
- t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
- }
-
- distStrings := entries[0].GetDistForGoals(module)
-
- if len(distStrings) != 4 {
- t.Errorf("Expected 4 entries for dist: PHONY and dist-for-goals, but got %d", len(distStrings))
- }
-
- if distStrings[0] != ".PHONY: bar\n" {
- t.Errorf("Expected .PHONY entry to declare bar, but got: %s", distStrings[0])
- }
-
- if !strings.Contains(distStrings[1], "$(call dist-for-goals,bar") ||
- !strings.Contains(
- distStrings[1],
- ".intermediates/foo/android_common/javac/foo.jar:bar/dir/bar.qux.jar") {
- t.Errorf(
- "Expected dist-for-goals entry to contain bar and new dest dir, but got: %s", distStrings[1])
- }
-
- if distStrings[2] != ".PHONY: baz\n" {
- t.Errorf("Expected .PHONY entry to declare baz, but got: %s", distStrings[2])
- }
-
- if !strings.Contains(distStrings[3], "$(call dist-for-goals,baz") ||
- !strings.Contains(distStrings[3], ".intermediates/foo/android_common/dex/foo.jar:foo.jar") {
- t.Errorf(
- "Expected dist-for-goals entry to contain my_other_goal and new dest dir, but got: %s",
- distStrings[3])
- }
-}
-
-func TestDistsWithTag(t *testing.T) {
- ctx, config := testJava(t, `
- java_library {
- name: "foo_without_tag",
- srcs: ["a.java"],
- compile_dex: true,
- dists: [
- {
- targets: ["hi"],
- },
- ],
- }
- java_library {
- name: "foo_with_tag",
- srcs: ["a.java"],
- compile_dex: true,
- dists: [
- {
- targets: ["hi"],
- tag: ".jar",
- },
- ],
- }
- `)
-
- moduleWithoutTag := ctx.ModuleForTests("foo_without_tag", "android_common").Module()
- moduleWithTag := ctx.ModuleForTests("foo_with_tag", "android_common").Module()
-
- withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithoutTag)
- withTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithTag)
-
- if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
- t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
- }
-
- distFilesWithoutTag := withoutTagEntries[0].DistFiles
- distFilesWithTag := withTagEntries[0].DistFiles
-
- if len(distFilesWithTag[".jar"]) != 1 ||
- !strings.Contains(distFilesWithTag[".jar"][0].String(), "/javac/foo_with_tag.jar") {
- t.Errorf("expected foo_with_tag's .jar-tagged DistFiles to contain classes.jar, got %v", distFilesWithTag[".jar"])
- }
- if len(distFilesWithoutTag[".jar"]) > 0 {
- t.Errorf("did not expect foo_without_tag's .jar-tagged DistFiles to contain files, but got %v", distFilesWithoutTag[".jar"])
- }
-}
-
func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
ctx, config := testJava(t, `
java_sdk_library {
diff --git a/java/app.go b/java/app.go
index c24e0c5..c0ac9c2 100755
--- a/java/app.go
+++ b/java/app.go
@@ -565,9 +565,8 @@
aaptLinkFlags = append(aaptLinkFlags, a.additionalAaptFlags...)
a.aapt.splitNames = a.appProperties.Package_splits
- a.aapt.sdkLibraries = a.exportedSdkLibs
a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
- a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...)
+ a.aapt.buildActions(ctx, sdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
// apps manifests are handled by aapt, don't let Module see them
a.properties.Manifest = nil
@@ -601,7 +600,7 @@
return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
}
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext, sdkLibs dexpreopt.LibraryPaths) android.Path {
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
a.dexpreopter.installPath = a.installPath(ctx)
if a.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
@@ -609,12 +608,8 @@
}
a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
- a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
- a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
- a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
- a.dexpreopter.libraryPaths.AddLibraryPaths(sdkLibs)
+ a.dexpreopter.classLoaderContexts = a.classLoaderContexts
a.dexpreopter.manifestFile = a.mergedManifestFile
- a.exportedSdkLibs = make(dexpreopt.LibraryPaths)
if ctx.ModuleName() != "framework-res" {
a.Module.compile(ctx, a.aaptSrcJar)
@@ -784,6 +779,8 @@
a.aapt.noticeFile = a.noticeOutputs.HtmlGzOutput
}
+ a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
+
// Process all building blocks, from AAPT to certificates.
a.aaptBuildActions(ctx)
@@ -791,7 +788,7 @@
a.usesLibrary.freezeEnforceUsesLibraries()
// Add implicit SDK libraries to <uses-library> list.
- for _, usesLib := range android.SortedStringKeys(a.aapt.sdkLibraries) {
+ for _, usesLib := range a.classLoaderContexts.UsesLibs() {
a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
}
@@ -808,7 +805,7 @@
a.linter.resources = a.aapt.resourceFiles
a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
- dexJarFile := a.dexBuildActions(ctx, a.aapt.sdkLibraries)
+ dexJarFile := a.dexBuildActions(ctx)
jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
jniJarFile := a.jniBuildActions(jniLibs, ctx)
@@ -893,7 +890,7 @@
if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
if dep, ok := module.(*cc.Module); ok {
- if dep.IsNdk() || dep.IsStubs() {
+ if dep.IsNdk(ctx.Config()) || dep.IsStubs() {
return false
}
@@ -1118,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 {
@@ -1134,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
@@ -1403,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,
@@ -1443,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.
@@ -1470,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) {
@@ -1540,9 +1544,7 @@
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
- a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
- a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
- a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
+ a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
if a.dexpreopter.uncompressedDex {
@@ -1651,6 +1653,9 @@
return sdkSpecFrom("")
}
+var _ android.ApexModule = (*AndroidAppImport)(nil)
+
+// Implements android.ApexModule
func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// Do not check for prebuilts against the min_sdk_version of enclosing APEX
@@ -1852,7 +1857,7 @@
aaptLinkFlags = append(aaptLinkFlags,
"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
}
- r.aapt.buildActions(ctx, r, aaptLinkFlags...)
+ r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
// Sign the built package
_, certificates := collectAppDeps(ctx, r, false, false)
@@ -1976,17 +1981,18 @@
return optionalUsesLibs
}
-// usesLibraryPaths returns a map of module names of shared library dependencies to the paths
+// Returns a map of module names of shared library dependencies to the paths
// to their dex jars on host and on device.
-func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) dexpreopt.LibraryPaths {
- usesLibPaths := make(dexpreopt.LibraryPaths)
+func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
+ clcMap := make(dexpreopt.ClassLoaderContextMap)
if !ctx.Config().UnbundledBuild() {
ctx.VisitDirectDeps(func(m android.Module) {
- if _, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
+ if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
dep := ctx.OtherModuleName(m)
if lib, ok := m.(Dependency); ok {
- usesLibPaths.AddLibraryPath(ctx, dep, lib.DexJarBuildPath(), lib.DexJarInstallPath())
+ clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
+ lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
} else if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{dep})
} else {
@@ -1996,7 +2002,7 @@
})
}
- return usesLibPaths
+ return clcMap
}
// enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs
@@ -2019,8 +2025,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)
@@ -2033,7 +2039,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
}
@@ -2043,7 +2049,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).
@@ -2052,7 +2058,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 82577e3..e13c6b9 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -59,7 +59,7 @@
func testApp(t *testing.T, bp string) *android.TestContext {
config := testAppConfig(nil, bp, nil)
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -220,7 +220,7 @@
config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
config.Targets[android.Android] = test.targets
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
module := ctx.ModuleForTests("foo", "android_common")
const packedSplitApks = "foo.zip"
@@ -291,7 +291,7 @@
}
`)
- testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+ testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -335,7 +335,7 @@
}
`)
- testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+ testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
android_app {
name: "foo",
srcs: ["a.java"],
@@ -657,7 +657,7 @@
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
config := testConfig(nil, fmt.Sprintf(bp, testCase.prop), fs)
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
module := ctx.ModuleForTests("foo", "android_common")
@@ -973,7 +973,7 @@
config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
@@ -1039,7 +1039,7 @@
}
func checkSdkVersion(t *testing.T, config android.Config, expectedSdkVersion string) {
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -1633,7 +1633,7 @@
if test.certificateOverride != "" {
config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -1698,7 +1698,7 @@
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
config := testAppConfig(nil, test.bp, nil)
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -1758,7 +1758,7 @@
if test.packageNameOverride != "" {
config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
foo := ctx.ModuleForTests("foo", "android_common")
@@ -1793,7 +1793,7 @@
`
config := testAppConfig(nil, bp, nil)
config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -2416,7 +2416,7 @@
config := testAppConfig(nil, bp, nil)
config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -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: [
@@ -2777,7 +2841,7 @@
config := testAppConfig(nil, bp, nil)
config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -2815,11 +2879,11 @@
// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
cmd = app.Rule("dexpreopt").RuleParams.Command
w := `--target-context-for-sdk any ` +
- `PCL[/system/framework/foo.jar]#` +
- `PCL[/system/framework/quuz.jar]#` +
`PCL[/system/framework/qux.jar]#` +
- `PCL[/system/framework/runtime-library.jar]#` +
- `PCL[/system/framework/bar.jar]`
+ `PCL[/system/framework/quuz.jar]#` +
+ `PCL[/system/framework/foo.jar]#` +
+ `PCL[/system/framework/bar.jar]#` +
+ `PCL[/system/framework/runtime-library.jar]`
if !strings.Contains(cmd, w) {
t.Errorf("wanted %q in %q", w, cmd)
}
@@ -3129,7 +3193,7 @@
config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
@@ -3209,7 +3273,7 @@
}
`
config := testAppConfig(nil, bp, fs)
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
m := ctx.ModuleForTests("foo", "android_common")
@@ -3506,7 +3570,7 @@
config.TestProductVariables.EnforceRROExemptedTargets = testCase.enforceRROExemptTargets
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
modules := []string{"foo", "bar"}
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 900eb7a..823275b 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -85,8 +85,9 @@
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"))
// If this is not an unbundled build and missing dependencies are not allowed
@@ -96,14 +97,9 @@
// Iterate over the module names on the boot classpath in order
for _, name := range android.SortedStringKeys(moduleToApex) {
if apexVariant, ok := nameToApexVariant[name]; ok {
- if dep, ok := apexVariant.(Dependency); ok {
- // Add the implementation jars for the module to be checked. This uses implementation
- // and resources jar as that is what the previous make based check uses.
- for _, jar := range dep.ImplementationAndResourcesJars() {
- checkBootJars.Input(jar)
- }
- } else if _, ok := apexVariant.(*DexImport); ok {
- // TODO(b/171479578): ignore deximport when doing package check until boot_jars.go can check dex jars.
+ if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok {
+ // Add the dex implementation jar for the module to be checked.
+ checkBootJars.Input(dep.DexJarBuildPath())
} else {
ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
}
@@ -113,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 3043e46..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} ` +
@@ -572,14 +572,7 @@
}
func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) {
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Description: "manifest",
- Output: outputFile,
- Args: map[string]string{
- "content": "Main-Class: " + mainClass + "\n",
- },
- })
+ android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
}
func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 40a2280..4914d74 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -163,12 +163,12 @@
return nil
}
-func (d *DeviceHostConverter) ExportedSdkLibs() dexpreopt.LibraryPaths {
+func (d *DeviceHostConverter) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
return nil
}
-func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string) {
- return nil, nil
+func (d *DeviceHostConverter) ExportedPlugins() (android.Paths, []string, bool) {
+ return nil, nil, false
}
func (d *DeviceHostConverter) SrcJarArgs() ([]string, android.Paths) {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 20dbc66..67738d4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -33,11 +33,9 @@
isTest bool
isPresignedPrebuilt bool
- manifestFile android.Path
- usesLibs []string
- optionalUsesLibs []string
- enforceUsesLibs bool
- libraryPaths dexpreopt.LibraryPaths
+ manifestFile android.Path
+ enforceUsesLibs bool
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
builtInstalled string
}
@@ -193,10 +191,8 @@
ProfileIsTextListing: profileIsTextListing,
ProfileBootListing: profileBootListing,
- EnforceUsesLibraries: d.enforceUsesLibs,
- OptionalUsesLibraries: d.optionalUsesLibs,
- UsesLibraries: d.usesLibs,
- LibraryPaths: d.libraryPaths,
+ EnforceUsesLibraries: d.enforceUsesLibs,
+ ClassLoaderContexts: d.classLoaderContexts,
Archs: archs,
DexPreoptImages: images,
@@ -220,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 629d34f..062005b 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -25,11 +25,177 @@
"github.com/google/blueprint/proptools"
)
+// This comment describes:
+// 1. ART boot images in general (their types, structure, file layout, etc.)
+// 2. build system support for boot images
+//
+// 1. ART boot images
+// ------------------
+//
+// A boot image in ART is a set of files that contain AOT-compiled native code and a heap snapshot
+// of AOT-initialized classes for the bootclasspath Java libraries. A boot image is compiled from a
+// set of DEX jars by the dex2oat compiler. A boot image is used for two purposes: 1) it is
+// installed on device and loaded at runtime, and 2) other Java libraries and apps are compiled
+// against it (compilation may take place either on host, known as "dexpreopt", or on device, known
+// as "dexopt").
+//
+// A boot image is not a single file, but a collection of interrelated files. Each boot image has a
+// number of components that correspond to the Java libraries that constitute it. For each component
+// there are multiple files:
+// - *.oat or *.odex file with native code (architecture-specific, one per instruction set)
+// - *.art file with pre-initialized Java classes (architecture-specific, one per instruction set)
+// - *.vdex file with verification metadata for the DEX bytecode (architecture independent)
+//
+// *.vdex files for the boot images do not contain the DEX bytecode itself, because the
+// bootclasspath DEX files are stored on disk in uncompressed and aligned form. Consequently a boot
+// image is not self-contained and cannot be used without its DEX files. To simplify the management
+// of boot image files, ART uses a certain naming scheme and associates the following metadata with
+// each boot image:
+// - A stem, which is a symbolic name that is prepended to boot image file names.
+// - A location (on-device path to the boot image files).
+// - A list of boot image locations (on-device paths to dependency boot images).
+// - A set of DEX locations (on-device paths to the DEX files, one location for one DEX file used
+// to compile the boot image).
+//
+// There are two kinds of boot images:
+// - primary boot images
+// - boot image extensions
+//
+// 1.1. Primary boot images
+// ------------------------
+//
+// A primary boot image is compiled for a core subset of bootclasspath Java libraries. It does not
+// depend on any other images, and other boot images may depend on it.
+//
+// For example, assuming that the stem is "boot", the location is /apex/com.android.art/javalib/,
+// the set of core bootclasspath libraries is A B C, and the boot image is compiled for ARM targets
+// (32 and 64 bits), it will have three components with the following files:
+// - /apex/com.android.art/javalib/{arm,arm64}/boot.{art,oat,vdex}
+// - /apex/com.android.art/javalib/{arm,arm64}/boot-B.{art,oat,vdex}
+// - /apex/com.android.art/javalib/{arm,arm64}/boot-C.{art,oat,vdex}
+//
+// The files of the first component are special: they do not have the component name appended after
+// the stem. This naming convention dates back to the times when the boot image was not split into
+// components, and there were just boot.oat and boot.art. The decision to split was motivated by
+// licensing reasons for one of the bootclasspath libraries.
+//
+// As of November 2020 the only primary boot image in Android is the image in the ART APEX
+// com.android.art. The primary ART boot image contains the Core libraries that are part of the ART
+// module. When the ART module gets updated, the primary boot image will be updated with it, and all
+// dependent images will get invalidated (the checksum of the primary image stored in dependent
+// images will not match), unless they are updated in sync with the ART module.
+//
+// 1.2. Boot image extensions
+// --------------------------
+//
+// A boot image extension is compiled for a subset of bootclasspath Java libraries (in particular,
+// this subset does not include the Core bootclasspath libraries that go into the primary boot
+// image). A boot image extension depends on the primary boot image and optionally some other boot
+// image extensions. Other images may depend on it. In other words, boot image extensions can form
+// acyclic dependency graphs.
+//
+// The motivation for boot image extensions comes from the Mainline project. Consider a situation
+// when the list of bootclasspath libraries is A B C, and both A and B are parts of the Android
+// platform, but C is part of an updatable APEX com.android.C. When the APEX is updated, the Java
+// code for C might have changed compared to the code that was used to compile the boot image.
+// Consequently, the whole boot image is obsolete and invalidated (even though the code for A and B
+// that does not depend on C is up to date). To avoid this, the original monolithic boot image is
+// split in two parts: the primary boot image that contains A B, and the boot image extension that
+// contains C and depends on the primary boot image (extends it).
+//
+// For example, assuming that the stem is "boot", the location is /system/framework, the set of
+// bootclasspath libraries is D E (where D is part of the platform and is located in
+// /system/framework, and E is part of a non-updatable APEX com.android.E and is located in
+// /apex/com.android.E/javalib), and the boot image is compiled for ARM targets (32 and 64 bits),
+// it will have two components with the following files:
+// - /system/framework/{arm,arm64}/boot-D.{art,oat,vdex}
+// - /system/framework/{arm,arm64}/boot-E.{art,oat,vdex}
+//
+// As of November 2020 the only boot image extension in Android is the Framework boot image
+// extension. It extends the primary ART boot image and contains Framework libraries and other
+// bootclasspath libraries from the platform and non-updatable APEXes that are not included in the
+// ART image. The Framework boot image extension is updated together with the platform. In the
+// future other boot image extensions may be added for some updatable modules.
+//
+//
+// 2. Build system support for boot images
+// ---------------------------------------
+//
+// The primary ART boot image needs to be compiled with one dex2oat invocation that depends on DEX
+// jars for the core libraries. Framework boot image extension needs to be compiled with one dex2oat
+// invocation that depends on the primary ART boot image and all bootclasspath DEX jars except the
+// Core libraries.
+//
+// 2.1. Libraries that go in the boot images
+// -----------------------------------------
+//
+// The contents of each boot image are determined by the PRODUCT variables. The primary ART APEX
+// boot image contains libraries listed in the ART_APEX_JARS variable in the AOSP makefiles. The
+// Framework boot image extension contains libraries specified in the PRODUCT_BOOT_JARS and
+// PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries,
+// but more product-specific libraries can be added in the product makefiles.
+//
+// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is either a simple
+// name (if the library is a part of the Platform), or a colon-separated pair <apex, name> (if the
+// library is a part of a non-updatable APEX).
+//
+// A related variable PRODUCT_UPDATABLE_BOOT_JARS contains bootclasspath libraries that are in
+// updatable APEXes. They are not included in the boot image.
+//
+// One exception to the above rules are "coverage" builds (a special build flavor which requires
+// setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in
+// boot image libraries is instrumented, which means that the instrumentation library (jacocoagent)
+// needs to be added to the list of bootclasspath DEX jars.
+//
+// In general, there is a requirement that the source code for a boot image library must be
+// available at build time (e.g. it cannot be a stub that has a separate implementation library).
+//
+// 2.2. Static configs
+// -------------------
+//
+// Because boot images are used to dexpreopt other Java modules, the paths to boot image files must
+// be known by the time dexpreopt build rules for the dependent modules are generated. Boot image
+// configs are constructed very early during the build, before build rule generation. The configs
+// provide predefined paths to boot image files (these paths depend only on static build
+// configuration, such as PRODUCT variables, and use hard-coded directory names).
+//
+// 2.3. Singleton
+// --------------
+//
+// Build rules for the boot images are generated with a Soong singleton. Because a singleton has no
+// dependencies on other modules, it has to find the modules for the DEX jars using VisitAllModules.
+// Soong loops through all modules and compares each module against a list of bootclasspath library
+// names. Then it generates build rules that copy DEX jars from their intermediate module-specific
+// locations to the hard-coded locations predefined in the boot image configs.
+//
+// It would be possible to use a module with proper dependencies instead, but that would require
+// changes in the way Soong generates variables for Make: a singleton can use one MakeVars() method
+// that writes variables to out/soong/make_vars-*.mk, which is included early by the main makefile,
+// but module(s) would have to use out/soong/Android-*.mk which has a group of LOCAL_* variables
+// for each module, and is included later.
+//
+// 2.4. Install rules
+// ------------------
+//
+// The primary boot image and the Framework extension are installed in different ways. The primary
+// boot image is part of the ART APEX: it is copied into the APEX intermediate files, packaged
+// together with other APEX contents, extracted and mounted on device. The Framework boot image
+// extension is installed by the rules defined in makefiles (make/core/dex_preopt_libart.mk). Soong
+// writes out a few DEXPREOPT_IMAGE_* variables for Make; these variables contain boot image names,
+// paths and so on.
+//
+// 2.5. JIT-Zygote configuration
+// -----------------------------
+//
+// One special configuration is JIT-Zygote build, when the primary ART image is used for compiling
+// apps instead of the Framework boot image extension (see DEXPREOPT_USE_ART_IMAGE and UseArtImage).
+//
+
func init() {
RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
}
-// Target-independent description of pre-compiled boot image.
+// Target-independent description of a boot image.
type bootImageConfig struct {
// If this image is an extension, the image that it extends.
extends *bootImageConfig
@@ -66,7 +232,7 @@
variants []*bootImageVariant
}
-// Target-dependent description of pre-compiled boot image.
+// Target-dependent description of a boot image.
type bootImageVariant struct {
*bootImageConfig
@@ -90,6 +256,7 @@
unstrippedInstalls android.RuleBuilderInstalls
}
+// Get target-specific boot image variant for the given boot image config and target.
func (image bootImageConfig) getVariant(target android.Target) *bootImageVariant {
for _, variant := range image.variants {
if variant.target.Os == target.Os && variant.target.Arch.ArchType == target.Arch.ArchType {
@@ -99,7 +266,7 @@
return nil
}
-// Return any (the first) variant which is for the device (as opposed to for the host)
+// Return any (the first) variant which is for the device (as opposed to for the host).
func (image bootImageConfig) getAnyAndroidVariant() *bootImageVariant {
for _, variant := range image.variants {
if variant.target.Os == android.Android {
@@ -109,10 +276,12 @@
return nil
}
+// Return the name of a boot image module given a boot image config and a component (module) index.
+// A module name is a combination of the Java library name, and the boot image stem (that is stored
+// in the config).
func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string {
- // Dexpreopt on the boot class path produces multiple files. The first dex file
- // is converted into 'name'.art (to match the legacy assumption that 'name'.art
- // exists), and the rest are converted to 'name'-<jar>.art.
+ // The first module of the primary boot image is special: its module name has only the stem, but
+ // not the library name. All other module names are of the form <stem>-<library name>
m := image.modules.Jar(idx)
name := image.stem
if idx != 0 || image.extends != nil {
@@ -121,6 +290,7 @@
return name
}
+// Return the name of the first boot image module, or stem if the list of modules is empty.
func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
if image.modules.Len() > 0 {
return image.moduleName(ctx, 0)
@@ -129,6 +299,8 @@
}
}
+// Return filenames for the given boot image component, given the output directory and a list of
+// extensions.
func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
ret := make(android.OutputPaths, 0, image.modules.Len()*len(exts))
for i := 0; i < image.modules.Len(); i++ {
@@ -140,17 +312,26 @@
return ret
}
+// Return boot image locations (as a list of symbolic paths).
+//
// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
// exist on the device. Typically it is /apex/com.android.art/javalib/boot.art and should be the
// same for all supported architectures on the device. The concrete architecture specific files
// actually end up in architecture-specific sub-directory such as arm, arm64, x86, or x86_64.
//
-// For example a physical file
-// "/apex/com.android.art/javalib/x86/boot.art" has "image location"
-// "/apex/com.android.art/javalib/boot.art" (which is not an actual file).
+// For example a physical file /apex/com.android.art/javalib/x86/boot.art has "image location"
+// /apex/com.android.art/javalib/boot.art (which is not an actual file).
+//
+// For a primary boot image the list of locations has a single element.
+//
+// For a boot image extension the list of locations contains a location for all dependency images
+// (including the primary image) and the location of the extension itself. For example, for the
+// Framework boot image extension that depends on the primary ART boot image the list contains two
+// elements.
//
// The location is passed as an argument to the ART tools like dex2oat instead of the real path.
// ART tools will then reconstruct the architecture-specific real path.
+//
func (image *bootImageVariant) imageLocations() (imageLocations []string) {
if image.extends != nil {
imageLocations = image.extends.getVariant(image.target).imageLocations()
@@ -158,18 +339,6 @@
return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
}
-func concat(lists ...[]string) []string {
- var size int
- for _, l := range lists {
- size += len(l)
- }
- ret := make([]string, 0, size)
- for _, l := range lists {
- ret = append(ret, l...)
- }
- return ret
-}
-
func dexpreoptBootJarsFactory() android.Singleton {
return &dexpreoptBootJars{}
}
@@ -182,10 +351,21 @@
return dexpreopt.GetGlobalConfig(ctx).DisablePreopt
}
+// Singleton for generating boot image build rules.
type dexpreoptBootJars struct {
+ // Default boot image config (currently always the Framework boot image extension). It should be
+ // noted that JIT-Zygote builds use ART APEX image instead of the Framework boot image extension,
+ // but the switch is handled not here, but in the makefiles (triggered with
+ // DEXPREOPT_USE_ART_IMAGE=true).
defaultBootImage *bootImageConfig
- otherImages []*bootImageConfig
+ // Other boot image configs (currently the list contains only the primary ART APEX image. It
+ // used to contain an experimental JIT-Zygote image (now replaced with the ART APEX image). In
+ // the future other boot image extensions may be added.
+ otherImages []*bootImageConfig
+
+ // Build path to a config file that Soong writes for Make (to be used in makefiles that install
+ // the default boot image).
dexpreoptConfigForMake android.WritablePath
}
@@ -205,7 +385,7 @@
return files
}
-// dexpreoptBoot singleton rules
+// Generate build rules for boot images.
func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
if skipDexpreoptBootJars(ctx) {
return
@@ -238,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
@@ -285,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)
}
@@ -315,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
}
})
@@ -326,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)
@@ -334,9 +531,10 @@
}
}
- // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
- // the bootclasspath modules have been compiled. Copy the dex jars there so the module rules that have
- // already been set up can find them.
+ // The paths to bootclasspath DEX files need to be known at module GenerateAndroidBuildAction
+ // time, before the boot images are built (these paths are used in dexpreopt rule generation for
+ // Java libraries and apps). Generate rules that copy bootclasspath DEX jars to the predefined
+ // paths.
for i := range bootDexJars {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
@@ -358,19 +556,20 @@
}
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
}
+// Generate boot image build rules for a specific target.
func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant,
profile android.Path, missingDeps []string) android.WritablePaths {
@@ -386,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())
@@ -428,12 +627,15 @@
}
if image.extends != nil {
+ // It is a boot image extension, so it needs the boot image it depends on (in this case the
+ // primary ART APEX image).
artImage := image.primaryImages
cmd.
Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", image.dexPathsDeps.Paths(), ":").
Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", image.dexLocationsDeps, ":").
FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage)
} else {
+ // It is a primary image, so it needs a base address.
cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
}
@@ -504,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()
@@ -528,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
@@ -559,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()
@@ -581,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
@@ -594,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")
@@ -609,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
@@ -651,16 +853,10 @@
updatableBcpPackagesName := "updatable-bcp-packages.txt"
updatableBcpPackages := image.dir.Join(ctx, updatableBcpPackagesName)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: updatableBcpPackages,
- Args: map[string]string{
- // WriteFile automatically adds the last end-of-line.
- "content": strings.Join(updatablePackages, "\\n"),
- },
- })
+ // 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`?
@@ -684,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)
}
@@ -720,16 +916,12 @@
func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
data := dexpreopt.GetGlobalConfigRawData(ctx)
- ctx.Build(pctx, android.BuildParams{
- Rule: android.WriteFile,
- Output: path,
- Args: map[string]string{
- "content": string(data),
- },
- })
+ android.WriteFileRule(ctx, path, string(data))
}
-// Export paths for default boot image to Make
+// Define Make variables for boot image names, paths, etc. These variables are used in makefiles
+// (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
+// correct output directories.
func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
if d.dexpreoptConfigForMake != nil {
ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
@@ -743,6 +935,11 @@
ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.getAnyAndroidVariant().dexLocationsDeps, " "))
var imageNames []string
+ // TODO: the primary ART boot image should not be exposed to Make, as it is installed in a
+ // different way as a part of the ART APEX. However, there is a special JIT-Zygote build
+ // configuration which uses the primary ART image instead of the Framework boot image
+ // extension, and it relies on the ART image being exposed to Make. To fix this, it is
+ // necessary to rework the logic in makefiles.
for _, current := range append(d.otherImages, image) {
imageNames = append(imageNames, current.name)
for _, variant := range current.variants {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index ab31958..95fe5e1 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -51,7 +51,7 @@
dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList([]string{"platform:foo", "platform:bar", "platform:baz"})
dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
- ctx := testContext()
+ ctx := testContext(config)
RegisterDexpreoptBootJarsComponents(ctx)
run(t, ctx, config)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 1e09d64..9c88a3c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -19,7 +19,6 @@
"path/filepath"
"strings"
- "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -30,16 +29,6 @@
func init() {
RegisterDocsBuildComponents(android.InitRegistrationContext)
RegisterStubsBuildComponents(android.InitRegistrationContext)
-
- // Register sdk member type.
- android.RegisterSdkMemberType(&droidStubsSdkMemberType{
- SdkMemberTypeBase: android.SdkMemberTypeBase{
- PropertyName: "stubs_sources",
- // stubs_sources can be used with sdk to provide the source stubs for APIs provided by
- // the APEX.
- SupportsSdk: true,
- },
- })
}
func RegisterDocsBuildComponents(ctx android.RegistrationContext) {
@@ -682,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())
@@ -700,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).
@@ -711,7 +700,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", "javadoc")
+ rule.Build("javadoc", "javadoc")
}
//
@@ -856,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).
@@ -925,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, ":").
@@ -945,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)
@@ -979,7 +968,7 @@
}
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", d.docZip).
@@ -990,7 +979,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", desc)
+ rule.Build("javadoc", desc)
}
//
@@ -1061,7 +1050,8 @@
return android.Paths{d.stubsSrcJar}, nil
case ".docs.zip":
return android.Paths{d.docZip}, nil
- case ".api.txt":
+ case ".api.txt", android.DefaultDistTag:
+ // This is the default dist path for dist properties that have no tag property.
return android.Paths{d.apiFilePath}, nil
case ".removed-api.txt":
return android.Paths{d.removedApiFilePath}, nil
@@ -1288,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").
@@ -1343,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.
@@ -1490,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).
@@ -1513,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).
@@ -1534,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") {
@@ -1552,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 {"
@@ -1574,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())
@@ -1586,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")
@@ -1612,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) != "" {
@@ -1635,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("(").
@@ -1647,7 +1637,7 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
+ rule.Build("nullabilityWarningsCheck", "nullability warnings check")
}
}
@@ -1732,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"`).
@@ -1759,8 +1749,6 @@
properties PrebuiltStubsSourcesProperties
- // The source directories containing stubs source files.
- srcDirs android.Paths
stubsSrcJar android.ModuleOutPath
}
@@ -1780,25 +1768,33 @@
func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
- p.srcDirs = android.PathsForModuleSrc(ctx, p.properties.Srcs)
+ if len(p.properties.Srcs) != 1 {
+ ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
+ return
+ }
- rule := android.NewRuleBuilder()
- command := rule.Command().
- BuiltTool(ctx, "soong_zip").
+ localSrcDir := p.properties.Srcs[0]
+ // Although PathForModuleSrc can return nil if either the path doesn't exist or
+ // the path components are invalid it won't in this case because no components
+ // are specified and the module directory must exist in order to get this far.
+ srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
+
+ // Glob the contents of the directory just in case the directory does not exist.
+ srcGlob := localSrcDir + "/**/*"
+ srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-jar").
- FlagWithOutput("-o ", p.stubsSrcJar)
-
- for _, d := range p.srcDirs {
- dir := d.String()
- command.
- FlagWithArg("-C ", dir).
- FlagWithInput("-D ", d)
- }
+ FlagWithOutput("-o ", p.stubsSrcJar).
+ FlagWithArg("-C ", srcDir.String()).
+ FlagWithRspFileInputList("-r ", srcPaths)
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 {
@@ -1829,47 +1825,3 @@
InitDroiddocModule(module, android.HostAndDeviceSupported)
return module
}
-
-type droidStubsSdkMemberType struct {
- android.SdkMemberTypeBase
-}
-
-func (mt *droidStubsSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
- mctx.AddVariationDependencies(nil, dependencyTag, names...)
-}
-
-func (mt *droidStubsSdkMemberType) IsInstance(module android.Module) bool {
- _, ok := module.(*Droidstubs)
- return ok
-}
-
-func (mt *droidStubsSdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
- return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_stubs_sources")
-}
-
-func (mt *droidStubsSdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
- return &droidStubsInfoProperties{}
-}
-
-type droidStubsInfoProperties struct {
- android.SdkMemberPropertiesBase
-
- StubsSrcJar android.Path
-}
-
-func (p *droidStubsInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
- droidstubs := variant.(*Droidstubs)
- p.StubsSrcJar = droidstubs.stubsSrcJar
-}
-
-func (p *droidStubsInfoProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
- if p.StubsSrcJar != nil {
- builder := ctx.SnapshotBuilder()
-
- snapshotRelativeDir := filepath.Join("java", ctx.Name()+"_stubs_sources")
-
- builder.UnzipToSnapshot(p.StubsSrcJar, snapshotRelativeDir)
-
- propertySet.AddProperty("srcs", []string{snapshotRelativeDir})
- }
-}
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 c3ae75e..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,16 +209,7 @@
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags")
-}
-
-func moduleForGreyListRemovedApis(ctx android.SingletonContext, module android.Module) bool {
- switch ctx.ModuleName(module) {
- case "api-stubs-docs", "system-api-stubs-docs", "android.car-stubs-docs", "android.car-system-stubs-docs":
- return true
- default:
- return false
- }
+ 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
@@ -245,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")
@@ -264,16 +256,18 @@
FlagWithInput("--max-target-p ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-max-target-p.txt")).
FlagWithInput("--max-target-o ", android.PathForSource(
- ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").
+ ctx, "frameworks/base/config/hiddenapi-max-target-o.txt")).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--blocked ",
android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
+ FlagWithInput("--blocked ",
+ android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
FlagWithInput("--unsupported ", android.PathForSource(
ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
FlagWithOutput("--output ", tempPath)
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags")
+ rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
return outputPath
}
@@ -281,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
}
@@ -306,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
}
@@ -406,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/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 7acaae7..34a4856 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -29,8 +29,8 @@
return config
}
-func testContextWithHiddenAPI() *android.TestContext {
- ctx := testContext()
+func testContextWithHiddenAPI(config android.Config) *android.TestContext {
+ ctx := testContext(config)
ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
return ctx
}
@@ -38,7 +38,7 @@
func testHiddenAPIWithConfig(t *testing.T, config android.Config) *android.TestContext {
t.Helper()
- ctx := testContextWithHiddenAPI()
+ ctx := testContextWithHiddenAPI(config)
run(t, ctx, config)
return ctx
diff --git a/java/java.go b/java/java.go
index 9f09051..02d78f2 100644
--- a/java/java.go
+++ b/java/java.go
@@ -201,7 +201,10 @@
// List of modules to use as annotation processors
Plugins []string
- // List of modules to export to libraries that directly depend on this library as annotation processors
+ // List of modules to export to libraries that directly depend on this library as annotation
+ // processors. Note that if the plugins set generates_api: true this will disable the turbine
+ // optimization on modules that depend on this module, which will reduce parallelism and cause
+ // more recompilation.
Exported_plugins []string
// The number of Java source entries each Javac instance can process
@@ -248,6 +251,9 @@
Errorprone struct {
// List of javac flags that should only be used when running errorprone.
Javacflags []string
+
+ // List of java_plugin modules that provide extra errorprone checks.
+ Extra_check_modules []string
}
Proto struct {
@@ -416,8 +422,8 @@
// manifest file to use instead of properties.Manifest
overrideManifest android.OptionalPath
- // map of SDK libs exported by this java module to their build and install paths
- exportedSdkLibs dexpreopt.LibraryPaths
+ // map of SDK version to class loader context
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
// list of plugins that this java module is exporting
exportedPluginJars android.Paths
@@ -425,6 +431,9 @@
// list of plugins that this java module is exporting
exportedPluginClasses []string
+ // if true, the exported plugins generate API and require disabling turbine.
+ exportedDisableTurbine bool
+
// list of source files, collected from srcFiles with unique java and all kt files,
// will be used by android.IDEInfo struct
expandIDEInfoCompiledSrcs []string
@@ -447,8 +456,6 @@
// list of the xref extraction files
kytheFiles android.Paths
- distFiles android.TaggedDistFiles
-
// Collect the module directory for IDE info in java/jdeps.go.
modulePaths []string
@@ -477,6 +484,8 @@
switch tag {
case "":
return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
+ case android.DefaultDistTag:
+ return android.Paths{j.outputFile}, nil
case ".jar":
return android.Paths{j.implementationAndResourcesJar}, nil
case ".proguard_map":
@@ -509,8 +518,8 @@
ImplementationJars() android.Paths
ResourceJars() android.Paths
AidlIncludeDirs() android.Paths
- ExportedSdkLibs() dexpreopt.LibraryPaths
- ExportedPlugins() (android.Paths, []string)
+ ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
+ ExportedPlugins() (android.Paths, []string, bool)
SrcJarArgs() ([]string, android.Paths)
BaseModuleName() string
JacocoReportClassesFile() android.Path
@@ -547,6 +556,14 @@
name string
}
+// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
+// dependency to be installed when the parent module is installed.
+type installDependencyTag struct {
+ blueprint.BaseDependencyTag
+ android.InstallAlwaysNeededDependencyTag
+ name string
+}
+
type usesLibraryDependencyTag struct {
dependencyTag
sdkVersion int // SDK version in which the library appared as a standalone library.
@@ -569,6 +586,7 @@
libTag = dependencyTag{name: "javalib"}
java9LibTag = dependencyTag{name: "java9lib"}
pluginTag = dependencyTag{name: "plugin"}
+ errorpronePluginTag = dependencyTag{name: "errorprone-plugin"}
exportedPluginTag = dependencyTag{name: "exported-plugin"}
bootClasspathTag = dependencyTag{name: "bootclasspath"}
systemModulesTag = dependencyTag{name: "system modules"}
@@ -580,6 +598,8 @@
instrumentationForTag = dependencyTag{name: "instrumentation_for"}
extraLintCheckTag = dependencyTag{name: "extra-lint-check"}
jniLibTag = dependencyTag{name: "jnilib"}
+ jniInstallTag = installDependencyTag{name: "jni install"}
+ binaryInstallTag = installDependencyTag{name: "binary install"}
usesLibTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
usesLibCompat28Tag = makeUsesLibraryDependencyTag(28)
usesLibCompat29Tag = makeUsesLibraryDependencyTag(29)
@@ -752,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 {
@@ -765,6 +816,7 @@
}
ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), pluginTag, j.properties.Plugins...)
+ ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), errorpronePluginTag, j.properties.Errorprone.Extra_check_modules...)
ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), exportedPluginTag, j.properties.Exported_plugins...)
android.ProtoDeps(ctx, &j.protoProperties)
@@ -852,21 +904,22 @@
}
type deps struct {
- classpath classpath
- java9Classpath classpath
- bootClasspath classpath
- processorPath classpath
- processorClasses []string
- staticJars android.Paths
- staticHeaderJars android.Paths
- staticResourceJars android.Paths
- aidlIncludeDirs android.Paths
- srcs android.Paths
- srcJars android.Paths
- systemModules *systemModules
- aidlPreprocess android.OptionalPath
- kotlinStdlib android.Paths
- kotlinAnnotations android.Paths
+ classpath classpath
+ java9Classpath classpath
+ bootClasspath classpath
+ processorPath classpath
+ errorProneProcessorPath classpath
+ processorClasses []string
+ staticJars android.Paths
+ staticHeaderJars android.Paths
+ staticResourceJars android.Paths
+ aidlIncludeDirs android.Paths
+ srcs android.Paths
+ srcJars android.Paths
+ systemModules *systemModules
+ aidlPreprocess android.OptionalPath
+ kotlinStdlib android.Paths
+ kotlinAnnotations android.Paths
disableTurbine bool
}
@@ -952,7 +1005,9 @@
return
}
otherLinkType, _ := to.getLinkType(ctx.OtherModuleName(to))
- commonMessage := "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source."
+ commonMessage := " In order to fix this, consider adjusting sdk_version: OR platform_apis: " +
+ "property of the source or target module so that target module is built with the same " +
+ "or smaller API set when compared to the source."
switch myLinkType {
case javaCore:
@@ -1026,8 +1081,8 @@
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.exportedSdkLibs.MaybeAddLibraryPath(ctx, dep.OptionalImplicitSdkLibrary(), dep.DexJarBuildPath(), dep.DexJarInstallPath())
+ j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(),
+ dep.DexJarBuildPath(), dep.DexJarInstallPath())
case staticLibTag:
ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
}
@@ -1037,11 +1092,11 @@
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.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
+ j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
- pluginJars, pluginClasses := dep.ExportedPlugins()
+ pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
+ deps.disableTurbine = deps.disableTurbine || disableTurbine
case java9LibTag:
deps.java9Classpath = append(deps.java9Classpath, dep.HeaderJars()...)
case staticLibTag:
@@ -1049,11 +1104,13 @@
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.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
- pluginJars, pluginClasses := dep.ExportedPlugins()
+ pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
addPlugins(&deps, pluginJars, pluginClasses...)
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
+ deps.disableTurbine = deps.disableTurbine || disableTurbine
case pluginTag:
if plugin, ok := dep.(*Plugin); ok {
if plugin.pluginProperties.Processor_class != nil {
@@ -1061,19 +1118,29 @@
} else {
addPlugins(&deps, plugin.ImplementationAndResourcesJars())
}
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
deps.disableTurbine = deps.disableTurbine || Bool(plugin.pluginProperties.Generates_api)
} else {
ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
}
+ case errorpronePluginTag:
+ if plugin, ok := dep.(*Plugin); ok {
+ deps.errorProneProcessorPath = append(deps.errorProneProcessorPath, plugin.ImplementationAndResourcesJars()...)
+ } else {
+ ctx.PropertyErrorf("plugins", "%q is not a java_plugin module", otherName)
+ }
case exportedPluginTag:
if plugin, ok := dep.(*Plugin); ok {
- if plugin.pluginProperties.Generates_api != nil && *plugin.pluginProperties.Generates_api {
- ctx.PropertyErrorf("exported_plugins", "Cannot export plugins with generates_api = true, found %v", otherName)
- }
j.exportedPluginJars = append(j.exportedPluginJars, plugin.ImplementationAndResourcesJars()...)
if plugin.pluginProperties.Processor_class != nil {
j.exportedPluginClasses = append(j.exportedPluginClasses, *plugin.pluginProperties.Processor_class)
}
+ // Turbine doesn't run annotation processors, so any module that uses an
+ // annotation processor that generates API is incompatible with the turbine
+ // optimization.
+ j.exportedDisableTurbine = Bool(plugin.pluginProperties.Generates_api)
} else {
ctx.PropertyErrorf("exported_plugins", "%q is not a java_plugin module", otherName)
}
@@ -1111,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
@@ -1188,7 +1258,7 @@
flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
if ctx.Config().RunErrorProne() {
- if config.ErrorProneClasspath == nil {
+ if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
}
@@ -1208,6 +1278,7 @@
flags.classpath = append(flags.classpath, deps.classpath...)
flags.java9Classpath = append(flags.java9Classpath, deps.java9Classpath...)
flags.processorPath = append(flags.processorPath, deps.processorPath...)
+ flags.errorProneProcessorPath = append(flags.errorProneProcessorPath, deps.errorProneProcessorPath...)
flags.processors = append(flags.processors, deps.processorClasses...)
flags.processors = android.FirstUniqueStrings(flags.processors)
@@ -1374,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.
@@ -1902,12 +1976,15 @@
return j.exportAidlIncludeDirs
}
-func (j *Module) ExportedSdkLibs() dexpreopt.LibraryPaths {
- return j.exportedSdkLibs
+func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return j.classLoaderContexts
}
-func (j *Module) ExportedPlugins() (android.Paths, []string) {
- return j.exportedPluginJars, j.exportedPluginClasses
+// ExportedPlugins returns the list of jars needed to run the exported plugins, the list of
+// classes for the plugins, and a boolean for whether turbine needs to be disabled due to plugins
+// that generate APIs.
+func (j *Module) ExportedPlugins() (android.Paths, []string, bool) {
+ return j.exportedPluginJars, j.exportedPluginClasses, j.exportedDisableTurbine
}
func (j *Module) SrcJarArgs() ([]string, android.Paths) {
@@ -1944,10 +2021,12 @@
return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
}
+// Implements android.ApexModule
func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return j.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
sdkSpec := j.minSdkVersion()
@@ -1993,6 +2072,8 @@
InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
}
+var _ android.ApexModule = (*Library)(nil)
+
// Provides access to the list of permitted packages from updatable boot jars.
type PermittedPackagesForUpdatableBootJars interface {
PermittedPackagesForUpdatableBootJars() []string
@@ -2041,7 +2122,7 @@
j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
}
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
- j.exportedSdkLibs = make(dexpreopt.LibraryPaths)
+ j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
j.compile(ctx, nil)
// Collect the module directory for IDE info in java/jdeps.go.
@@ -2061,14 +2142,13 @@
// add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest.
- j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), j.DexJarBuildPath(), j.DexJarInstallPath())
+ j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
+ j.DexJarBuildPath(), j.DexJarInstallPath())
// A non-SDK library may provide a <uses-library> (the name may be different from the module name).
if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
- j.exportedSdkLibs.AddLibraryPath(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
+ j.classLoaderContexts.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
}
-
- j.distFiles = j.GenerateTaggedDistFiles(ctx)
}
func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2224,6 +2304,9 @@
type TestOptions struct {
// a list of extra test configuration files that should be installed with the module.
Extra_test_configs []string `android:"path,arch_variant"`
+
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
}
type testProperties struct {
@@ -2320,7 +2403,7 @@
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
- j.testProperties.Test_suites, j.testProperties.Auto_gen_config)
+ j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
@@ -2339,7 +2422,7 @@
func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
- j.prebuiltTestProperties.Test_suites, nil)
+ j.prebuiltTestProperties.Test_suites, nil, nil)
j.Import.GenerateAndroidBuildActions(ctx)
}
@@ -2548,9 +2631,12 @@
if ctx.Arch().ArchType == android.Common {
j.deps(ctx)
} else {
- // This dependency ensures the host installation rules will install the jni libraries
- // when the wrapper is installed.
- ctx.AddVariationDependencies(nil, jniLibTag, j.binaryProperties.Jni_libs...)
+ // These dependencies ensure the host installation rules will install the jar file and
+ // the jni libraries when the wrapper is installed.
+ ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
+ ctx.AddVariationDependencies(
+ []blueprint.Variation{{Mutator: "arch", Variation: android.CommonArch.String()}},
+ binaryInstallTag, ctx.ModuleName())
}
}
@@ -2644,7 +2730,7 @@
dexJarFile android.Path
combinedClasspathFile android.Path
- exportedSdkLibs dexpreopt.LibraryPaths
+ classLoaderContexts dexpreopt.ClassLoaderContextMap
exportAidlIncludeDirs android.Paths
hideApexVariantFromMake bool
@@ -2719,7 +2805,7 @@
TransformJetifier(ctx, outputFile, inputFile)
}
j.combinedClasspathFile = outputFile
- j.exportedSdkLibs = make(dexpreopt.LibraryPaths)
+ j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
var flags javaBuilderFlags
@@ -2732,8 +2818,6 @@
switch tag {
case libTag, staticLibTag:
flags.classpath = append(flags.classpath, dep.HeaderJars()...)
- // sdk lib names from dependencies are re-exported
- j.exportedSdkLibs.AddLibraryPaths(dep.ExportedSdkLibs())
case bootClasspathTag:
flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
}
@@ -2741,10 +2825,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.exportedSdkLibs.AddLibraryPath(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
+ 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
@@ -2757,7 +2843,8 @@
// add the name of that java_sdk_library to the exported sdk libs to make sure
// that, if necessary, a <uses-library> element for that java_sdk_library is
// added to the Android manifest.
- j.exportedSdkLibs.MaybeAddLibraryPath(ctx, j.OptionalImplicitSdkLibrary(), outputFile, installFile)
+ j.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
+ outputFile, installFile)
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
@@ -2839,22 +2926,26 @@
return j.exportAidlIncludeDirs
}
-func (j *Import) ExportedSdkLibs() dexpreopt.LibraryPaths {
- return j.exportedSdkLibs
+func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+ return j.classLoaderContexts
}
-func (j *Import) ExportedPlugins() (android.Paths, []string) {
- return nil, nil
+func (j *Import) ExportedPlugins() (android.Paths, []string, bool) {
+ return nil, nil, false
}
func (j *Import) SrcJarArgs() ([]string, android.Paths) {
return nil, nil
}
+var _ android.ApexModule = (*Import)(nil)
+
+// Implements android.ApexModule
func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
return j.depIsInSameApex(ctx, dep)
}
+// Implements android.ApexModule
func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// Do not check for prebuilts against the min_sdk_version of enclosing APEX
@@ -2999,21 +3090,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).
@@ -3021,7 +3112,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,
@@ -3046,6 +3137,9 @@
return j.dexJarFile
}
+var _ android.ApexModule = (*DexImport)(nil)
+
+// Implements android.ApexModule
func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// we don't check prebuilt modules for sdk_version
@@ -3164,3 +3258,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 6c0a908..f7cf03f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"io/ioutil"
"os"
"path/filepath"
@@ -29,7 +30,6 @@
"android/soong/android"
"android/soong/cc"
"android/soong/dexpreopt"
- "android/soong/genrule"
"android/soong/python"
)
@@ -70,9 +70,9 @@
return config
}
-func testContext() *android.TestContext {
+func testContext(config android.Config) *android.TestContext {
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
RegisterJavaBuildComponents(ctx)
RegisterAppBuildComponents(ctx)
RegisterAARBuildComponents(ctx)
@@ -80,7 +80,6 @@
RegisterSystemModulesBuildComponents(ctx)
ctx.RegisterModuleType("java_plugin", PluginFactory)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
- ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
ctx.RegisterModuleType("python_binary_host", python.PythonBinaryHostFactory)
RegisterDocsBuildComponents(ctx)
RegisterStubsBuildComponents(ctx)
@@ -92,8 +91,8 @@
ctx.PreDepsMutators(python.RegisterPythonPreDepsMutators)
ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
- ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(OverlaySingletonFactory))
- ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(sdkPreSingletonFactory))
+ ctx.RegisterPreSingletonType("overlay", android.SingletonFactoryAdaptor(ctx.Context, OverlaySingletonFactory))
+ ctx.RegisterPreSingletonType("sdk_versions", android.SingletonFactoryAdaptor(ctx.Context, sdkPreSingletonFactory))
android.RegisterPrebuiltMutators(ctx)
@@ -115,7 +114,7 @@
pathCtx := android.PathContextForTesting(config)
dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
@@ -129,12 +128,12 @@
func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config) (*android.TestContext, android.Config) {
t.Helper()
- ctx := testContext()
+ ctx := testContext(config)
pathCtx := android.PathContextForTesting(config)
dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx))
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseBlueprintsFiles("Android.bp")
if len(errs) > 0 {
android.FailIfNoMatchingErrors(t, pattern, errs)
@@ -163,7 +162,7 @@
func testJavaWithConfig(t *testing.T, config android.Config) (*android.TestContext, android.Config) {
t.Helper()
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
return ctx, config
@@ -202,7 +201,7 @@
}
`)
- testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+ testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -246,7 +245,7 @@
}
`)
- testJavaError(t, "Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.", `
+ testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
java_library {
name: "foo",
srcs: ["a.java"],
@@ -314,8 +313,9 @@
func TestExportedPlugins(t *testing.T) {
type Result struct {
- library string
- processors string
+ library string
+ processors string
+ disableTurbine bool
}
var tests = []struct {
name string
@@ -374,6 +374,18 @@
{library: "foo", processors: "-processor com.android.TestPlugin,com.android.TestPlugin2"},
},
},
+ {
+ name: "Exports plugin to with generates_api to dependee",
+ extra: `
+ java_library{name: "exports", exported_plugins: ["plugin_generates_api"]}
+ java_library{name: "foo", srcs: ["a.java"], libs: ["exports"]}
+ java_library{name: "bar", srcs: ["a.java"], static_libs: ["exports"]}
+ `,
+ results: []Result{
+ {library: "foo", processors: "-processor com.android.TestPlugin", disableTurbine: true},
+ {library: "bar", processors: "-processor com.android.TestPlugin", disableTurbine: true},
+ },
+ },
}
for _, test := range tests {
@@ -383,6 +395,11 @@
name: "plugin",
processor_class: "com.android.TestPlugin",
}
+ java_plugin {
+ name: "plugin_generates_api",
+ generates_api: true,
+ processor_class: "com.android.TestPlugin",
+ }
`+test.extra)
for _, want := range test.results {
@@ -390,6 +407,11 @@
if javac.Args["processor"] != want.processors {
t.Errorf("For library %v, expected %v, found %v", want.library, want.processors, javac.Args["processor"])
}
+ turbine := ctx.ModuleForTests(want.library, "android_common").MaybeRule("turbine")
+ disableTurbine := turbine.BuildParams.Rule == nil
+ if disableTurbine != want.disableTurbine {
+ t.Errorf("For library %v, expected disableTurbine %v, found %v", want.library, want.disableTurbine, disableTurbine)
+ }
}
})
}
@@ -622,6 +644,35 @@
}
}
+func TestPrebuiltStubsSources(t *testing.T) {
+ test := func(t *testing.T, sourcesPath string, expectedInputs []string) {
+ ctx, _ := testJavaWithFS(t, fmt.Sprintf(`
+prebuilt_stubs_sources {
+ name: "stubs-source",
+ srcs: ["%s"],
+}`, sourcesPath), map[string][]byte{
+ "stubs/sources/pkg/A.java": nil,
+ "stubs/sources/pkg/B.java": nil,
+ })
+
+ zipSrc := ctx.ModuleForTests("stubs-source", "android_common").Rule("zip_src")
+ if expected, actual := expectedInputs, zipSrc.Inputs.Strings(); !reflect.DeepEqual(expected, actual) {
+ t.Errorf("mismatch of inputs to soong_zip: expected %q, actual %q", expected, actual)
+ }
+ }
+
+ t.Run("empty/missing directory", func(t *testing.T) {
+ test(t, "empty-directory", []string{})
+ })
+
+ t.Run("non-empty set of sources", func(t *testing.T) {
+ test(t, "stubs/sources", []string{
+ "stubs/sources/pkg/A.java",
+ "stubs/sources/pkg/B.java",
+ })
+ })
+}
+
func TestJavaSdkLibraryImport(t *testing.T) {
ctx, _ := testJava(t, `
java_library {
@@ -754,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 {
@@ -1379,8 +1589,8 @@
baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
- if len(jargen.Inputs) != 1 || jargen.Inputs[0].String() != foo.Output.String() {
- t.Errorf("expected jargen inputs [%q], got %q", foo.Output.String(), jargen.Inputs.Strings())
+ if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
+ t.Errorf("expected jargen inputs [%q], got %q", w, g)
}
if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
@@ -1440,7 +1650,7 @@
}
`),
})
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
}
@@ -1458,7 +1668,7 @@
}
`),
})
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
}
@@ -1593,8 +1803,8 @@
// test if baz has exported SDK lib names foo and bar to qux
qux := ctx.ModuleForTests("qux", "android_common")
if quxLib, ok := qux.Module().(*Library); ok {
- sdkLibs := android.SortedStringKeys(quxLib.ExportedSdkLibs())
- if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
+ sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
+ if w := []string{"foo", "bar", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
t.Errorf("qux should export %q but exports %q", w, sdkLibs)
}
}
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/kotlin_test.go b/java/kotlin_test.go
index 60ca1c4..77ef294 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -84,11 +84,14 @@
}
func TestKapt(t *testing.T) {
- ctx, _ := testJava(t, `
+ bp := `
java_library {
name: "foo",
srcs: ["a.java", "b.kt"],
plugins: ["bar", "baz"],
+ errorprone: {
+ extra_check_modules: ["my_check"],
+ },
}
java_plugin {
@@ -102,64 +105,119 @@
processor_class: "com.baz",
srcs: ["b.java"],
}
- `)
- buildOS := android.BuildOs.String()
+ java_plugin {
+ name: "my_check",
+ srcs: ["b.java"],
+ }
+ `
+ t.Run("", func(t *testing.T) {
+ ctx, _ := testJava(t, bp)
- kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
- kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ buildOS := android.BuildOs.String()
- bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
- baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
+ kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
- // Test that the kotlin and java sources are passed to kapt and kotlinc
- if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
- }
- if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
- }
+ bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
+ baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
- // Test that only the java sources are passed to javac
- if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
- t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
- }
+ // Test that the kotlin and java sources are passed to kapt and kotlinc
+ if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
+ }
+ if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
+ }
- // Test that the kapt srcjar is a dependency of kotlinc and javac rules
- if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
- t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
- }
- if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
- t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
- }
+ // Test that only the java sources are passed to javac
+ if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+ }
- // Test that the kapt srcjar is extracted by the kotlinc and javac rules
- if kotlinc.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
- }
- if javac.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
- }
+ // Test that the kapt srcjar is a dependency of kotlinc and javac rules
+ if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
+ t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
+ }
+ if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
+ t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
+ }
- // Test that the processors are passed to kapt
- expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
- " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
- if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
- t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
- }
- expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
- if kapt.Args["kaptProcessor"] != expectedProcessor {
- t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
- }
+ // Test that the kapt srcjar is extracted by the kotlinc and javac rules
+ if kotlinc.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
+ if javac.Args["srcJars"] != kapt.Output.String() {
+ t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ }
- // Test that the processors are not passed to javac
- if javac.Args["processorPath"] != "" {
- t.Errorf("expected processorPath '', got %q", javac.Args["processorPath"])
- }
- if javac.Args["processor"] != "-proc:none" {
- t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
- }
+ // Test that the processors are passed to kapt
+ expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
+ " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
+ if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
+ t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
+ }
+ expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
+ if kapt.Args["kaptProcessor"] != expectedProcessor {
+ t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
+ }
+
+ // Test that the processors are not passed to javac
+ if javac.Args["processorpath"] != "" {
+ t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"])
+ }
+ if javac.Args["processor"] != "-proc:none" {
+ t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
+ }
+ })
+
+ t.Run("errorprone", func(t *testing.T) {
+ env := map[string]string{
+ "RUN_ERROR_PRONE": "true",
+ }
+ config := testConfig(env, bp, nil)
+ ctx, _ := testJavaWithConfig(t, config)
+
+ buildOS := android.BuildOs.String()
+
+ kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
+ //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+ errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+
+ bar := ctx.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
+ baz := ctx.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
+ myCheck := ctx.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
+
+ // Test that the errorprone plugins are not passed to kapt
+ expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
+ " -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
+ if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
+ t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
+ }
+ expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
+ if kapt.Args["kaptProcessor"] != expectedProcessor {
+ t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
+ }
+
+ // Test that the errorprone plugins are not passed to javac
+ if javac.Args["processorpath"] != "" {
+ t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"])
+ }
+ if javac.Args["processor"] != "-proc:none" {
+ t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
+ }
+
+ // Test that the errorprone plugins are passed to errorprone
+ expectedProcessorPath = "-processorpath " + myCheck
+ if errorprone.Args["processorpath"] != expectedProcessorPath {
+ t.Errorf("expected processorpath %q, got %q", expectedProcessorPath, errorprone.Args["processorpath"])
+ }
+ if errorprone.Args["processor"] != "-proc:none" {
+ t.Errorf("expected processor '-proc:none', got %q", errorprone.Args["processor"])
+ }
+ })
}
func TestKaptEncodeFlags(t *testing.T) {
diff --git a/java/lint.go b/java/lint.go
index 3df582f..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,
@@ -447,7 +447,7 @@
var outputs []*lintOutputs
var dirs []string
ctx.VisitAllModules(func(m android.Module) {
- if ctx.Config().EmbeddedInMake() && !m.ExportedToMake() {
+ if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
return
}
@@ -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 04fc117..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
@@ -389,8 +389,10 @@
}
runtimeFromSourceJar := android.OutputFileForModule(ctx, runtimeFromSourceModule, "")
+ // TODO(murj) Update this to ctx.Config().PlatformSdkCodename() once the platform
+ // classes like android.os.Build are updated to S.
runtimeName := fmt.Sprintf("android-all-%s-robolectric-r0.jar",
- ctx.Config().PlatformSdkCodename())
+ "R")
installedRuntime := ctx.InstallFile(androidAllDir, runtimeName, runtimeFromSourceJar)
r.runtimes = append(r.runtimes, installedRuntime)
}
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 d6ef4e9..2e10f9c 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -435,8 +435,23 @@
// If set to true, the path of dist files is apistubs/core. Defaults to false.
Core_lib *bool
- // don't create dist rules.
- No_dist *bool `blueprint:"mutated"`
+ // If set to true then don't create dist rules.
+ No_dist *bool
+
+ // The stem for the artifacts that are copied to the dist, if not specified
+ // then defaults to the base module name.
+ //
+ // For each scope the following artifacts are copied to the apistubs/<scope>
+ // directory in the dist.
+ // * stubs impl jar -> <dist-stem>.jar
+ // * API specification file -> api/<dist-stem>.txt
+ // * Removed API specification file -> api/<dist-stem>-removed.txt
+ //
+ // Also used to construct the name of the filegroup (created by prebuilt_apis)
+ // that references the latest released API and remove API specification files.
+ // * API specification filegroup -> <dist-stem>.api.<scope>.latest
+ // * Removed API specification filegroup -> <dist-stem>-removed.api.<scope>.latest
+ Dist_stem *string
// indicates whether system and test apis should be generated.
Generate_system_and_test_apis bool `blueprint:"mutated"`
@@ -1109,12 +1124,16 @@
}
}
+func (module *SdkLibrary) distStem() string {
+ return proptools.StringDefault(module.sdkLibraryProperties.Dist_stem, module.BaseModuleName())
+}
+
func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.BaseModuleName() + ".api." + apiScope.name + ".latest"
+ return ":" + module.distStem() + ".api." + apiScope.name + ".latest"
}
func (module *SdkLibrary) latestRemovedApiFilegroupName(apiScope *apiScope) string {
- return ":" + module.BaseModuleName() + "-removed.api." + apiScope.name + ".latest"
+ return ":" + module.distStem() + "-removed.api." + apiScope.name + ".latest"
}
func childModuleVisibility(childVisibility []string) []string {
@@ -1220,7 +1239,7 @@
// Dist the class jar artifact for sdk builds.
if !Bool(module.sdkLibraryProperties.No_dist) {
props.Dist.Targets = []string{"sdk", "win_sdk"}
- props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.BaseModuleName()))
+ props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.jar", module.distStem()))
props.Dist.Dir = proptools.StringPtr(module.apiDistPath(apiScope))
props.Dist.Tag = proptools.StringPtr(".jar")
}
@@ -1262,11 +1281,7 @@
Include_dirs []string
Local_include_dirs []string
}
- Dist struct {
- Targets []string
- Dest *string
- Dir *string
- }
+ Dists []android.Dist
}{}
// The stubs source processing uses the same compile time classpath when extracting the
@@ -1366,11 +1381,23 @@
}
}
- // Dist the api txt artifact for sdk builds.
if !Bool(module.sdkLibraryProperties.No_dist) {
- props.Dist.Targets = []string{"sdk", "win_sdk"}
- props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
- props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+ // Dist the api txt and removed api txt artifacts for sdk builds.
+ distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+ for _, p := range []struct {
+ tag string
+ pattern string
+ }{
+ {tag: ".api.txt", pattern: "%s.txt"},
+ {tag: ".removed-api.txt", pattern: "%s-removed.txt"},
+ } {
+ props.Dists = append(props.Dists, android.Dist{
+ Targets: []string{"sdk", "win_sdk"},
+ Dir: distDir,
+ Dest: proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
+ Tag: proptools.StringPtr(p.tag),
+ })
+ }
}
mctx.CreateModule(DroidstubsFactory, &props)
@@ -1474,10 +1501,6 @@
return module.sdkJars(ctx, sdkVersion, false /*headerJars*/)
}
-func (module *SdkLibrary) SetNoDist() {
- module.sdkLibraryProperties.No_dist = proptools.BoolPtr(true)
-}
-
var javaSdkLibrariesKey = android.NewOnceKey("javaSdkLibraries")
func javaSdkLibraries(config android.Config) *[]string {
@@ -1505,12 +1528,10 @@
}
// If this builds against standard libraries (i.e. is not part of the core libraries)
- // then assume it provides both system and test apis. Otherwise, assume it does not and
- // also assume it does not contribute to the dist build.
+ // then assume it provides both system and test apis.
sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
hasSystemAndTestApis := sdkDep.hasStandardLibs()
module.sdkLibraryProperties.Generate_system_and_test_apis = hasSystemAndTestApis
- module.sdkLibraryProperties.No_dist = proptools.BoolPtr(!hasSystemAndTestApis)
missing_current_api := false
@@ -1904,6 +1925,9 @@
}
}
+var _ android.ApexModule = (*SdkLibraryImport)(nil)
+
+// Implements android.ApexModule
func (module *SdkLibraryImport) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
depTag := mctx.OtherModuleDependencyTag(dep)
if depTag == xmlPermissionsFileTag {
@@ -1915,6 +1939,7 @@
return false
}
+// Implements android.ApexModule
func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// we don't check prebuilt modules for sdk_version
@@ -2120,6 +2145,9 @@
// do nothing
}
+var _ android.ApexModule = (*sdkLibraryXml)(nil)
+
+// Implements android.ApexModule
func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
@@ -2155,12 +2183,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/java/sdk_test.go b/java/sdk_test.go
index 776069d..dc90ea3 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -356,7 +356,7 @@
config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
checkClasspath(t, ctx, true /* isJava8 */)
@@ -377,7 +377,7 @@
config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
checkClasspath(t, ctx, false /* isJava8 */)
@@ -401,7 +401,7 @@
config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
checkClasspath(t, ctx, true /* isJava8 */)
@@ -417,7 +417,7 @@
config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
config.TestProductVariables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
}
- ctx := testContext()
+ ctx := testContext(config)
run(t, ctx, config)
checkClasspath(t, ctx, false /* isJava8 */)
diff --git a/java/sysprop.go b/java/sysprop.go
index 1a70499..e41aef6 100644
--- a/java/sysprop.go
+++ b/java/sysprop.go
@@ -14,6 +14,10 @@
package java
+// This file contains a map to redirect dependencies towards sysprop_library. If a sysprop_library
+// is owned by Platform, and the client module links against system API, the public stub of the
+// sysprop_library should be used. The map will contain public stub names of sysprop_libraries.
+
import (
"sync"
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/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 13c276a..01f4657 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -56,9 +56,9 @@
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("linker_config", linkerConfigFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
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/linkerconfig/proto/linker_config.proto b/linkerconfig/proto/linker_config.proto
index 91a5968..fec66c8 100644
--- a/linkerconfig/proto/linker_config.proto
+++ b/linkerconfig/proto/linker_config.proto
@@ -28,4 +28,10 @@
// Force APEX namespace visible
bool visible = 2;
+
+ // Providing libs from the module
+ repeated string provideLibs = 3;
+
+ // Required libs from the module
+ repeated string requireLibs = 4;
}
diff --git a/python/androidmk.go b/python/androidmk.go
index 8ad5889..60637d3 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -15,8 +15,6 @@
package python
import (
- "fmt"
- "io"
"path/filepath"
"strings"
@@ -24,82 +22,72 @@
)
type subAndroidMkProvider interface {
- AndroidMk(*Module, *android.AndroidMkData)
+ AndroidMk(*Module, *android.AndroidMkEntries)
}
-func (p *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
if p.subAndroidMkOnce == nil {
p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
}
if androidmk, ok := obj.(subAndroidMkProvider); ok {
if !p.subAndroidMkOnce[androidmk] {
p.subAndroidMkOnce[androidmk] = true
- androidmk.AndroidMk(p, data)
+ androidmk.AndroidMk(p, entries)
}
}
}
-func (p *Module) AndroidMk() android.AndroidMkData {
- ret := android.AndroidMkData{OutputFile: p.installSource}
+func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
+ entries := android.AndroidMkEntries{OutputFile: p.installSource}
- p.subAndroidMk(&ret, p.installer)
+ p.subAndroidMk(&entries, p.installer)
- return ret
+ return []android.AndroidMkEntries{entries}
}
-func (p *binaryDecorator) AndroidMk(base *Module, ret *android.AndroidMkData) {
- ret.Class = "EXECUTABLES"
+func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
+ entries.Class = "EXECUTABLES"
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if len(p.binaryProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(p.binaryProperties.Test_suites, " "))
- }
+ entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
})
- base.subAndroidMk(ret, p.pythonInstaller)
+ base.subAndroidMk(entries, p.pythonInstaller)
}
-func (p *testDecorator) AndroidMk(base *Module, ret *android.AndroidMkData) {
- ret.Class = "NATIVE_TESTS"
+func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
+ entries.Class = "NATIVE_TESTS"
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if len(p.binaryDecorator.binaryProperties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(p.binaryDecorator.binaryProperties.Test_suites, " "))
- }
+ entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
if p.testConfig != nil {
- fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=",
- p.testConfig.String())
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
}
- if !BoolDefault(p.binaryProperties.Auto_gen_config, true) {
- fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
- }
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
- if len(p.data) > 0 {
- fmt.Fprintln(w, "LOCAL_TEST_DATA :=",
- strings.Join(android.AndroidMkDataPaths(p.data), " "))
- }
+ entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)
+
+ entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(p.testProperties.Test_options.Unit_test))
})
- base.subAndroidMk(ret, p.binaryDecorator.pythonInstaller)
+ base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
}
-func (installer *pythonInstaller) AndroidMk(base *Module, ret *android.AndroidMkData) {
+func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
// Soong installation is only supported for host modules. Have Make
// installation trigger Soong installation.
if base.Target().Os.Class == android.Host {
- ret.OutputFile = android.OptionalPathForPath(installer.path)
+ entries.OutputFile = android.OptionalPathForPath(installer.path)
}
- ret.Required = append(ret.Required, "libc++")
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ entries.Required = append(entries.Required, "libc++")
+ entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
path, file := filepath.Split(installer.path.ToMakePath().String())
stem := strings.TrimSuffix(file, filepath.Ext(file))
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file))
- fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
- fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
- fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(installer.androidMkSharedLibs, " "))
- fmt.Fprintln(w, "LOCAL_CHECK_ELF_FILES := false")
+ entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
+ entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
})
}
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 479c729..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("version_split", versionSplitMutator()).Parallel()
+ 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.
@@ -86,14 +86,19 @@
// the test. the file extension can be arbitrary except for (.py).
Data []string `android:"path,arch_variant"`
+ // list of java modules that provide data that should be installed alongside the test.
+ Java_data []string
+
// list of the Python libraries compatible both with Python2 and Python3.
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"`
@@ -101,8 +106,17 @@
// this property name is hidden from users' perspectives, and soong will populate it during
// runtime.
Actual_version string `blueprint:"mutated"`
+
+ // 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"`
+
+ // 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
@@ -119,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.
@@ -143,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,
@@ -153,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,
@@ -162,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.AndroidMkDataProvider = (*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()...)
}
@@ -202,16 +231,29 @@
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
+}
+
var (
pythonLibTag = dependencyTag{name: "pythonLib"}
+ javaDataTag = dependencyTag{name: "javaData"}
launcherTag = dependencyTag{name: "launcher"}
- launcherSharedLibTag = dependencyTag{name: "launcherSharedLib"}
- pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
+ launcherSharedLibTag = installDependencyTag{name: "launcherSharedLib"}
+ pathComponentRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
pyExt = ".py"
protoExt = ".proto"
pyVersion2 = "PY2"
@@ -220,44 +262,59 @@
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.CreateVariations(versionNames...)
+ 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 to the overall properties
+ err := proptools.AppendMatchingProperties([]interface{}{&modules[i].(*Module).properties}, &versionProps[i], nil)
+ if err != nil {
+ panic(err)
+ }
}
}
}
}
+// 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 "":
@@ -270,20 +327,13 @@
}
}
-func (p *Module) isEmbeddedLauncherEnabled(actual_version string) bool {
- switch actual_version {
- case pyVersion2:
- return Bool(p.properties.Version.Py2.Embedded_launcher)
- case pyVersion3:
- return Bool(p.properties.Version.Py3.Embedded_launcher)
- }
-
- return false
+func (p *Module) isEmbeddedLauncherEnabled() bool {
+ 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
}
}
@@ -291,136 +341,104 @@
return false
}
-func (p *Module) hasSrcExt(ctx android.BottomUpMutatorContext, ext string) bool {
- if hasSrcExt(p.properties.Srcs, protoExt) {
- return true
- }
- switch p.properties.Actual_version {
- case pyVersion2:
- return hasSrcExt(p.properties.Version.Py2.Srcs, protoExt)
- case pyVersion3:
- return hasSrcExt(p.properties.Version.Py3.Srcs, protoExt)
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
- }
+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)
- if p.hasSrcExt(ctx, protoExt) && p.Name() != "libprotobuf-python" {
- ctx.AddVariationDependencies(nil, pythonLibTag, "libprotobuf-python")
+ versionVariation := []blueprint.Variation{
+ {"python_version", p.properties.Actual_version},
}
- switch p.properties.Actual_version {
- case pyVersion2:
- ctx.AddVariationDependencies(nil, pythonLibTag,
- uniqueLibs(ctx, p.properties.Libs, "version.py2.libs",
- p.properties.Version.Py2.Libs)...)
- if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion2) {
- ctx.AddVariationDependencies(nil, pythonLibTag, "py2-stdlib")
+ // 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")
+ }
- launcherModule := "py2-launcher"
+ // Add python library dependencies for this python version variation
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, android.LastUniqueStrings(p.properties.Libs)...)
+
+ // 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")
+ }
+
+ switch p.properties.Actual_version {
+ case pyVersion2:
+ stdLib = "py2-stdlib"
+
+ 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")
+ case pyVersion3:
+ stdLib = "py3-stdlib"
- if ctx.Target().Os.Bionic() {
- ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag,
- "libc", "libdl", "libm")
- }
- }
-
- case pyVersion3:
- ctx.AddVariationDependencies(nil, pythonLibTag,
- uniqueLibs(ctx, p.properties.Libs, "version.py3.libs",
- p.properties.Version.Py3.Libs)...)
-
- if p.bootstrapper != nil && p.isEmbeddedLauncherEnabled(pyVersion3) {
- ctx.AddVariationDependencies(nil, 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()))
- }
-}
-
-// check "libs" duplicates from current module dependencies.
-func uniqueLibs(ctx android.BottomUpMutatorContext,
- commonLibs []string, versionProp string, versionLibs []string) []string {
- set := make(map[string]string)
- ret := []string{}
-
- // deps from "libs" property.
- for _, l := range commonLibs {
- if _, found := set[l]; found {
- ctx.PropertyErrorf("libs", "%q has duplicates within libs.", l)
- } else {
- set[l] = "libs"
- ret = append(ret, l)
- }
- }
- // deps from "version.pyX.libs" property.
- for _, l := range versionLibs {
- if _, found := set[l]; found {
- ctx.PropertyErrorf(versionProp, "%q has duplicates within %q.", set[l])
- } else {
- set[l] = versionProp
- ret = append(ret, l)
- }
+ ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
+ ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
+ ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
}
- return ret
+ // Emulate the data property for java_data but with the arch variation overridden to "common"
+ // so that it can point to java modules.
+ javaDataVariation := []blueprint.Variation{{"arch", android.Common.String()}}
+ ctx.AddVariationDependencies(javaDataVariation, javaDataTag, p.properties.Java_data...)
}
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
- if p.properties.Actual_version == pyVersion2 {
- embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion2)
- } else {
- embeddedLauncher = p.isEmbeddedLauncherEnabled(pyVersion3)
- }
+ // 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))
@@ -428,29 +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
- switch p.properties.Actual_version {
- case pyVersion2:
- srcs = append(srcs, p.properties.Version.Py2.Srcs...)
- exclude_srcs = append(exclude_srcs, p.properties.Version.Py2.Exclude_srcs...)
- case pyVersion3:
- srcs = append(srcs, p.properties.Version.Py3.Srcs...)
- exclude_srcs = append(exclude_srcs, p.properties.Version.Py3.Exclude_srcs...)
- default:
- panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
- p.properties.Actual_version, ctx.ModuleName()))
- }
- 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
@@ -462,9 +467,15 @@
// expand data files from "data" property.
expandedData := android.PathsForModuleSrc(ctx, p.properties.Data)
- // sanitize pkg_path.
+ // Emulate the data property for java_data dependencies.
+ for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+ expandedData = append(expandedData, android.OutputFilesForModule(ctx, javaData, "")...)
+ }
+
+ // 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, "/") {
@@ -473,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
@@ -502,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})
}
}
@@ -522,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 {
@@ -562,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)
}
}
@@ -588,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 {
@@ -609,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()
}
@@ -639,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
}
@@ -648,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 23db24e..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." +
@@ -329,13 +329,13 @@
for _, d := range data {
t.Run(d.desc, func(t *testing.T) {
config := android.TestConfig(buildDir, nil, "", d.mockFiles)
- ctx := android.NewTestContext()
+ ctx := android.NewTestContext(config)
ctx.PreDepsMutators(RegisterPythonPreDepsMutators)
ctx.RegisterModuleType("python_library_host", PythonLibraryHostFactory)
ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
ctx.RegisterModuleType("python_defaults", defaultsFactory)
ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- ctx.Register(config)
+ ctx.Register()
_, testErrs := ctx.ParseBlueprintsFiles(bpFile)
android.FailIfErrored(t, testErrs)
_, actErrs := ctx.PrepareBuildActions(config)
@@ -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))
}
}
@@ -395,10 +395,11 @@
if !reflect.DeepEqual(actualPyRunfiles, expectedPyRunfiles) {
testErrs = append(testErrs, errors.New(fmt.Sprintf(
- `binary "%s" variant "%s" has unexpected pyRunfiles: %q!`,
+ `binary "%s" variant "%s" has unexpected pyRunfiles: %q! (expected: %q)`,
base.Name(),
base.properties.Actual_version,
- actualPyRunfiles)))
+ actualPyRunfiles,
+ expectedPyRunfiles)))
}
if base.srcsZip.String() != strings.Replace(expectedSrcsZip, "@prefix@", buildDir, 1) {
diff --git a/python/test.go b/python/test.go
index 434e71a..b7cd475 100644
--- a/python/test.go
+++ b/python/test.go
@@ -26,6 +26,12 @@
android.RegisterModuleType("python_test", PythonTestFactory)
}
+// Test option struct.
+type TestOptions struct {
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
+}
+
type TestProperties struct {
// the name of the test configuration (for example "AndroidTest.xml") that should be
// installed with the module.
@@ -38,6 +44,12 @@
// list of files or filegroup modules that provide data that should be installed alongside
// the test
Data []string `android:"path,arch_variant"`
+
+ // list of java modules that provide data that should be installed alongside the test.
+ Java_data []string
+
+ // Test options.
+ Test_options TestOptions
}
type testDecorator struct {
@@ -71,6 +83,13 @@
for _, dataSrcPath := range dataSrcPaths {
test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
}
+
+ // Emulate the data property for java_data dependencies.
+ for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
+ for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
+ test.data = append(test.data, android.DataPath{SrcPath: javaDataSrcPath})
+ }
+ }
}
func NewTest(hod android.HostOrDeviceSupported) *Module {
@@ -89,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 29e4bd7..e9da6fa 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -15,10 +15,7 @@
package rust
import (
- "fmt"
- "io"
"path/filepath"
- "strings"
"android/soong/android"
)
@@ -26,14 +23,14 @@
type AndroidMkContext interface {
Name() string
Target() android.Target
- SubAndroidMk(*android.AndroidMkData, interface{})
+ SubAndroidMk(*android.AndroidMkEntries, interface{})
}
type SubAndroidMkProvider interface {
- AndroidMk(AndroidMkContext, *android.AndroidMkData)
+ AndroidMk(AndroidMkContext, *android.AndroidMkEntries)
}
-func (mod *Module) SubAndroidMk(data *android.AndroidMkData, obj interface{}) {
+func (mod *Module) SubAndroidMk(data *android.AndroidMkEntries, obj interface{}) {
if mod.subAndroidMkOnce == nil {
mod.subAndroidMkOnce = make(map[SubAndroidMkProvider]bool)
}
@@ -45,33 +42,23 @@
}
}
-func (mod *Module) AndroidMk() android.AndroidMkData {
- if mod.Properties.HideFromMake {
- return android.AndroidMkData{
- Disabled: true,
- }
+func (mod *Module) AndroidMkEntries() []android.AndroidMkEntries {
+ if mod.Properties.HideFromMake || mod.hideApexVariantFromMake {
+
+ return []android.AndroidMkEntries{android.AndroidMkEntries{Disabled: true}}
}
- ret := android.AndroidMkData{
+ ret := android.AndroidMkEntries{
OutputFile: mod.outputFile,
Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
- Extra: []android.AndroidMkExtraFunc{
- func(w io.Writer, outputFile android.Path) {
- if len(mod.Properties.AndroidMkRlibs) > 0 {
- fmt.Fprintln(w, "LOCAL_RLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkRlibs, " "))
- }
- if len(mod.Properties.AndroidMkDylibs) > 0 {
- fmt.Fprintln(w, "LOCAL_DYLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkDylibs, " "))
- }
- if len(mod.Properties.AndroidMkProcMacroLibs) > 0 {
- fmt.Fprintln(w, "LOCAL_PROC_MACRO_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkProcMacroLibs, " "))
- }
- if len(mod.Properties.AndroidMkSharedLibs) > 0 {
- fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkSharedLibs, " "))
- }
- if len(mod.Properties.AndroidMkStaticLibs) > 0 {
- fmt.Fprintln(w, "LOCAL_STATIC_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkStaticLibs, " "))
- }
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.AddStrings("LOCAL_RLIB_LIBRARIES", mod.Properties.AndroidMkRlibs...)
+ entries.AddStrings("LOCAL_DYLIB_LIBRARIES", mod.Properties.AndroidMkDylibs...)
+ 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)
},
},
}
@@ -84,10 +71,10 @@
}
ret.SubName += mod.Properties.SubName
- return ret
+ return []android.AndroidMkEntries{ret}
}
-func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, binary.baseCompiler)
if binary.distFile.Valid() {
@@ -95,32 +82,26 @@
}
ret.Class = "EXECUTABLES"
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if binary.coverageOutputZipFile.Valid() {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+binary.coverageOutputZipFile.String())
- }
+ ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.SetOptionalPath("LOCAL_PREBUILT_COVERAGE_ARCHIVE", binary.coverageOutputZipFile)
})
}
-func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (test *testDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
test.binaryDecorator.AndroidMk(ctx, ret)
ret.Class = "NATIVE_TESTS"
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if len(test.Properties.Test_suites) > 0 {
- fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
- strings.Join(test.Properties.Test_suites, " "))
- }
+ ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
if test.testConfig != nil {
- fmt.Fprintln(w, "LOCAL_FULL_TEST_CONFIG :=", test.testConfig.String())
+ entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
- if !BoolDefault(test.Properties.Auto_gen_config, true) {
- fmt.Fprintln(w, "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true")
- }
+ entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
+ entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
})
// TODO(chh): add test data with androidMkWriteTestData(test.data, ctx, ret)
}
-func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, library.baseCompiler)
if library.rlib() {
@@ -137,15 +118,12 @@
ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
}
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if library.coverageOutputZipFile.Valid() {
- fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE := "+library.coverageOutputZipFile.String())
- }
-
+ ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.SetOptionalPath("LOCAL_PREBUILT_COVERAGE_ARCHIVE", library.coverageOutputZipFile)
})
}
-func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, procMacro.baseCompiler)
ret.Class = "PROC_MACRO_LIBRARIES"
@@ -155,29 +133,29 @@
}
-func (sourceProvider *BaseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (sourceProvider *BaseSourceProvider) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
outFile := sourceProvider.OutputFiles[0]
ret.Class = "ETC"
ret.OutputFile = android.OptionalPathForPath(outFile)
ret.SubName += sourceProvider.subName
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
_, file := filepath.Split(outFile.String())
stem, suffix, _ := android.SplitFileExt(file)
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
- fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
- fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
+ entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
})
}
-func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (bindgen *bindgenDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, bindgen.BaseSourceProvider)
}
-func (proto *protobufDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (proto *protobufDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, proto.BaseSourceProvider)
}
-func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
if compiler.path == (android.InstallPath{}) {
return
}
@@ -191,14 +169,12 @@
unstrippedOutputFile = ret.OutputFile
ret.OutputFile = compiler.strippedOutputFile
}
- ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
- if compiler.strippedOutputFile.Valid() {
- fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", unstrippedOutputFile)
- }
+ ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ entries.SetOptionalPath("LOCAL_SOONG_UNSTRIPPED_BINARY", unstrippedOutputFile)
path, file := filepath.Split(compiler.path.ToMakePath().String())
stem, suffix, _ := android.SplitFileExt(file)
- fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
- fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
- fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+ entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
+ entries.SetString("LOCAL_MODULE_PATH", path)
+ entries.SetString("LOCAL_MODULE_STEM", stem)
})
}
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/bindgen.go b/rust/bindgen.go
index 3db2d65..35a807b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,10 +29,10 @@
defaultBindgenFlags = []string{""}
// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
- bindgenClangVersion = "clang-r383902c"
+ bindgenClangVersion = "clang-r399163b"
//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
- _ = pctx.SourcePathVariable("bindgenCmd", "out/host/${config.HostPrebuiltTag}/bin/bindgen")
+ _ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
_ = pctx.SourcePathVariable("bindgenClang",
"${cc_config.ClangBase}/${config.HostPrebuiltTag}/"+bindgenClangVersion+"/bin/clang")
_ = pctx.SourcePathVariable("bindgenLibClang",
@@ -258,5 +258,6 @@
deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
deps.StaticLibs = append(deps.StaticLibs, b.ClangProperties.Static_libs...)
+ deps.HeaderLibs = append(deps.StaticLibs, b.ClangProperties.Header_libs...)
return deps
}
diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go
index c7ce42b..af04cfc 100644
--- a/rust/bindgen_test.go
+++ b/rust/bindgen_test.go
@@ -32,6 +32,7 @@
cflags: ["--clang-flag()"],
shared_libs: ["libfoo_shared"],
static_libs: ["libfoo_static"],
+ header_libs: ["libfoo_header"],
}
cc_library_shared {
name: "libfoo_shared",
@@ -41,6 +42,10 @@
name: "libfoo_static",
export_include_dirs: ["static_include"],
}
+ cc_library_headers {
+ name: "libfoo_header",
+ export_include_dirs: ["header_include"],
+ }
cc_defaults {
name: "cc_defaults_flags",
cflags: ["--default-flag"],
@@ -60,6 +65,9 @@
if !strings.Contains(libbindgen.Args["cflags"], "-Istatic_include") {
t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
}
+ if !strings.Contains(libbindgen.Args["cflags"], "-Iheader_include") {
+ t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
+ }
if !strings.Contains(libbindgen.Args["cflags"], "--default-flag") {
t.Errorf("rust_bindgen missing cflags defined in cc_defaults: cflags %#v", libbindgen.Args["cflags"])
}
diff --git a/rust/builder.go b/rust/builder.go
index 654b1e6..8ec2da2 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -15,6 +15,7 @@
package rust
import (
+ "path/filepath"
"strings"
"github.com/google/blueprint"
@@ -22,6 +23,7 @@
"android/soong/android"
"android/soong/cc"
+ "android/soong/rust/config"
)
var (
@@ -138,6 +140,13 @@
crate_name := ctx.RustModule().CrateName()
targetTriple := ctx.toolchain().RustTriple()
+ // libstd requires a specific environment variable to be set. This is
+ // not officially documented and may be removed in the future. See
+ // https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866.
+ if crate_name == "std" {
+ envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType])
+ }
+
inputs = append(inputs, main)
// Collect rustc flags
@@ -180,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())
@@ -191,7 +201,9 @@
profileEmitArg := strings.TrimPrefix(cc.PwdPrefix(), "PWD=") + "/"
if outputFile.Ext() != "" {
- gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno"))
+ // rustc seems to split the output filename at the first '.' when determining the gcno filename
+ // so we need to do the same here.
+ gcnoFile = android.PathForModuleOut(ctx, strings.Split(outputFile.Base(), ".")[0]+".gcno")
rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcda")).String())
} else {
@@ -227,7 +239,18 @@
},
})
implicits = append(implicits, outputs.Paths()...)
- envVars = append(envVars, "OUT_DIR=$$PWD/"+moduleGenDir.String())
+
+ // We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
+ // assumes that paths are relative to the source file.
+ var outDirPrefix string
+ if !filepath.IsAbs(moduleGenDir.String()) {
+ // If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
+ outDirPrefix = "$$PWD/"
+ } else {
+ // If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
+ outDirPrefix = ""
+ }
+ envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
}
if flags.Clippy {
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index 7815aab..e24f666 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -18,6 +18,7 @@
"testing"
"android/soong/android"
+ "android/soong/cc"
)
func TestClippy(t *testing.T) {
@@ -45,6 +46,7 @@
}`
bp = bp + GatherRequiredDepsForTest()
+ bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
fs := map[string][]byte{
// Reuse the same blueprint file for subdirectories.
@@ -65,8 +67,8 @@
t.Run("path="+tc.modulePath, func(t *testing.T) {
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
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/compiler_test.go b/rust/compiler_test.go
index a25523c..2b40727 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -19,6 +19,7 @@
"testing"
"android/soong/android"
+ "android/soong/cc"
)
// Test that feature flags are being correctly generated.
@@ -132,6 +133,7 @@
}`
bp = bp + GatherRequiredDepsForTest()
+ bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
fs := map[string][]byte{
// Reuse the same blueprint file for subdirectories.
@@ -152,8 +154,8 @@
t.Run("path="+tc.modulePath, func(t *testing.T) {
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := CreateTestContext()
- ctx.Register(config)
+ ctx := CreateTestContext(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{tc.modulePath + "Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
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 3ad32fa..21df024 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -6,12 +6,15 @@
// for an example.
// TODO(b/160223496): enable rustfmt globally.
RustAllowedPaths = []string{
+ "device/google/cuttlefish",
"external/adhd",
"external/crosvm",
+ "external/libchromeos-rs",
"external/minijail",
"external/rust",
"external/vm_tools/p9",
"frameworks/native/libs/binder/rust",
+ "packages/modules/DnsResolver",
"packages/modules/Virtualization",
"prebuilts/rust",
"system/bt",
diff --git a/rust/config/global.go b/rust/config/global.go
index 71c4240..22d9567 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
var pctx = android.NewPackageContext("android/soong/rust/config")
var (
- RustDefaultVersion = "1.46.0"
+ RustDefaultVersion = "1.48.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2018"
Stdlibs = []string{
@@ -32,6 +32,15 @@
"libtest",
}
+ // Mapping between Soong internal arch types and std::env constants.
+ // Required as Rust uses aarch64 when Soong uses arm64.
+ StdEnvArch = map[android.ArchType]string{
+ android.Arm: "arm",
+ android.Arm64: "aarch64",
+ android.X86: "x86",
+ android.X86_64: "x86_64",
+ }
+
GlobalRustFlags = []string{
"--remap-path-prefix $$(pwd)=",
"-C codegen-units=1",
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/coverage_test.go b/rust/coverage_test.go
index 90155ca..e7f873e 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -160,7 +160,7 @@
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", fizzZipInputs)
}
if !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_rlib_dylib-std_cov/librlib.gcno") ||
- !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.dylib.gcno") {
+ !android.SuffixInList(libfooZipInputs, "android_arm64_armv8-a_dylib_cov/libfoo.gcno") {
t.Fatalf("missing expected coverage files for rust 'fizz' binary: %#v", libfooZipInputs)
}
if !android.SuffixInList(buzzZipInputs, "android_arm64_armv8-a_cov/obj/foo.gcno") ||
diff --git a/rust/image.go b/rust/image.go
new file mode 100644
index 0000000..af8c3b2
--- /dev/null
+++ b/rust/image.go
@@ -0,0 +1,153 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+ "strings"
+
+ "android/soong/android"
+ "android/soong/cc"
+)
+
+var _ android.ImageInterface = (*Module)(nil)
+
+func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+ return false
+}
+
+func (mod *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+ return mod.Properties.CoreVariantNeeded
+}
+
+func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
+ return mod.InRamdisk()
+}
+
+func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
+ return mod.InRecovery()
+}
+
+func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
+ return mod.Properties.ExtraVariants
+}
+
+func (ctx *moduleContext) ProductSpecific() bool {
+ return false
+}
+
+func (mod *Module) InRecovery() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInRamdisk() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInRecovery() bool {
+ // TODO(b/165791368)
+ return false
+}
+
+func (mod *Module) OnlyInVendorRamdisk() bool {
+ return false
+}
+
+// Returns true when this module is configured to have core and vendor variants.
+func (mod *Module) HasVendorVariant() bool {
+ return mod.IsVndk() || Bool(mod.VendorProperties.Vendor_available)
+}
+
+func (c *Module) VendorAvailable() bool {
+ return Bool(c.VendorProperties.Vendor_available)
+}
+
+func (c *Module) InProduct() bool {
+ return false
+}
+
+func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+ m := module.(*Module)
+ if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
+ m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
+ m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
+
+ // Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
+ // Hide other vendor variants to avoid collision.
+ vndkVersion := ctx.DeviceConfig().VndkVersion()
+ if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
+ m.Properties.HideFromMake = true
+ m.HideFromMake()
+ }
+ }
+}
+
+func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+ vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+ platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+
+ // Rust does not support installing to the product image yet.
+ if 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 ae33f0f..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)
@@ -428,7 +431,9 @@
var srcPath android.Path
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)
}
@@ -600,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.
@@ -636,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 5697408..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,90 +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)
+ // Skip intra-module dependencies (i.e., generated-source library depending on the source variant).
+ if module.Name() == child.Name() {
+ return
+ }
+ // Skip unsupported modules.
+ rChild, compChild, ok := isModuleSupported(ctx, child)
if !ok {
return
}
- if _, ok = deps[ctx.ModuleName(child)]; ok {
+ // 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: childCrateName})
- deps[ctx.ModuleName(child)] = childId
+ 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) {
@@ -168,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 69288fc..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"])
@@ -131,6 +182,27 @@
t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
rootModule, android.BuildOs.String())
}
+ // Check that libbindings1 does not depend on itself.
+ if strings.Contains(rootModule, "libbindings1") {
+ 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)
+ }
+ }
}
}
@@ -155,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 ebb1c3c..b91fea8 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -15,6 +15,9 @@
package rust
import (
+ "fmt"
+ "strings"
+
"android/soong/android"
)
@@ -22,6 +25,12 @@
defaultProtobufFlags = []string{""}
)
+const (
+ grpcSuffix = "_grpc"
+)
+
+type PluginType int
+
func init() {
android.RegisterModuleType("rust_protobuf", RustProtobufFactory)
android.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory)
@@ -30,53 +39,159 @@
var _ SourceProvider = (*protobufDecorator)(nil)
type ProtobufProperties struct {
- // Path to the proto file that will be used to generate the source
- Proto *string `android:"path,arch_variant"`
+ // 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"`
+
+ // List of libraries which export include paths required for this module
+ Header_libs []string `android:"arch_variant,variant_prepend"`
}
type protobufDecorator struct {
*BaseSourceProvider
Properties ProtobufProperties
+ protoNames []string
+ grpcNames []string
+
+ grpcProtoFlags android.ProtoFlags
+ protoFlags android.ProtoFlags
}
func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
var protoFlags android.ProtoFlags
- pluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
-
- protoFlags.OutTypeFlag = "--rust_out"
-
- protoFlags.Flags = append(protoFlags.Flags, " --plugin="+pluginPath.String())
- protoFlags.Flags = append(protoFlags.Flags, defaultProtobufFlags...)
- protoFlags.Flags = append(protoFlags.Flags, proto.Properties.Proto_flags...)
-
- protoFlags.Deps = append(protoFlags.Deps, pluginPath)
-
- protoFile := android.OptionalPathForModuleSrc(ctx, proto.Properties.Proto)
- if !protoFile.Valid() {
- ctx.PropertyErrorf("proto", "invalid path to proto file")
- }
+ var grpcProtoFlags android.ProtoFlags
+ var commonProtoFlags []string
outDir := android.PathForModuleOut(ctx)
+ 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)
- // rust protobuf-codegen output <stem>.rs
- stemFile := android.PathForModuleOut(ctx, stem+".rs")
- // add mod_<stem>.rs to import <stem>.rs
- modFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
- // mod_<stem>.rs is the main/first output file to be included/compiled
- outputs := android.WritablePaths{modFile, stemFile}
- depFile := android.PathForModuleOut(ctx, "mod_"+stem+".d")
- rule := android.NewRuleBuilder()
- android.ProtoRule(ctx, rule, protoFile.Path(), protoFlags, protoFlags.Deps, outDir, depFile, outputs)
- rule.Command().Text("printf '// @generated\\npub mod %s;\\n' '" + stem + "' >").Output(modFile)
- rule.Build(pctx, ctx, "protoc_"+protoFile.Path().Rel(), "protoc "+protoFile.Path().Rel())
+ // The mod_stem.rs file is used to avoid collisions if this is not included as a crate.
+ stemFile := android.PathForModuleOut(ctx, "mod_"+stem+".rs")
- proto.BaseSourceProvider.OutputFiles = android.Paths{modFile, stemFile}
- return modFile
+ // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
+ var outputs android.WritablePaths
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ for _, protoFile := range protoFiles {
+ // Since we're iterating over the protoFiles already, make sure they're not redeclared in grpcFiles
+ if android.InList(protoFile.String(), grpcFiles.Strings()) {
+ ctx.PropertyErrorf("protos",
+ "A proto can only be added once to either grpc_protos or protos. %q is declared in both properties",
+ protoFile.String())
+ }
+
+ protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
+ proto.protoNames = append(proto.protoNames, protoName)
+
+ protoOut := android.PathForModuleOut(ctx, protoName+".rs")
+ depFile := android.PathForModuleOut(ctx, protoName+".d")
+
+ ruleOutputs := android.WritablePaths{protoOut, depFile}
+
+ android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
+ outputs = append(outputs, ruleOutputs...)
+ }
+
+ for _, grpcFile := range grpcFiles {
+ grpcName := strings.TrimSuffix(grpcFile.Base(), ".proto")
+ proto.grpcNames = append(proto.grpcNames, grpcName)
+
+ // GRPC protos produce two files, a proto.rs and a proto_grpc.rs
+ protoOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+".rs"))
+ grpcOut := android.WritablePath(android.PathForModuleOut(ctx, grpcName+grpcSuffix+".rs"))
+ depFile := android.PathForModuleOut(ctx, grpcName+".d")
+
+ ruleOutputs := android.WritablePaths{protoOut, grpcOut, depFile}
+
+ android.ProtoRule(rule, grpcFile, grpcProtoFlags, grpcProtoFlags.Deps, outDir, depFile, ruleOutputs)
+ outputs = append(outputs, ruleOutputs...)
+ }
+
+ // Check that all proto base filenames are unique as outputs are written to the same directory.
+ baseFilenames := append(proto.protoNames, proto.grpcNames...)
+ if len(baseFilenames) != len(android.FirstUniqueStrings(baseFilenames)) {
+ ctx.PropertyErrorf("protos", "proto filenames must be unique across 'protos' and 'grpc_protos' "+
+ "to be used in the same rust_protobuf module. For example, foo.proto and src/foo.proto will conflict.")
+ }
+
+ android.WriteFileRule(ctx, stemFile, proto.genModFileContents())
+
+ rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
+
+ // 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() string {
+ lines := []string{
+ "// @Soong generated Source",
+ }
+ for _, protoName := range proto.protoNames {
+ lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
+ }
+
+ 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 {",
+ " pub use protobuf::well_known_types::Empty;",
+ "}")
+ }
+
+ return strings.Join(lines, "\n")
}
func (proto *protobufDecorator) SourceProviderProps() []interface{} {
@@ -86,6 +201,13 @@
func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
+ deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
+
+ if len(proto.Properties.Grpc_protos) > 0 {
+ deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
+ deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
+ }
+
return deps
}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index bd11a5a..1ac66f3 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -15,25 +15,122 @@
package rust
import (
- "android/soong/android"
+ "strings"
"testing"
+
+ "android/soong/android"
)
func TestRustProtobuf(t *testing.T) {
ctx := testRust(t, `
rust_protobuf {
name: "librust_proto",
- proto: "buf.proto",
+ protos: ["buf.proto", "proto.proto"],
crate_name: "rust_proto",
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 there's a rule to generate the expected output
- _ = ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
-
// Check that libprotobuf is added as a dependency.
librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
if !android.InList("libprotobuf", librust_proto.Properties.AndroidMkDylibs) {
t.Errorf("libprotobuf dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
}
+
+ // Make sure the correct plugin is being used.
+ librust_proto_out := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
+ cmd := librust_proto_out.RuleParams.Command
+ if w := "protoc-gen-rust"; !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 proto.rs, the second protobuf, is listed as an output
+ librust_proto_outputs := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").AllOutputs()
+ if android.InList("proto.rs", librust_proto_outputs) {
+ t.Errorf("rust_protobuf is not producing multiple outputs; expected 'proto.rs' in list, got: %#v ",
+ librust_proto_outputs)
+ }
+}
+
+func TestRustGrpc(t *testing.T) {
+ ctx := testRust(t, `
+ rust_protobuf {
+ name: "librust_grpcio",
+ protos: ["buf.proto"],
+ grpc_protos: ["foo.proto", "proto.proto"],
+ crate_name: "rust_grpcio",
+ source_stem: "buf",
+ }
+ `)
+
+ // Check that libprotobuf is added as a dependency.
+ librust_grpcio_module := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_dylib").Module().(*Module)
+
+ // Check that libgrpcio is added as a dependency.
+ if !android.InList("libgrpcio", librust_grpcio_module.Properties.AndroidMkDylibs) {
+ t.Errorf("libgrpcio dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
+ }
+
+ // Check that libfutures is added as a dependency.
+ if !android.InList("libfutures", librust_grpcio_module.Properties.AndroidMkDylibs) {
+ t.Errorf("libfutures dependency missing for rust_grpcio (dependency missing from AndroidMkDylibs)")
+ }
+
+ // Make sure the correct plugin is being used.
+ 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 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)
+ }
+
+ // Check proto.rs, the second protobuf, is listed as an output
+ librust_grpcio_outputs := ctx.ModuleForTests("librust_grpcio", "android_arm64_armv8-a_source").AllOutputs()
+ if android.InList("proto_grpc.rs", librust_grpcio_outputs) {
+ t.Errorf("rust_protobuf is not producing multiple outputs; expected 'proto_grpc.rs' in list, got: %#v ",
+ 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 e65b90e..21da5ae 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
@@ -74,12 +83,17 @@
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
+ android.ApexModuleBase
+
+ VendorProperties cc.VendorProperties
Properties BaseProperties
hod android.HostOrDeviceSupported
multilib android.Multilib
+ makeLinkType string
+
compiler compiler
coverage *coverage
clippy *clippy
@@ -88,6 +102,8 @@
subAndroidMkOnce map[SubAndroidMkProvider]bool
outputFile android.OptionalPath
+
+ hideApexVariantFromMake bool
}
func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -106,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 ""
}
@@ -173,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 {
@@ -198,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
}
@@ -229,6 +217,7 @@
ProcMacros []string
SharedLibs []string
StaticLibs []string
+ HeaderLibs []string
CrtBegin, CrtEnd string
}
@@ -256,7 +245,8 @@
CrtEnd android.OptionalPath
// Paths to generated source files
- SrcDeps android.Paths
+ SrcDeps android.Paths
+ srcProviderFiles android.Paths
}
type RustLibraries []RustLibrary
@@ -372,6 +362,7 @@
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
+ &cc.VendorProperties{},
&BindgenProperties{},
&BaseCompilerProperties{},
&BinaryCompilerProperties{},
@@ -468,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() {
@@ -492,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()...)
@@ -507,6 +494,7 @@
}
android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
+ android.InitApexModule(mod)
android.InitDefaultableModule(mod)
return mod
@@ -604,7 +592,18 @@
ModuleContext: actx,
}
+ apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if !apexInfo.IsForPlatform() {
+ mod.hideApexVariantFromMake = true
+ }
+
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.
@@ -684,6 +683,14 @@
proc_macro bool
}
+// InstallDepNeeded returns true for rlibs, dylibs, and proc macros so that they or their transitive
+// dependencies (especially C/C++ shared libs) are installed as dependencies of a rust binary.
+func (d dependencyTag) InstallDepNeeded() bool {
+ return d.library || d.proc_macro
+}
+
+var _ android.InstallNeededDependencyTag = dependencyTag{}
+
var (
customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
rlibDepTag = dependencyTag{name: "rlibTag", library: true}
@@ -693,6 +700,11 @@
sourceDepTag = dependencyTag{name: "source"}
)
+func IsDylibDepTag(depTag blueprint.DependencyTag) bool {
+ tag, ok := depTag.(dependencyTag)
+ return ok && tag == dylibDepTag
+}
+
type autoDep struct {
variation string
depTag dependencyTag
@@ -838,6 +850,11 @@
directSharedLibDeps = append(directSharedLibDeps, ccDep)
mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
exportDep = true
+ case cc.IsHeaderDepTag(depTag):
+ exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
+ depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
+ depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
+ depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
case depTag == cc.CrtBeginDepTag:
depPaths.CrtBegin = linkObject
case depTag == cc.CrtEndDepTag:
@@ -928,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 {
@@ -983,6 +996,8 @@
blueprint.Variation{Mutator: "link", Variation: "static"}),
cc.StaticDepTag(), deps.StaticLibs...)
+ actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
+
crtVariations := cc.GetCrtVariations(ctx, mod)
if deps.CrtBegin != "" {
actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin)
@@ -1044,6 +1059,84 @@
return android.OptionalPath{}
}
+var _ android.ApexModule = (*Module)(nil)
+
+func (mod *Module) minSdkVersion() string {
+ return String(mod.Properties.Min_sdk_version)
+}
+
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
+func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+ minSdkVersion := mod.minSdkVersion()
+ if minSdkVersion == "apex_inherit" {
+ return nil
+ }
+ if minSdkVersion == "" {
+ return fmt.Errorf("min_sdk_version is not specificed")
+ }
+
+ // Not using nativeApiLevelFromUser because the context here is not
+ // necessarily a native context.
+ ver, err := android.ApiLevelFromUser(ctx, minSdkVersion)
+ if err != nil {
+ return err
+ }
+
+ if ver.GreaterThan(sdkVersion) {
+ return fmt.Errorf("newer SDK(%v)", ver)
+ }
+ return nil
+}
+
+// Implements android.ApexModule
+func (mod *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ depTag := ctx.OtherModuleDependencyTag(dep)
+
+ if ccm, ok := dep.(*cc.Module); ok {
+ if ccm.HasStubsVariants() {
+ if cc.IsSharedDepTag(depTag) {
+ // dynamic dep to a stubs lib crosses APEX boundary
+ return false
+ }
+ if cc.IsRuntimeDepTag(depTag) {
+ // runtime dep to a stubs lib also crosses APEX boundary
+ return false
+ }
+
+ if cc.IsHeaderDepTag(depTag) {
+ return false
+ }
+ }
+ if mod.Static() && cc.IsSharedDepTag(depTag) {
+ // shared_lib dependency from a static lib is considered as crossing
+ // the APEX boundary because the dependency doesn't actually is
+ // linked; the dependency is used only during the compilation phase.
+ return false
+ }
+ }
+
+ if depTag == procMacroDepTag {
+ return false
+ }
+
+ return true
+}
+
+// Overrides ApexModule.IsInstallabeToApex()
+func (mod *Module) IsInstallableToApex() bool {
+ if mod.compiler != nil {
+ if lib, ok := mod.compiler.(*libraryDecorator); ok && (lib.shared() || lib.dylib()) {
+ return true
+ }
+ if _, ok := mod.compiler.(*binaryDecorator); ok {
+ return true
+ }
+ }
+ return false
+}
+
var Bool = proptools.Bool
var BoolDefault = proptools.BoolDefault
var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 9b2f023..48c8d74 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -106,13 +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,
- "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,
}
}
@@ -120,6 +123,7 @@
// attributes of the testRustCtx.
func (tctx *testRustCtx) generateConfig() {
tctx.bp = tctx.bp + GatherRequiredDepsForTest()
+ tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
cc.GatherRequiredFilesForTest(tctx.fs)
config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
tctx.config = &config
@@ -142,8 +146,8 @@
if tctx.config == nil {
t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
}
- ctx := CreateTestContext()
- ctx.Register(*tctx.config)
+ ctx := CreateTestContext(*tctx.config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(*tctx.config)
@@ -157,8 +161,8 @@
if tctx.config == nil {
t.Fatalf("tctx.config not been generated yet. Please call generateConfig first.")
}
- ctx := CreateTestContext()
- ctx.Register(*tctx.config)
+ ctx := CreateTestContext(*tctx.config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
if len(errs) > 0 {
@@ -286,6 +290,12 @@
srcs: ["src/any.h"],
out: ["src/any.rs"],
}
+ rust_binary_host {
+ name: "any_rust_binary",
+ srcs: [
+ "foo.rs",
+ ],
+ }
rust_bindgen {
name: "libbindings",
crate_name: "bindings",
diff --git a/rust/source_provider.go b/rust/source_provider.go
index 436518c..7719611 100644
--- a/rust/source_provider.go
+++ b/rust/source_provider.go
@@ -30,6 +30,7 @@
type BaseSourceProvider struct {
Properties SourceProviderProperties
+ // The first file in OutputFiles must be the library entry point.
OutputFiles android.Paths
subAndroidMkOnce map[SubAndroidMkProvider]bool
subName string
diff --git a/rust/strip.go b/rust/strip.go
index d1bbba6..110e3cc 100644
--- a/rust/strip.go
+++ b/rust/strip.go
@@ -19,11 +19,14 @@
"android/soong/cc"
)
-// Stripper encapsulates cc.Stripper.
+// Stripper defines the stripping actions and properties for a module. The Rust
+// implementation reuses the C++ implementation.
type Stripper struct {
cc.Stripper
}
+// StripExecutableOrSharedLib strips a binary or shared library from its debug
+// symbols and other debug information.
func (s *Stripper) StripExecutableOrSharedLib(ctx ModuleContext, in android.Path, out android.ModuleOutPath) {
ccFlags := cc.StripFlags{Toolchain: ctx.RustModule().ccToolchain(ctx)}
s.Stripper.StripExecutableOrSharedLib(ctx, in, out, ccFlags)
diff --git a/rust/test.go b/rust/test.go
index bc7f53c..408e03a 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -19,6 +19,12 @@
"android/soong/tradefed"
)
+// Test option struct.
+type TestOptions struct {
+ // If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+ Unit_test *bool
+}
+
type TestProperties struct {
// Disables the creation of a test-specific directory when used with
// relative_install_path. Useful if several tests need to be in the same
@@ -44,6 +50,9 @@
// if set, build with the standard Rust test harness. Defaults to true.
Test_harness *bool
+
+ // Test options.
+ Test_options TestOptions
}
// A test module is a binary module with extra --test compiler flag
diff --git a/rust/testing.go b/rust/testing.go
index 42b0da1..07f557a 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -17,7 +17,6 @@
import (
"android/soong/android"
"android/soong/cc"
- "android/soong/genrule"
)
func GatherRequiredDepsForTest() string {
@@ -78,6 +77,15 @@
no_libcrt: true,
nocrt: true,
system_shared_libs: [],
+ apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+ min_sdk_version: "29",
+ }
+ cc_library {
+ name: "libprotobuf-cpp-full",
+ no_libcrt: true,
+ nocrt: true,
+ system_shared_libs: [],
+ export_include_dirs: ["libprotobuf-cpp-full-includes"],
}
rust_library {
name: "libstd",
@@ -85,8 +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",
@@ -94,8 +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",
@@ -103,17 +117,24 @@
srcs: ["foo.rs"],
host_supported: true,
}
+ rust_library {
+ name: "libgrpcio",
+ crate_name: "grpcio",
+ srcs: ["foo.rs"],
+ host_supported: true,
+ }
+ rust_library {
+ name: "libfutures",
+ crate_name: "futures",
+ srcs: ["foo.rs"],
+ host_supported: true,
+ }
-` + cc.GatherRequiredDepsForTest(android.NoOsType)
+`
return bp
}
-func CreateTestContext() *android.TestContext {
- ctx := android.NewTestArchContext()
- android.RegisterPrebuiltMutators(ctx)
- ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
- cc.RegisterRequiredBuildComponentsForTest(ctx)
- ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
@@ -145,6 +166,14 @@
ctx.BottomUp("rust_begin", BeginMutator).Parallel()
})
ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+}
+
+func CreateTestContext(config android.Config) *android.TestContext {
+ ctx := android.NewTestArchContext(config)
+ android.RegisterPrebuiltMutators(ctx)
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ cc.RegisterRequiredBuildComponentsForTest(ctx)
+ RegisterRequiredBuildComponentsForTest(ctx)
return ctx
}
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index ef5565d..4b08ac3 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -12,7 +12,7 @@
export OUT_DIR=${OUT_DIR:-out}
-if [ -e ${OUT_DIR}/soong/.soong.in_make ]; then
+if [ -e ${OUT_DIR}/soong/.soong.kati_enabled ]; then
# If ${OUT_DIR} has been created without --skip-make, Soong will create an
# ${OUT_DIR}/soong/build.ninja that leaves out many targets which are
# expected to be supplied by the .mk files, and that might cause errors in
@@ -32,11 +32,19 @@
my_get_build_var() {
# get_build_var will run Soong in normal in-make mode where it creates
- # .soong.in_make. That would clobber our real out directory, so we need to
- # run it in a different one.
+ # .soong.kati_enabled. That would clobber our real out directory, so we need
+ # to run it in a different one.
OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@"
}
+readonly SOONG_OUT=${OUT_DIR}/soong
+mkdir -p ${SOONG_OUT}
+
+# Some Soong build rules may require this, and the failure mode if it's missing
+# is confusing (b/172548608).
+readonly BUILD_NUMBER="$(my_get_build_var BUILD_NUMBER)"
+echo -n ${BUILD_NUMBER} > ${SOONG_OUT}/build_number.txt
+
readonly PLATFORM_SDK_VERSION="$(my_get_build_var PLATFORM_SDK_VERSION)"
readonly PLATFORM_VERSION="$(my_get_build_var PLATFORM_VERSION)"
PLATFORM_VERSION_ALL_CODENAMES="$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)"
@@ -46,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
@@ -61,8 +74,6 @@
USE_GOMA=false
fi
-readonly SOONG_OUT=${OUT_DIR}/soong
-mkdir -p ${SOONG_OUT}
readonly SOONG_VARS=${SOONG_OUT}/soong.variables
# Aml_abis: true
@@ -71,8 +82,14 @@
# CrossHost: linux_bionic
# CrossHostArch: x86_64
# - Enable Bionic on host as ART needs prebuilts for it.
+# VendorVars.art_mdoule.source_build
+# - TODO(b/172480615): Change default to false when platform uses ART Module
+# prebuilts by default.
cat > ${SOONG_VARS}.new << EOF
{
+ "BuildNumberFile": "build_number.txt",
+
+ "Platform_version_name": "${PLATFORM_VERSION}",
"Platform_sdk_version": ${PLATFORM_SDK_VERSION},
"Platform_sdk_codename": "${PLATFORM_VERSION}",
"Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
@@ -86,7 +103,15 @@
"Allow_missing_dependencies": ${SOONG_ALLOW_MISSING_DEPENDENCIES:-false},
"Unbundled_build": ${TARGET_BUILD_UNBUNDLED:-false},
- "UseGoma": ${USE_GOMA}
+ "UseGoma": ${USE_GOMA},
+
+ "VendorVars": {
+ "art_module": {
+ "source_build": "${ENABLE_ART_SOURCE_BUILD:-true}"
+ }
+ },
+
+ "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
}
EOF
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index b8dd7aa..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/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
index cf4ef27..c271211 100755
--- a/scripts/check_boot_jars/check_boot_jars.py
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -3,13 +3,14 @@
"""
Check boot jars.
-Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
+Usage: check_boot_jars.py <dexdump_path> <package_allow_list_file> <jar1> <jar2> ...
"""
import logging
import os.path
import re
import subprocess
import sys
+import xml.etree.ElementTree
# The compiled allow list RE.
@@ -38,46 +39,57 @@
return False
return True
-
-def CheckJar(allow_list_path, jar):
- """Check a jar file.
+def CheckDexJar(dexdump_path, allow_list_path, jar):
+ """Check a dex jar file.
"""
- # Get the list of files inside the jar file.
- p = subprocess.Popen(args='jar tf %s' % jar,
+ # Use dexdump to generate the XML representation of the dex jar file.
+ p = subprocess.Popen(args='%s -l xml %s' % (dexdump_path, jar),
stdout=subprocess.PIPE, shell=True)
stdout, _ = p.communicate()
if p.returncode != 0:
return False
- items = stdout.split()
- classes = 0
- for f in items:
- if f.endswith('.class'):
- classes += 1
- package_name = os.path.dirname(f)
- package_name = package_name.replace('/', '.')
- if not package_name or not allow_list_re.match(package_name):
- print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
- ' not in the allow list %s of packages allowed on the bootclasspath.'
- % (jar, f, package_name, allow_list_path))
- return False
- if classes == 0:
- print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
+
+ packages = 0
+ try:
+ # TODO(b/172063475) - improve performance
+ root = xml.etree.ElementTree.fromstring(stdout)
+ except xml.etree.ElementTree.ParseError as e:
+ print >> sys.stderr, 'Error processing jar %s - %s' % (jar, e)
+ print >> sys.stderr, stdout
+ return False
+ for package_elt in root.iterfind('package'):
+ packages += 1
+ package_name = package_elt.get('name')
+ if not package_name or not allow_list_re.match(package_name):
+ # Report the name of a class in the package as it is easier to navigate to
+ # the source of a concrete class than to a package which is often required
+ # to investigate this failure.
+ class_name = package_elt[0].get('name')
+ if package_name != "":
+ class_name = package_name + "." + class_name
+ print >> sys.stderr, ('Error: %s contains class file %s, whose package name "%s" is empty or'
+ ' not in the allow list %s of packages allowed on the bootclasspath.'
+ % (jar, class_name, package_name, allow_list_path))
+ return False
+ if packages == 0:
+ print >> sys.stderr, ('Error: %s does not contain any packages.' % jar)
return False
return True
def main(argv):
- if len(argv) < 2:
+ if len(argv) < 3:
print __doc__
return 1
- allow_list_path = argv[0]
+ dexdump_path = argv[0]
+ allow_list_path = argv[1]
if not LoadAllowList(allow_list_path):
return 1
passed = True
- for jar in argv[1:]:
- if not CheckJar(allow_list_path, jar):
+ for jar in argv[2:]:
+ if not CheckDexJar(dexdump_path, allow_list_path, jar):
passed = False
if not passed:
return 1
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/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 84e4f28..b1eebe9 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -109,6 +109,7 @@
name: "mysdk_sdkmember@current",
sdk_member_name: "sdkmember",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
installable: false,
stl: "none",
@@ -131,6 +132,7 @@
name: "sdkmember",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
stl: "none",
compile_multilib: "64",
@@ -353,6 +355,7 @@
name: "mysdk_crtobj@current",
sdk_member_name: "crtobj",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
sanitize: {
@@ -372,6 +375,7 @@
name: "crtobj",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
sanitize: {
@@ -480,6 +484,7 @@
name: "mysdk_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
stl: "none",
compile_multilib: "both",
@@ -511,6 +516,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
export_include_dirs: ["include/include"],
@@ -575,6 +581,7 @@
name: "mymodule_exports_mynativebinary@current",
sdk_member_name: "mynativebinary",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
arch: {
@@ -591,6 +598,7 @@
name: "mynativebinary",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
arch: {
arm64: {
@@ -654,6 +662,7 @@
name: "myexports_mynativebinary@current",
sdk_member_name: "mynativebinary",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -687,6 +696,7 @@
name: "mynativebinary",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -802,6 +812,7 @@
name: "myexports_mynativebinary@current",
sdk_member_name: "mynativebinary",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -822,6 +833,7 @@
name: "mynativebinary",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -841,6 +853,7 @@
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -861,6 +874,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -933,6 +947,7 @@
name: "mymodule_exports_linker@current",
sdk_member_name: "linker",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -959,6 +974,7 @@
name: "linker",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -1167,6 +1183,7 @@
name: "mysdk_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
stl: "none",
compile_multilib: "both",
@@ -1188,6 +1205,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
shared_libs: [
@@ -1208,6 +1226,7 @@
name: "mysdk_myothernativelib@current",
sdk_member_name: "myothernativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
stl: "none",
compile_multilib: "both",
@@ -1226,6 +1245,7 @@
name: "myothernativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
system_shared_libs: ["libm"],
@@ -1243,6 +1263,7 @@
name: "mysdk_mysystemnativelib@current",
sdk_member_name: "mysystemnativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
stl: "none",
compile_multilib: "both",
@@ -1260,6 +1281,7 @@
name: "mysystemnativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
arch: {
@@ -1327,6 +1349,7 @@
name: "mysdk_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -1355,6 +1378,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
sdk_version: "minimum",
@@ -1449,6 +1473,7 @@
name: "mysdk_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -1482,6 +1507,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -1572,6 +1598,7 @@
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
stl: "none",
compile_multilib: "both",
@@ -1592,6 +1619,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
export_include_dirs: ["include/include"],
@@ -1660,6 +1688,7 @@
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -1687,6 +1716,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -1769,6 +1799,7 @@
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
recovery_available: true,
vendor_available: true,
@@ -1799,6 +1830,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
recovery_available: true,
vendor_available: true,
stl: "none",
@@ -1877,6 +1909,7 @@
name: "myexports_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
installable: false,
@@ -1899,6 +1932,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -1964,6 +1998,7 @@
name: "mysdk_mynativeheaders@current",
sdk_member_name: "mynativeheaders",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
export_include_dirs: ["include/include"],
@@ -1973,6 +2008,7 @@
name: "mynativeheaders",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
stl: "none",
compile_multilib: "both",
export_include_dirs: ["include/include"],
@@ -2016,6 +2052,7 @@
name: "mysdk_mynativeheaders@current",
sdk_member_name: "mynativeheaders",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -2038,6 +2075,7 @@
name: "mynativeheaders",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
stl: "none",
@@ -2113,6 +2151,7 @@
name: "mysdk_mynativeheaders@current",
sdk_member_name: "mynativeheaders",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
stl: "none",
compile_multilib: "both",
@@ -2140,6 +2179,7 @@
name: "mynativeheaders",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
stl: "none",
compile_multilib: "both",
@@ -2220,6 +2260,7 @@
name: "mysdk_sslnil@current",
sdk_member_name: "sslnil",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
arch: {
@@ -2236,6 +2277,7 @@
name: "sslnil",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
arch: {
arm64: {
@@ -2251,6 +2293,7 @@
name: "mysdk_sslempty@current",
sdk_member_name: "sslempty",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
system_shared_libs: [],
@@ -2268,6 +2311,7 @@
name: "sslempty",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
system_shared_libs: [],
arch: {
@@ -2284,6 +2328,7 @@
name: "mysdk_sslnonempty@current",
sdk_member_name: "sslnonempty",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
system_shared_libs: ["mysdk_sslnil@current"],
@@ -2301,6 +2346,7 @@
name: "sslnonempty",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
system_shared_libs: ["sslnil"],
arch: {
@@ -2350,6 +2396,7 @@
name: "mysdk_sslvariants@current",
sdk_member_name: "sslvariants",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
installable: false,
compile_multilib: "both",
@@ -2381,6 +2428,7 @@
name: "sslvariants",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
compile_multilib: "both",
target: {
@@ -2456,6 +2504,7 @@
name: "mysdk_stubslib@current",
sdk_member_name: "stubslib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
stubs: {
@@ -2479,6 +2528,7 @@
name: "stubslib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
stubs: {
versions: [
@@ -2537,6 +2587,7 @@
name: "mysdk_stubslib@current",
sdk_member_name: "stubslib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
installable: false,
compile_multilib: "both",
@@ -2572,6 +2623,7 @@
name: "stubslib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
compile_multilib: "both",
stubs: {
@@ -2645,6 +2697,7 @@
name: "mysdk_mylib@current",
sdk_member_name: "mylib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
installable: false,
unique_host_soname: true,
@@ -2674,6 +2727,7 @@
name: "mylib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
unique_host_soname: true,
compile_multilib: "both",
@@ -2755,6 +2809,7 @@
name: "mysdk_mynativelib@current",
sdk_member_name: "mynativelib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
installable: false,
compile_multilib: "both",
export_include_dirs: ["include/include"],
@@ -2772,6 +2827,7 @@
name: "mynativelib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
compile_multilib: "both",
export_include_dirs: ["include/include"],
arch: {
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
index aa1200f..1c59244 100644
--- a/sdk/exports_test.go
+++ b/sdk/exports_test.go
@@ -50,6 +50,7 @@
name: "myexports_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -57,6 +58,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index d6828c9..b44f66e 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -122,6 +122,7 @@
name: "mysdk_sdkmember@current",
sdk_member_name: "sdkmember",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/sdkmember.jar"],
}
@@ -129,6 +130,7 @@
name: "sdkmember",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/sdkmember.jar"],
}
@@ -247,6 +249,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -254,6 +257,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -302,6 +306,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
@@ -311,6 +316,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
@@ -357,6 +363,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
target: {
android: {
@@ -372,6 +379,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
target: {
android: {
@@ -426,6 +434,7 @@
name: "myexports_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -433,6 +442,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -481,6 +491,7 @@
name: "myexports_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
@@ -490,6 +501,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavalib.jar"],
@@ -535,6 +547,7 @@
name: "myexports_myjavatests@current",
sdk_member_name: "myjavatests",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavatests.jar"],
test_config: "java/myjavatests-AndroidTest.xml",
}
@@ -543,6 +556,7 @@
name: "myjavatests",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavatests.jar"],
test_config: "java/myjavatests-AndroidTest.xml",
}
@@ -588,6 +602,7 @@
name: "myexports_myjavatests@current",
sdk_member_name: "myjavatests",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavatests.jar"],
@@ -598,6 +613,7 @@
name: "myjavatests",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/myjavatests.jar"],
@@ -619,148 +635,6 @@
)
}
-func testSdkWithDroidstubs(t *testing.T, bp string) *testSdkResult {
- t.Helper()
-
- fs := map[string][]byte{
- "foo/bar/Foo.java": nil,
- "stubs-sources/foo/bar/Foo.java": nil,
- }
- return testSdkWithFs(t, bp, fs)
-}
-
-// Note: This test does not verify that a droidstubs can be referenced, either
-// directly or indirectly from an APEX as droidstubs can never be a part of an
-// apex.
-func TestBasicSdkWithDroidstubs(t *testing.T) {
- testSdkWithDroidstubs(t, `
- sdk {
- name: "mysdk",
- stubs_sources: ["mystub"],
- }
- sdk_snapshot {
- name: "mysdk@10",
- stubs_sources: ["mystub_mysdk@10"],
- }
- prebuilt_stubs_sources {
- name: "mystub_mysdk@10",
- sdk_member_name: "mystub",
- srcs: ["stubs-sources/foo/bar/Foo.java"],
- }
- droidstubs {
- name: "mystub",
- srcs: ["foo/bar/Foo.java"],
- sdk_version: "none",
- system_modules: "none",
- }
- java_library {
- name: "myjavalib",
- srcs: [":mystub"],
- sdk_version: "none",
- system_modules: "none",
- }
- `)
-}
-
-func TestSnapshotWithDroidstubs(t *testing.T) {
- result := testSdkWithDroidstubs(t, `
- module_exports {
- name: "myexports",
- stubs_sources: ["myjavaapistubs"],
- }
-
- droidstubs {
- name: "myjavaapistubs",
- srcs: ["foo/bar/Foo.java"],
- system_modules: "none",
- sdk_version: "none",
- }
- `)
-
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_stubs_sources {
- name: "myexports_myjavaapistubs@current",
- sdk_member_name: "myjavaapistubs",
- visibility: ["//visibility:public"],
- srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-prebuilt_stubs_sources {
- name: "myjavaapistubs",
- prefer: false,
- visibility: ["//visibility:public"],
- srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- stubs_sources: ["myexports_myjavaapistubs@current"],
-}
-
-`),
- checkAllCopyRules(""),
- checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
- )
-}
-
-func TestHostSnapshotWithDroidstubs(t *testing.T) {
- result := testSdkWithDroidstubs(t, `
- module_exports {
- name: "myexports",
- device_supported: false,
- host_supported: true,
- stubs_sources: ["myjavaapistubs"],
- }
-
- droidstubs {
- name: "myjavaapistubs",
- device_supported: false,
- host_supported: true,
- srcs: ["foo/bar/Foo.java"],
- system_modules: "none",
- sdk_version: "none",
- }
- `)
-
- result.CheckSnapshot("myexports", "",
- checkAndroidBpContents(`
-// This is auto-generated. DO NOT EDIT.
-
-prebuilt_stubs_sources {
- name: "myexports_myjavaapistubs@current",
- sdk_member_name: "myjavaapistubs",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-prebuilt_stubs_sources {
- name: "myjavaapistubs",
- prefer: false,
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- srcs: ["java/myjavaapistubs_stubs_sources"],
-}
-
-module_exports_snapshot {
- name: "myexports@current",
- visibility: ["//visibility:public"],
- device_supported: false,
- host_supported: true,
- stubs_sources: ["myexports_myjavaapistubs@current"],
-}
-`),
- checkAllCopyRules(""),
- checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
- )
-}
-
func TestSnapshotWithJavaSystemModules(t *testing.T) {
result := testSdkWithJava(t, `
sdk {
@@ -797,6 +671,7 @@
name: "mysdk_exported-system-module@current",
sdk_member_name: "exported-system-module",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/exported-system-module.jar"],
}
@@ -804,6 +679,7 @@
name: "exported-system-module",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/exported-system-module.jar"],
}
@@ -811,6 +687,7 @@
name: "mysdk_system-module@current",
sdk_member_name: "system-module",
visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/system-module.jar"],
}
@@ -818,6 +695,7 @@
name: "mysdk_system-module",
prefer: false,
visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/system-module.jar"],
}
@@ -889,6 +767,7 @@
name: "mysdk_system-module@current",
sdk_member_name: "system-module",
visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/system-module.jar"],
@@ -898,6 +777,7 @@
name: "mysdk_system-module",
prefer: false,
visibility: ["//visibility:private"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/system-module.jar"],
@@ -978,6 +858,7 @@
name: "myexports_hostjavalib@current",
sdk_member_name: "hostjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/hostjavalib.jar"],
@@ -987,6 +868,7 @@
name: "hostjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
device_supported: false,
host_supported: true,
jars: ["java/hostjavalib.jar"],
@@ -996,6 +878,7 @@
name: "myexports_androidjavalib@current",
sdk_member_name: "androidjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/androidjavalib.jar"],
}
@@ -1003,6 +886,7 @@
name: "androidjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/androidjavalib.jar"],
}
@@ -1010,6 +894,7 @@
name: "myexports_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
target: {
android: {
@@ -1025,6 +910,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
host_supported: true,
target: {
android: {
@@ -1187,6 +1073,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
public: {
jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1201,6 +1088,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
public: {
jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1254,6 +1142,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
public: {
jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1268,6 +1157,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
public: {
jars: ["sdk_library/public/myjavalib-stubs.jar"],
@@ -1689,6 +1579,7 @@
name: "mysdk_myjavalib@current",
sdk_member_name: "myjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
doctag_files: ["doctags/docs/known_doctags"],
public: {
@@ -1704,6 +1595,7 @@
name: "myjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
shared_library: true,
doctag_files: ["doctags/docs/known_doctags"],
public: {
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/sdk_test.go b/sdk/sdk_test.go
index 2e6c62a..c4dc41b 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -181,6 +181,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -192,6 +193,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myjavalib.jar"],
}
@@ -199,6 +201,7 @@
name: "mysdk_mypublicjavalib@current",
sdk_member_name: "mypublicjavalib",
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/mypublicjavalib.jar"],
}
@@ -206,6 +209,7 @@
name: "mypublicjavalib",
prefer: false,
visibility: ["//visibility:public"],
+ apex_available: ["//apex_available:platform"],
jars: ["java/mypublicjavalib.jar"],
}
@@ -217,6 +221,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/mydefaultedjavalib.jar"],
}
@@ -228,6 +233,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/mydefaultedjavalib.jar"],
}
@@ -238,6 +244,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myprivatejavalib.jar"],
}
@@ -248,6 +255,7 @@
"//package",
"//prebuilts/mysdk",
],
+ apex_available: ["//apex_available:platform"],
jars: ["java/myprivatejavalib.jar"],
}
diff --git a/sdk/testing.go b/sdk/testing.go
index 0b280ef..91aa879 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -80,14 +80,14 @@
}
}
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
// Enable androidmk support.
// * Register the singleton
// * Configure that we are inside make
// * Add CommonOS to ensure that androidmk processing works.
android.RegisterAndroidMkBuildComponents(ctx)
- android.SetInMakeForTests(config)
+ android.SetKatiEnabledForTests(config)
config.Targets[android.CommonOS] = []android.Target{
{android.CommonOS, android.Arch{ArchType: android.Common}, android.NativeBridgeDisabled, "", "", true},
}
@@ -129,7 +129,7 @@
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
- ctx.Register(config)
+ ctx.Register()
return ctx, config
}
diff --git a/sdk/update.go b/sdk/update.go
index 7bf5dea..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.
@@ -735,6 +735,24 @@
}
}
+ // Where available copy apex_available properties from the member.
+ if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok {
+ apexAvailable := apexAware.ApexAvailable()
+ if len(apexAvailable) == 0 {
+ // //apex_available:platform is the default.
+ apexAvailable = []string{android.AvailableToPlatform}
+ }
+
+ // Add in any baseline apex available settings.
+ apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...)
+
+ // Remove duplicates and sort.
+ apexAvailable = android.FirstUniqueStrings(apexAvailable)
+ sort.Strings(apexAvailable)
+
+ m.AddProperty("apex_available", apexAvailable)
+ }
+
deviceSupported := false
hostSupported := false
@@ -749,22 +767,6 @@
addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
- // Where available copy apex_available properties from the member.
- if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok {
- apexAvailable := apexAware.ApexAvailable()
-
- // Add in any baseline apex available settings.
- apexAvailable = append(apexAvailable, apex.BaselineApexAvailable(member.Name())...)
-
- if len(apexAvailable) > 0 {
- // Remove duplicates and sort.
- apexAvailable = android.FirstUniqueStrings(apexAvailable)
- sort.Strings(apexAvailable)
-
- m.AddProperty("apex_available", apexAvailable)
- }
- }
-
// Disable installation in the versioned module of those modules that are ever installable.
if installable, ok := variant.(interface{ EverInstallable() bool }); ok {
if installable.EverInstallable() {
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/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 0aa607b..c664461 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -45,13 +45,13 @@
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("sh_test", ShTestFactory)
ctx.RegisterModuleType("sh_test_host", ShTestHostFactory)
cc.RegisterRequiredBuildComponentsForTest(ctx)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
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 f5dc5ac..1b9ff60 100644
--- a/shared/paths.go
+++ b/shared/paths.go
@@ -18,9 +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")
}
+
+// 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(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 edb8b30..93a3fe0 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// sysprop package defines a module named sysprop_library that can implement sysprop as API
+// See https://source.android.com/devices/architecture/sysprops-apis for details
package sysprop
import (
@@ -71,6 +73,8 @@
})
}
+// syspropJavaGenRule module generates srcjar containing generated java APIs.
+// It also depends on check api rule, so api check has to pass to use sysprop_library.
func (g *syspropJavaGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
var checkApiFileTimeStamp android.WritablePath
@@ -143,6 +147,9 @@
// Make this module available when building for vendor
Vendor_available *bool
+ // Make this module available when building for product
+ Product_available *bool
+
// list of .sysprop files which defines the properties.
Srcs []string `android:"path"`
@@ -167,6 +174,7 @@
syspropLibrariesLock sync.Mutex
)
+// List of sysprop_library used by property_contexts to perform type check.
func syspropLibraries(config android.Config) *[]string {
return config.Once(syspropLibrariesKey, func() interface{} {
return &[]string{}
@@ -189,7 +197,7 @@
return m.properties.Property_owner
}
-func (m *syspropLibrary) CcModuleName() string {
+func (m *syspropLibrary) CcImplementationModuleName() string {
return "lib" + m.BaseModuleName()
}
@@ -220,6 +228,8 @@
return m.currentApiFile
}
+// GenerateAndroidBuildActions of sysprop_library handles API dump and API check.
+// generated java_library will depend on these API files.
func (m *syspropLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
baseModuleName := m.BaseModuleName()
@@ -237,18 +247,19 @@
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. current.txt <-> api_dump.txt
+ // 1. compares current.txt to api-dump.txt
+ // current.txt should be identical to api-dump.txt.
msg := fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with current.txt\n`+
`Please update current.txt by:\n`+
@@ -264,7 +275,8 @@
Flag(`"` + msg + `"`).
Text("; exit 38) )")
- // 2. current.txt <-> latest.txt
+ // 2. compares current.txt to latest.txt (frozen API)
+ // current.txt should be compatible with latest.txt
msg = fmt.Sprintf(`\n******************************\n`+
`API of sysprop_library %s doesn't match with latest version\n`+
`Please fix the breakage and rebuild.\n`+
@@ -272,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").
@@ -285,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 {
@@ -310,6 +322,9 @@
}}
}
+var _ android.ApexModule = (*syspropLibrary)(nil)
+
+// Implements android.ApexModule
func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
sdkVersion android.ApiLevel) error {
return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
@@ -353,6 +368,7 @@
Recovery *bool
Recovery_available *bool
Vendor_available *bool
+ Product_available *bool
Host_supported *bool
Apex_available []string
Min_sdk_version *string
@@ -406,14 +422,16 @@
installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
installedInProduct := ctx.ProductSpecific()
isOwnerPlatform := false
- var stub string
+ var javaSyspropStub string
+ // javaSyspropStub contains stub libraries used by generated APIs, instead of framework stub.
+ // This is to make sysprop_library link against core_current.
if installedInVendorOrOdm {
- stub = "sysprop-library-stub-vendor"
+ javaSyspropStub = "sysprop-library-stub-vendor"
} else if installedInProduct {
- stub = "sysprop-library-stub-product"
+ javaSyspropStub = "sysprop-library-stub-product"
} else {
- stub = "sysprop-library-stub-platform"
+ javaSyspropStub = "sysprop-library-stub-platform"
}
switch m.Owner() {
@@ -437,8 +455,10 @@
"Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner())
}
+ // Generate a C++ implementation library.
+ // cc_library can receive *.sysprop files as their srcs, generating sources itself.
ccProps := ccLibraryProperties{}
- ccProps.Name = proptools.StringPtr(m.CcModuleName())
+ ccProps.Name = proptools.StringPtr(m.CcImplementationModuleName())
ccProps.Srcs = m.properties.Srcs
ccProps.Soc_specific = proptools.BoolPtr(ctx.SocSpecific())
ccProps.Device_specific = proptools.BoolPtr(ctx.DeviceSpecific())
@@ -449,6 +469,7 @@
ccProps.Target.Host.Static_libs = []string{"libbase", "liblog"}
ccProps.Recovery_available = m.properties.Recovery_available
ccProps.Vendor_available = m.properties.Vendor_available
+ ccProps.Product_available = m.properties.Product_available
ccProps.Host_supported = m.properties.Host_supported
ccProps.Apex_available = m.ApexProperties.Apex_available
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
@@ -458,15 +479,17 @@
// We need to only use public version, if the partition where sysprop_library will be installed
// is different from owner.
-
if ctx.ProductSpecific() {
- // Currently product partition can't own any sysprop_library.
+ // Currently product partition can't own any sysprop_library. So product always uses public.
scope = "public"
} else if isOwnerPlatform && installedInVendorOrOdm {
// Vendor or Odm should use public version of Platform's sysprop_library.
scope = "public"
}
+ // Generate a Java implementation library.
+ // Contrast to C++, syspropJavaGenRule module will generate srcjar and the srcjar will be fed
+ // to Java implementation library.
ctx.CreateModule(syspropJavaGenFactory, &syspropGenProperties{
Srcs: m.properties.Srcs,
Scope: scope,
@@ -481,7 +504,7 @@
Product_specific: proptools.BoolPtr(ctx.ProductSpecific()),
Installable: m.properties.Installable,
Sdk_version: proptools.StringPtr("core_current"),
- Libs: []string{stub},
+ Libs: []string{javaSyspropStub},
})
// if platform sysprop_library is installed in /system or /system-ext, we regard it as an API
@@ -500,11 +523,13 @@
Srcs: []string{":" + m.javaGenPublicStubName()},
Installable: proptools.BoolPtr(false),
Sdk_version: proptools.StringPtr("core_current"),
- Libs: []string{stub},
+ Libs: []string{javaSyspropStub},
Stem: proptools.StringPtr(m.BaseModuleName()),
})
}
+ // syspropLibraries will be used by property_contexts to check types.
+ // Record absolute paths of sysprop_library to prevent soong_namespace problem.
if m.ExportedToMake() {
syspropLibrariesLock.Lock()
defer syspropLibrariesLock.Unlock()
@@ -514,6 +539,8 @@
}
}
+// syspropDepsMutator adds dependencies from java implementation library to sysprop library.
+// java implementation library then depends on check API rule of sysprop library.
func syspropDepsMutator(ctx android.BottomUpMutatorContext) {
if m, ok := ctx.Module().(*syspropLibrary); ok {
ctx.AddReverseDependency(m, nil, m.javaGenModuleName())
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 7a79a1b..b8b93f6 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -57,7 +57,7 @@
func testContext(config android.Config) *android.TestContext {
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
java.RegisterJavaBuildComponents(ctx)
java.RegisterAppBuildComponents(ctx)
java.RegisterSystemModulesBuildComponents(ctx)
@@ -76,7 +76,7 @@
ctx.RegisterModuleType("sysprop_library", syspropLibraryFactory)
- ctx.Register(config)
+ ctx.Register()
return ctx
}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index b35f831..27d71e8 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -188,7 +188,7 @@
}
func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
- testSuites []string, autoGenConfig *bool) android.Path {
+ testSuites []string, autoGenConfig *bool, unitTest *bool) android.Path {
path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
if autogenPath != nil {
templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
@@ -198,7 +198,11 @@
if ctx.Device() {
autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil, "")
} else {
- autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+ if Bool(unitTest) {
+ autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", nil, "")
+ } else {
+ autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+ }
}
}
return autogenPath
diff --git a/tradefed/config.go b/tradefed/config.go
index f7e8349..f3566a8 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -27,6 +27,7 @@
pctx.SourcePathVariable("InstrumentationTestConfigTemplate", "build/make/core/instrumentation_test_config_template.xml")
pctx.SourcePathVariable("JavaTestConfigTemplate", "build/make/core/java_test_config_template.xml")
pctx.SourcePathVariable("JavaHostTestConfigTemplate", "build/make/core/java_host_test_config_template.xml")
+ pctx.SourcePathVariable("JavaHostUnitTestConfigTemplate", "build/make/core/java_host_unit_test_config_template.xml")
pctx.SourcePathVariable("NativeBenchmarkTestConfigTemplate", "build/make/core/native_benchmark_test_config_template.xml")
pctx.SourcePathVariable("NativeHostTestConfigTemplate", "build/make/core/native_host_test_config_template.xml")
pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
diff --git a/ui/build/bazel.go b/ui/build/bazel.go
index 978553d..81ce939 100644
--- a/ui/build/bazel.go
+++ b/ui/build/bazel.go
@@ -15,14 +15,50 @@
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) {
+ ctx.BeginTrace(metrics.RunBazel, "bazel")
+ defer ctx.EndTrace()
+
// "droid" is the default ninja target.
+ // TODO(b/160568333): stop hardcoding 'droid' to support building any
+ // Ninja target.
outputGroups := "droid"
if len(config.ninjaArgs) > 0 {
// At this stage, the residue slice of args passed to ninja
@@ -31,73 +67,187 @@
outputGroups = strings.Join(config.ninjaArgs, ",")
}
+ // 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.
+ 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.
bazelExecutable := filepath.Join("tools", "bazel")
- args := []string{
- "build",
- "--output_groups=" + outputGroups,
- "//:" + config.TargetProduct() + "-" + config.TargetBuildVariant(),
+ cmd := Command(ctx, config, "bazel", bazelExecutable)
+
+ // Append custom startup flags to the Bazel command. Startup flags affect
+ // the Bazel server itself, and any changes to these flags would incur a
+ // restart of the server, losing much of the in-memory incrementality.
+ if extraStartupArgs, ok := cmd.Environment.Get("BAZEL_STARTUP_ARGS"); ok {
+ cmd.Args = append(cmd.Args, strings.Fields(extraStartupArgs)...)
}
- cmd := Command(ctx, config, "bazel", bazelExecutable, args...)
+ // Start constructing the `build` command.
+ actionName := bazel.BazelNinjaExecRunName
+ cmd.Args = append(cmd.Args,
+ "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, actionName)),
+ "--slim_profile=true",
+ )
- cmd.Environment.Set("DIST_DIR", config.DistDir())
- cmd.Environment.Set("SHELL", "/bin/bash")
+ 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)
+ }
- ctx.Println(cmd.Cmd)
+ // 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
+ // user-provided one, for hermeticity reasons.
+ if pathEnvValue, ok := config.environ.Get("PATH"); ok {
+ cmd.Environment.Set("PATH", pathEnvValue)
+ cmd.Args = append(cmd.Args, "--action_env=PATH="+pathEnvValue)
+ }
+
+ // 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)...)
+ }
+
+ // 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()
- // Obtain the Bazel output directory for ninja_build.
- infoArgs := []string{
- "info",
- "output_path",
- }
+ // Post-processing steps start here. Once the Bazel build completes, the
+ // output files are still stored in the execution root, not in $OUT_DIR.
+ // 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.
- infoCmd := Command(ctx, config, "bazel", bazelExecutable, infoArgs...)
-
- 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 1cf2023..926da31 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -23,13 +23,20 @@
"android/soong/ui/metrics"
)
-// Ensures the out directory exists, and has the proper files to prevent kati
-// from recursing into it.
+// SetupOutDir ensures the out directory exists, and has the proper files to
+// prevent kati from recursing into it.
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() {
- ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make"))
+ 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
+ // soong_build output directory for the soong_build primary builder to
+ // know if the user wants to run Kati after.
+ //
+ // This does not preclude running Kati for *product configuration purposes*.
+ ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.kati_enabled"))
}
// The ninja_build file is used by our buildbots to understand that the output
// can be parsed as ninja output.
@@ -37,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)
}
@@ -60,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
}
@@ -78,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
@@ -90,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 {
@@ -101,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")
@@ -108,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)
}
@@ -132,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)
@@ -159,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, what)
+ help(ctx, config)
return
}
@@ -184,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)
@@ -209,14 +233,18 @@
runMakeProductConfig(ctx, config)
}
+ // Everything below here depends on product config.
+
if inList("installclean", config.Arguments()) ||
inList("install-clean", config.Arguments()) {
- installClean(ctx, config, what)
+ 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, what)
+ dataClean(ctx, config)
ctx.Println("Deleted data files.")
return
}
@@ -233,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 {
@@ -252,7 +280,7 @@
}
if what&BuildNinja != 0 {
- if !config.SkipMake() {
+ if what&BuildKati != 0 {
installCleanIfNecessary(ctx, config)
}
@@ -260,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)
}
@@ -273,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())
}
}
@@ -295,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/cleanbuild.go b/ui/build/cleanbuild.go
index 03e884a..06f6c63 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -26,8 +26,11 @@
"android/soong/ui/metrics"
)
+// Given a series of glob patterns, remove matching files and directories from the filesystem.
+// For example, "malware*" would remove all files and directories in the current directory that begin with "malware".
func removeGlobs(ctx Context, globs ...string) {
for _, glob := range globs {
+ // Find files and directories that match this glob pattern.
files, err := filepath.Glob(glob)
if err != nil {
// Only possible error is ErrBadPattern
@@ -45,13 +48,15 @@
// Remove everything under the out directory. Don't remove the out directory
// itself in case it's a symlink.
-func clean(ctx Context, config Config, what int) {
+func clean(ctx Context, config Config) {
removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
ctx.Println("Entire build directory removed.")
}
-func dataClean(ctx Context, config Config, what int) {
+// Remove everything in the data directory.
+func dataClean(ctx Context, config Config) {
removeGlobs(ctx, filepath.Join(config.ProductOut(), "data", "*"))
+ ctx.Println("Entire data directory removed.")
}
// installClean deletes all of the installed files -- the intent is to remove
@@ -61,8 +66,8 @@
//
// This is faster than a full clean, since we're not deleting the
// intermediates. Instead of recompiling, we can just copy the results.
-func installClean(ctx Context, config Config, what int) {
- dataClean(ctx, config, what)
+func installClean(ctx Context, config Config) {
+ dataClean(ctx, config)
if hostCrossOutPath := config.hostCrossOut(); hostCrossOutPath != "" {
hostCrossOut := func(path string) string {
@@ -145,85 +150,95 @@
// Since products and build variants (unfortunately) shared the same
// PRODUCT_OUT staging directory, things can get out of sync if different
// build configurations are built in the same tree. This function will
-// notice when the configuration has changed and call installclean to
+// notice when the configuration has changed and call installClean to
// remove the files necessary to keep things consistent.
func installCleanIfNecessary(ctx Context, config Config) {
configFile := config.DevicePreviousProductConfig()
prefix := "PREVIOUS_BUILD_CONFIG := "
suffix := "\n"
- currentProduct := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix
+ currentConfig := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix
ensureDirectoriesExist(ctx, filepath.Dir(configFile))
writeConfig := func() {
- err := ioutil.WriteFile(configFile, []byte(currentProduct), 0666)
+ err := ioutil.WriteFile(configFile, []byte(currentConfig), 0666) // a+rw
if err != nil {
ctx.Fatalln("Failed to write product config:", err)
}
}
- prev, err := ioutil.ReadFile(configFile)
+ previousConfigBytes, err := ioutil.ReadFile(configFile)
if err != nil {
if os.IsNotExist(err) {
+ // Just write the new config file, no old config file to worry about.
writeConfig()
return
} else {
ctx.Fatalln("Failed to read previous product config:", err)
}
- } else if string(prev) == currentProduct {
+ }
+
+ previousConfig := string(previousConfigBytes)
+ if previousConfig == currentConfig {
+ // Same config as before - nothing to clean.
return
}
- if disable, _ := config.Environment().Get("DISABLE_AUTO_INSTALLCLEAN"); disable == "true" {
- ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.")
+ if config.Environment().IsEnvTrue("DISABLE_AUTO_INSTALLCLEAN") {
+ ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set and true; skipping auto-clean. Your tree may be in an inconsistent state.")
return
}
ctx.BeginTrace(metrics.PrimaryNinja, "installclean")
defer ctx.EndTrace()
- prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix)
- currentConfig := strings.TrimPrefix(strings.TrimSuffix(currentProduct, suffix), prefix)
+ previousProductAndVariant := strings.TrimPrefix(strings.TrimSuffix(previousConfig, suffix), prefix)
+ currentProductAndVariant := strings.TrimPrefix(strings.TrimSuffix(currentConfig, suffix), prefix)
- ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", prevConfig, currentConfig)
+ ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", previousProductAndVariant, currentProductAndVariant)
- installClean(ctx, config, 0)
+ installClean(ctx, config)
writeConfig()
}
// cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
// the filesystem if they were removed from the input file since the last execution.
-func cleanOldFiles(ctx Context, basePath, file string) {
- file = filepath.Join(basePath, file)
- oldFile := file + ".previous"
+func cleanOldFiles(ctx Context, basePath, newFile string) {
+ newFile = filepath.Join(basePath, newFile)
+ oldFile := newFile + ".previous"
- if _, err := os.Stat(file); err != nil {
- ctx.Fatalf("Expected %q to be readable", file)
+ if _, err := os.Stat(newFile); err != nil {
+ ctx.Fatalf("Expected %q to be readable", newFile)
}
if _, err := os.Stat(oldFile); os.IsNotExist(err) {
- if err := os.Rename(file, oldFile); err != nil {
- ctx.Fatalf("Failed to rename file list (%q->%q): %v", file, oldFile, err)
+ if err := os.Rename(newFile, oldFile); err != nil {
+ ctx.Fatalf("Failed to rename file list (%q->%q): %v", newFile, oldFile, err)
}
return
}
- var newPaths, oldPaths []string
- if newData, err := ioutil.ReadFile(file); err == nil {
- if oldData, err := ioutil.ReadFile(oldFile); err == nil {
- // Common case: nothing has changed
- if bytes.Equal(newData, oldData) {
- return
- }
- newPaths = strings.Fields(string(newData))
- oldPaths = strings.Fields(string(oldData))
- } else {
- ctx.Fatalf("Failed to read list of installable files (%q): %v", oldFile, err)
- }
+ var newData, oldData []byte
+ if data, err := ioutil.ReadFile(newFile); err == nil {
+ newData = data
} else {
- ctx.Fatalf("Failed to read list of installable files (%q): %v", file, err)
+ ctx.Fatalf("Failed to read list of installable files (%q): %v", newFile, err)
}
+ if data, err := ioutil.ReadFile(oldFile); err == nil {
+ oldData = data
+ } else {
+ ctx.Fatalf("Failed to read list of installable files (%q): %v", oldFile, err)
+ }
+
+ // Common case: nothing has changed
+ if bytes.Equal(newData, oldData) {
+ return
+ }
+
+ var newPaths, oldPaths []string
+ newPaths = strings.Fields(string(newData))
+ oldPaths = strings.Fields(string(oldData))
// These should be mostly sorted by make already, but better make sure Go concurs
sort.Strings(newPaths)
@@ -242,42 +257,55 @@
continue
}
}
+
// File only exists in the old list; remove if it exists
- old := filepath.Join(basePath, oldPaths[0])
+ oldPath := filepath.Join(basePath, oldPaths[0])
oldPaths = oldPaths[1:]
- if fi, err := os.Stat(old); err == nil {
- if fi.IsDir() {
- if err := os.Remove(old); err == nil {
- ctx.Println("Removed directory that is no longer installed: ", old)
- cleanEmptyDirs(ctx, filepath.Dir(old))
+
+ if oldFile, err := os.Stat(oldPath); err == nil {
+ if oldFile.IsDir() {
+ if err := os.Remove(oldPath); err == nil {
+ ctx.Println("Removed directory that is no longer installed: ", oldPath)
+ cleanEmptyDirs(ctx, filepath.Dir(oldPath))
} else {
- ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err)
+ ctx.Println("Failed to remove directory that is no longer installed (%q): %v", oldPath, err)
ctx.Println("It's recommended to run `m installclean`")
}
} else {
- if err := os.Remove(old); err == nil {
- ctx.Println("Removed file that is no longer installed: ", old)
- cleanEmptyDirs(ctx, filepath.Dir(old))
+ // Removing a file, not a directory.
+ if err := os.Remove(oldPath); err == nil {
+ ctx.Println("Removed file that is no longer installed: ", oldPath)
+ cleanEmptyDirs(ctx, filepath.Dir(oldPath))
} else if !os.IsNotExist(err) {
- ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err)
+ ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", oldPath, err)
}
}
}
}
// Use the new list as the base for the next build
- os.Rename(file, oldFile)
+ os.Rename(newFile, oldFile)
}
+// cleanEmptyDirs will delete a directory if it contains no files.
+// If a deletion occurs, then it also recurses upwards to try and delete empty parent directories.
func cleanEmptyDirs(ctx Context, dir string) {
files, err := ioutil.ReadDir(dir)
- if err != nil || len(files) > 0 {
+ if err != nil {
+ ctx.Println("Could not read directory while trying to clean empty dirs: ", dir)
return
}
- if err := os.Remove(dir); err == nil {
- ctx.Println("Removed directory that is no longer installed: ", dir)
- } else {
- ctx.Fatalf("Failed to remove directory that is no longer installed (%q): %v", dir, err)
+ if len(files) > 0 {
+ // Directory is not empty.
+ return
}
+
+ if err := os.Remove(dir); err == nil {
+ ctx.Println("Removed empty directory (may no longer be installed?): ", dir)
+ } else {
+ ctx.Fatalf("Failed to remove empty directory (which may no longer be installed?) %q: (%v)", dir, err)
+ }
+
+ // Try and delete empty parent directories too.
cleanEmptyDirs(ctx, filepath.Dir(dir))
}
diff --git a/ui/build/config.go b/ui/build/config.go
index e57c730..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,6 +281,26 @@
}
}
+ 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
@@ -526,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] == '-' {
@@ -683,16 +713,28 @@
}
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
}
+func (c *configImpl) BazelOutDir() string {
+ return filepath.Join(c.OutDir(), "bazel")
+}
+
func (c *configImpl) SoongOutDir() string {
return filepath.Join(c.OutDir(), "soong")
}
@@ -726,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 {
@@ -840,6 +886,10 @@
return false
}
+func (c *configImpl) UseBazel() bool {
+ return c.useBazel
+}
+
func (c *configImpl) StartRBE() bool {
if !c.UseRBE() {
return false
@@ -854,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()
}
@@ -872,7 +922,7 @@
return v
}
}
- return c.logDir()
+ return c.rbeLogDir()
}
func (c *configImpl) rbeLogPath() string {
@@ -881,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 {
@@ -1089,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/environment.go b/ui/build/environment.go
index 9bca7c0..6d8a28f 100644
--- a/ui/build/environment.go
+++ b/ui/build/environment.go
@@ -37,8 +37,8 @@
// It's equivalent to the os.LookupEnv function, but with this copy of the
// Environment.
func (e *Environment) Get(key string) (string, bool) {
- for _, env := range *e {
- if k, v, ok := decodeKeyValue(env); ok && k == key {
+ for _, envVar := range *e {
+ if k, v, ok := decodeKeyValue(envVar); ok && k == key {
return v, true
}
}
@@ -65,37 +65,40 @@
// Unset removes the specified keys from the Environment.
func (e *Environment) Unset(keys ...string) {
- out := (*e)[:0]
- for _, env := range *e {
- if key, _, ok := decodeKeyValue(env); ok && inList(key, keys) {
+ newEnv := (*e)[:0]
+ for _, envVar := range *e {
+ if key, _, ok := decodeKeyValue(envVar); ok && inList(key, keys) {
+ // Delete this key.
continue
}
- out = append(out, env)
+ newEnv = append(newEnv, envVar)
}
- *e = out
+ *e = newEnv
}
// UnsetWithPrefix removes all keys that start with prefix.
func (e *Environment) UnsetWithPrefix(prefix string) {
- out := (*e)[:0]
- for _, env := range *e {
- if key, _, ok := decodeKeyValue(env); ok && strings.HasPrefix(key, prefix) {
+ newEnv := (*e)[:0]
+ for _, envVar := range *e {
+ if key, _, ok := decodeKeyValue(envVar); ok && strings.HasPrefix(key, prefix) {
+ // Delete this key.
continue
}
- out = append(out, env)
+ newEnv = append(newEnv, envVar)
}
- *e = out
+ *e = newEnv
}
// Allow removes all keys that are not present in the input list
func (e *Environment) Allow(keys ...string) {
- out := (*e)[:0]
- for _, env := range *e {
- if key, _, ok := decodeKeyValue(env); ok && inList(key, keys) {
- out = append(out, env)
+ newEnv := (*e)[:0]
+ for _, envVar := range *e {
+ if key, _, ok := decodeKeyValue(envVar); ok && inList(key, keys) {
+ // Keep this key.
+ newEnv = append(newEnv, envVar)
}
}
- *e = out
+ *e = newEnv
}
// Environ returns the []string required for exec.Cmd.Env
@@ -105,11 +108,11 @@
// Copy returns a copy of the Environment so that independent changes may be made.
func (e *Environment) Copy() *Environment {
- ret := Environment(make([]string, len(*e)))
- for i, v := range *e {
- ret[i] = v
+ envCopy := Environment(make([]string, len(*e)))
+ for i, envVar := range *e {
+ envCopy[i] = envVar
}
- return &ret
+ return &envCopy
}
// IsTrue returns whether an environment variable is set to a positive value (1,y,yes,on,true)
@@ -140,15 +143,20 @@
return e.appendFromKati(file)
}
+// Helper function for AppendFromKati. Accepts an io.Reader to make testing easier.
func (e *Environment) appendFromKati(reader io.Reader) error {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if len(text) == 0 || text[0] == '#' {
+ // Skip blank lines and comments.
continue
}
+ // We expect two space-delimited strings, like:
+ // unset 'HOME'
+ // export 'BEST_PIZZA_CITY'='NYC'
cmd := strings.SplitN(text, " ", 2)
if len(cmd) != 2 {
return fmt.Errorf("Unknown kati environment line: %q", text)
@@ -159,6 +167,8 @@
if !ok {
return fmt.Errorf("Failed to unquote kati line: %q", text)
}
+
+ // Actually unset it.
e.Unset(str)
} else if cmd[0] == "export" {
key, value, ok := decodeKeyValue(cmd[1])
@@ -175,6 +185,7 @@
return fmt.Errorf("Failed to unquote kati line: %q", text)
}
+ // Actually set it.
e.Set(key, value)
} else {
return fmt.Errorf("Unknown kati environment command: %q", text)
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 7a85657..2eb84ca 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -27,8 +27,10 @@
"android/soong/ui/metrics"
)
-// This file provides an interface to the Finder for use in Soong UI
-// This file stores configuration information about which files to find
+// This file provides an interface to the Finder type for soong_ui. Finder is
+// used to recursively traverse the source tree to gather paths of files, such
+// as Android.bp or Android.mk, and store the lists/database of paths in files
+// under `$OUT_DIR/.module_paths`. This directory can also be dist'd.
// NewSourceFinder returns a new Finder configured to search for source files.
// Callers of NewSourceFinder should call <f.Shutdown()> when done
@@ -36,14 +38,18 @@
ctx.BeginTrace(metrics.RunSetupTool, "find modules")
defer ctx.EndTrace()
+ // Set up the working directory for the Finder.
dir, err := os.Getwd()
if err != nil {
ctx.Fatalf("No working directory for module-finder: %v", err.Error())
}
filesystem := fs.OsFs
- // if the root dir is ignored, then the subsequent error messages are very confusing,
- // so check for that upfront
+ // .out-dir and .find-ignore are markers for Finder to ignore siblings and
+ // subdirectories of the directory Finder finds them in, hence stopping the
+ // search recursively down those branches. It's possible that these files
+ // are in the root directory, and if they are, then the subsequent error
+ // messages are very confusing, so check for that here.
pruneFiles := []string{".out-dir", ".find-ignore"}
for _, name := range pruneFiles {
prunePath := filepath.Join(dir, name)
@@ -53,22 +59,34 @@
}
}
+ // Set up configuration parameters for the Finder cache.
cacheParams := finder.CacheParams{
WorkingDirectory: dir,
RootDirs: []string{"."},
ExcludeDirs: []string{".git", ".repo"},
PruneFiles: pruneFiles,
IncludeFiles: []string{
+ // Kati build definitions.
"Android.mk",
+ // Product configuration files.
"AndroidProducts.mk",
+ // General Soong build definitions, using the Blueprint syntax.
"Android.bp",
+ // build/blueprint build definitions, using the Blueprint syntax.
"Blueprints",
+ // Bazel build definitions.
"BUILD.bazel",
+ // Kati clean definitions.
"CleanSpec.mk",
+ // Ownership definition.
"OWNERS",
+ // Test configuration for modules in directories that contain this
+ // file.
"TEST_MAPPING",
+ // Bazel top-level file to mark a directory as a Bazel workspace.
"WORKSPACE",
},
+ // Bazel Starlark configuration files.
IncludeSuffixes: []string{".bzl"},
}
dumpDir := config.FileListDir()
@@ -80,6 +98,7 @@
return f
}
+// Finds the list of Bazel-related files (BUILD, WORKSPACE and Starlark) in the tree.
func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
matches := []string{}
for _, foundName := range entries.FileNames {
@@ -98,12 +117,21 @@
dumpDir := config.FileListDir()
os.MkdirAll(dumpDir, 0777)
+ // Stop searching a subdirectory recursively after finding an Android.mk.
androidMks := f.FindFirstNamedAt(".", "Android.mk")
err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
}
+ // Stop searching a subdirectory recursively after finding a CleanSpec.mk.
+ cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
+ err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
+ if err != nil {
+ ctx.Fatalf("Could not export module list: %v", err)
+ }
+
+ // Only consider AndroidProducts.mk in device/, vendor/ and product/, recursively in these directories.
androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
@@ -112,31 +140,30 @@
ctx.Fatalf("Could not export product list: %v", err)
}
+ // Recursively look for all Bazel related files.
bazelFiles := f.FindMatching(".", findBazelFiles)
err = dumpListToFile(ctx, config, bazelFiles, filepath.Join(dumpDir, "bazel.list"))
if err != nil {
ctx.Fatalf("Could not export bazel BUILD list: %v", err)
}
- cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
- err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
- if err != nil {
- ctx.Fatalf("Could not export module list: %v", err)
- }
-
+ // Recursively look for all OWNERS files.
owners := f.FindNamedAt(".", "OWNERS")
err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list"))
if err != nil {
ctx.Fatalf("Could not find OWNERS: %v", err)
}
+ // Recursively look for all TEST_MAPPING files.
testMappings := f.FindNamedAt(".", "TEST_MAPPING")
err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
if err != nil {
ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
}
+ // Recursively look for all Android.bp files
androidBps := f.FindNamedAt(".", "Android.bp")
+ // The files are named "Blueprints" only in the build/blueprint directory.
androidBps = append(androidBps, f.FindNamedAt("build/blueprint", "Blueprints")...)
if len(androidBps) == 0 {
ctx.Fatalf("No Android.bp found")
@@ -148,10 +175,12 @@
if config.Dist() {
f.WaitForDbDump()
+ // Dist the files.db plain text database.
distFile(ctx, config, f.DbPath, "module_paths")
}
}
+// Write the .list files to disk.
func dumpListToFile(ctx Context, config Config, list []string, filePath string) (err error) {
desiredText := strings.Join(list, "\n")
desiredBytes := []byte(desiredText)
diff --git a/ui/build/kati.go b/ui/build/kati.go
index f6c0f52..06ec646 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -33,12 +33,18 @@
const katiCleanspecSuffix = "-cleanspec"
const katiPackageSuffix = "-package"
-// genKatiSuffix creates a suffix for kati-generated files so that we can cache
-// them based on their inputs. So this should encode all common changes to Kati
-// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command
-// line arguments, and the directories specified by mm/mmm.
+// genKatiSuffix creates a filename suffix for kati-generated files so that we
+// can cache them based on their inputs. Such files include the generated Ninja
+// files and env.sh environment variable setup files.
+//
+// The filename suffix should encode all common changes to Kati inputs.
+// Currently that includes the TARGET_PRODUCT and kati-processed command line
+// arguments.
func genKatiSuffix(ctx Context, config Config) {
+ // Construct the base suffix.
katiSuffix := "-" + config.TargetProduct()
+
+ // Append kati arguments to the suffix.
if args := config.KatiArgs(); len(args) > 0 {
katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
}
@@ -60,51 +66,101 @@
}
}
+// Base function to construct and run the Kati command line with additional
+// arguments, and a custom function closure to mutate the environment Kati runs
+// in.
func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
executable := config.PrebuiltBuildTool("ckati")
+ // cKati arguments.
args = append([]string{
+ // Instead of executing commands directly, generate a Ninja file.
"--ninja",
+ // Generate Ninja files in the output directory.
"--ninja_dir=" + config.OutDir(),
+ // Filename suffix of the generated Ninja file.
"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
+ // Remove common parts at the beginning of a Ninja file, like build_dir,
+ // local_pool and _kati_always_build_. Allows Kati to be run multiple
+ // times, with generated Ninja files combined in a single invocation
+ // using 'include'.
"--no_ninja_prelude",
+ // Support declaring phony outputs in AOSP Ninja.
"--use_ninja_phony_output",
+ // Support declaring symlink outputs in AOSP Ninja.
"--use_ninja_symlink_outputs",
+ // Regenerate the Ninja file if environment inputs have changed. e.g.
+ // CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
+ // $(shell ..) results.
"--regen",
+ // Skip '-include' directives starting with the specified path. Used to
+ // ignore generated .mk files.
"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
+ // Detect the use of $(shell echo ...).
"--detect_android_echo",
+ // Colorful ANSI-based warning and error messages.
"--color_warnings",
+ // Generate all targets, not just the top level requested ones.
"--gen_all_targets",
+ // Use the built-in emulator of GNU find for better file finding
+ // performance. Used with $(shell find ...).
"--use_find_emulator",
+ // Fail when the find emulator encounters problems.
"--werror_find_emulator",
+ // Do not provide any built-in rules.
"--no_builtin_rules",
+ // Fail when suffix rules are used.
"--werror_suffix_rules",
- "--warn_real_to_phony",
- "--warn_phony_looks_real",
+ // Fail when a real target depends on a phony target.
"--werror_real_to_phony",
- "--werror_phony_looks_real",
- "--werror_writable",
+ // Makes real_to_phony checks assume that any top-level or leaf
+ // dependencies that does *not* have a '/' in it is a phony target.
"--top_level_phony",
+ // Fail when a phony target contains slashes.
+ "--werror_phony_looks_real",
+ // Fail when writing to a read-only directory.
+ "--werror_writable",
+ // Print Kati's internal statistics, such as the number of variables,
+ // implicit/explicit/suffix rules, and so on.
"--kati_stats",
}, args...)
+ // Generate a minimal Ninja file.
+ //
+ // Used for build_test and multiproduct_kati, which runs Kati several
+ // hundred times for different configurations to test file generation logic.
+ // These can result in generating Ninja files reaching ~1GB or more,
+ // resulting in ~hundreds of GBs of writes.
+ //
+ // Since we don't care about executing the Ninja files in these test cases,
+ // generating the Ninja file content wastes time, so skip writing any
+ // information out with --empty_ninja_file.
+ //
+ // From https://github.com/google/kati/commit/87b8da7af2c8bea28b1d8ab17679453d859f96e5
if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") {
args = append(args, "--empty_ninja_file")
}
+ // Apply 'local_pool' to to all rules that don't specify a pool.
if config.UseRemoteBuild() {
args = append(args, "--default_pool=local_pool")
}
cmd := Command(ctx, config, "ckati", executable, args...)
+
+ // Set up the nsjail sandbox.
cmd.Sandbox = katiSandbox
+
+ // Set up stdout and stderr.
pipe, err := cmd.StdoutPipe()
if err != nil {
ctx.Fatalln("Error getting output pipe for ckati:", err)
}
cmd.Stderr = cmd.Stdout
+ // Apply the caller's function closure to mutate the environment variables.
envFunc(cmd.Environment)
+ // Pass on various build environment metadata to Kati.
if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
username := "unknown"
if u, err := user.Current(); err == nil {
@@ -125,6 +181,8 @@
}
cmd.StartOrFatal()
+ // Set up the ToolStatus command line reader for Kati for a consistent UI
+ // for the user.
status.KatiReader(ctx.Status.StartTool(), pipe)
cmd.WaitOrFatal()
}
@@ -134,35 +192,57 @@
defer ctx.EndTrace()
args := []string{
+ // Mark the output directory as writable.
"--writable", config.OutDir() + "/",
+ // Fail when encountering implicit rules. e.g.
+ // %.foo: %.bar
+ // cp $< $@
"--werror_implicit_rules",
+ // Entry point for the Kati Ninja file generation.
"-f", "build/make/core/main.mk",
}
if !config.BuildBrokenDupRules() {
+ // Fail when redefining / duplicating a target.
args = append(args, "--werror_overriding_commands")
}
args = append(args, config.KatiArgs()...)
args = append(args,
+ // Location of the Make vars .mk file generated by Soong.
"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
+ // Location of the Android.mk file generated by Soong. This
+ // file contains Soong modules represented as Kati modules,
+ // allowing Kati modules to depend on Soong modules.
"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
+ // Directory containing outputs for the target device.
"TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
+ // Directory containing .mk files for packaging purposes, such as
+ // the dist.mk file, containing dist-for-goals data.
"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
+ // compress and dist the main build ninja file.
distGzipFile(ctx, config, config.KatiBuildNinjaFile())
+ // Cleanup steps.
cleanCopyHeaders(ctx, config)
cleanOldInstalledFiles(ctx, config)
}
+// Clean out obsolete header files on the disk that were *not copied* during the
+// build with BUILD_COPY_HEADERS and LOCAL_COPY_HEADERS.
+//
+// These should be increasingly uncommon, as it's a deprecated feature and there
+// isn't an equivalent feature in Soong.
func cleanCopyHeaders(ctx Context, config Config) {
ctx.BeginTrace("clean", "clean copy headers")
defer ctx.EndTrace()
+ // Read and parse the list of copied headers from a file in the product
+ // output directory.
data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list"))
if err != nil {
if os.IsNotExist(err) {
@@ -178,6 +258,8 @@
headerDir := headers[0]
headers = headers[1:]
+ // Walk the tree and remove any headers that are not in the list of copied
+ // headers in the current build.
filepath.Walk(headerDir,
func(path string, info os.FileInfo, err error) error {
if err != nil {
@@ -196,6 +278,8 @@
})
}
+// Clean out any previously installed files from the disk that are not installed
+// in the current build.
func cleanOldInstalledFiles(ctx Context, config Config) {
ctx.BeginTrace("clean", "clean old installed files")
defer ctx.EndTrace()
@@ -213,18 +297,27 @@
cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
}
+// Generate the Ninja file containing the packaging command lines for the dist
+// dir.
func runKatiPackage(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati package")
defer ctx.EndTrace()
args := []string{
+ // Mark the dist dir as writable.
"--writable", config.DistDir() + "/",
+ // Fail when encountering implicit rules. e.g.
"--werror_implicit_rules",
+ // Fail when redefining / duplicating a target.
"--werror_overriding_commands",
+ // Entry point.
"-f", "build/make/packaging/main.mk",
+ // Directory containing .mk files for packaging purposes, such as
+ // the dist.mk file, containing dist-for-goals data.
"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
}
+ // Run Kati against a restricted set of environment variables.
runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
env.Allow([]string{
// Some generic basics
@@ -251,16 +344,21 @@
}
})
+ // Compress and dist the packaging Ninja file.
distGzipFile(ctx, config, config.KatiPackageNinjaFile())
}
+// Run Kati on the cleanspec files to clean the build.
func runKatiCleanSpec(ctx Context, config Config) {
ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
defer ctx.EndTrace()
runKati(ctx, config, katiCleanspecSuffix, []string{
+ // Fail when encountering implicit rules. e.g.
"--werror_implicit_rules",
+ // Fail when redefining / duplicating a target.
"--werror_overriding_commands",
+ // Entry point.
"-f", "build/make/core/cleanbuild.mk",
"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
"TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index fa44cb1..7799766 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -27,10 +27,16 @@
"android/soong/ui/status"
)
+// Constructs and runs the Ninja command line with a restricted set of
+// environment variables. It's important to restrict the environment Ninja runs
+// for hermeticity reasons, and to avoid spurious rebuilds.
func runNinja(ctx Context, config Config) {
ctx.BeginTrace(metrics.PrimaryNinja, "ninja")
defer ctx.EndTrace()
+ // Sets up the FIFO status updater that reads the Ninja protobuf output, and
+ // translates it to the soong_ui status output, displaying real-time
+ // progress of the build.
fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
defer nr.Close()
@@ -64,8 +70,12 @@
"-w", "missingdepfile=err")
cmd := Command(ctx, config, "ninja", executable, args...)
+
+ // Set up the nsjail sandbox Ninja runs in.
cmd.Sandbox = ninjaSandbox
if config.HasKatiSuffix() {
+ // Reads and executes a shell script from Kati that sets/unsets the
+ // environment Ninja runs in.
cmd.Environment.AppendFromKati(config.KatiEnvFile())
}
@@ -78,8 +88,8 @@
cmd.Args = append(cmd.Args, strings.Fields(extra)...)
}
- logPath := filepath.Join(config.OutDir(), ".ninja_log")
ninjaHeartbeatDuration := time.Minute * 5
+ // Get the ninja heartbeat interval from the environment before it's filtered away later.
if overrideText, ok := cmd.Environment.Get("NINJA_HEARTBEAT_INTERVAL"); ok {
// For example, "1m"
overrideDuration, err := time.ParseDuration(overrideText)
@@ -88,18 +98,22 @@
}
}
- // Filter the environment, as ninja does not rebuild files when environment variables change.
+ // Filter the environment, as ninja does not rebuild files when environment
+ // variables change.
//
- // Anything listed here must not change the output of rules/actions when the value changes,
- // otherwise incremental builds may be unsafe. Vars explicitly set to stable values
- // elsewhere in soong_ui are fine.
+ // Anything listed here must not change the output of rules/actions when the
+ // value changes, otherwise incremental builds may be unsafe. Vars
+ // explicitly set to stable values elsewhere in soong_ui are fine.
//
- // For the majority of cases, either Soong or the makefiles should be replicating any
- // necessary environment variables in the command line of each action that needs it.
+ // For the majority of cases, either Soong or the makefiles should be
+ // replicating any necessary environment variables in the command line of
+ // each action that needs it.
if cmd.Environment.IsEnvTrue("ALLOW_NINJA_ENV") {
ctx.Println("Allowing all environment variables during ninja; incremental builds may be unsafe.")
} else {
cmd.Environment.Allow(append([]string{
+ // Set the path to a symbolizer (e.g. llvm-symbolizer) so ASAN-based
+ // tools can symbolize crashes.
"ASAN_SYMBOLIZER_PATH",
"HOME",
"JAVA_HOME",
@@ -108,27 +122,23 @@
"OUT_DIR",
"PATH",
"PWD",
+ // https://docs.python.org/3/using/cmdline.html#envvar-PYTHONDONTWRITEBYTECODE
"PYTHONDONTWRITEBYTECODE",
"TMPDIR",
"USER",
// TODO: remove these carefully
+ // Options for the address sanitizer.
"ASAN_OPTIONS",
+ // The list of Android app modules to be built in an unbundled manner.
"TARGET_BUILD_APPS",
+ // The variant of the product being built. e.g. eng, userdebug, debug.
"TARGET_BUILD_VARIANT",
+ // The product name of the product being built, e.g. aosp_arm, aosp_flame.
"TARGET_PRODUCT",
// 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",
@@ -162,6 +172,7 @@
cmd.Environment.Set("DIST_DIR", config.DistDir())
cmd.Environment.Set("SHELL", "/bin/bash")
+ // Print the environment variables that Ninja is operating in.
ctx.Verboseln("Ninja environment: ")
envVars := cmd.Environment.Environ()
sort.Strings(envVars)
@@ -169,17 +180,21 @@
ctx.Verbosef(" %s", envVar)
}
- // Poll the ninja log for updates; if it isn't updated enough, then we want to show some diagnostics
+ // Poll the Ninja log for updates regularly based on the heartbeat
+ // frequency. If it isn't updated enough, then we want to surface the
+ // possibility that Ninja is stuck, to the user.
done := make(chan struct{})
defer close(done)
ticker := time.NewTicker(ninjaHeartbeatDuration)
defer ticker.Stop()
- checker := &statusChecker{}
+ ninjaChecker := &ninjaStucknessChecker{
+ logPath: filepath.Join(config.OutDir(), ".ninja_log"),
+ }
go func() {
for {
select {
case <-ticker.C:
- checker.check(ctx, config, logPath)
+ ninjaChecker.check(ctx, config)
case <-done:
return
}
@@ -190,37 +205,36 @@
cmd.RunAndStreamOrFatal()
}
-type statusChecker struct {
- prevTime time.Time
+// A simple struct for checking if Ninja gets stuck, using timestamps.
+type ninjaStucknessChecker struct {
+ logPath string
+ prevModTime time.Time
}
-func (c *statusChecker) check(ctx Context, config Config, pathToCheck string) {
- info, err := os.Stat(pathToCheck)
- var newTime time.Time
+// Check that a file has been modified since the last time it was checked. If
+// the mod time hasn't changed, then assume that Ninja got stuck, and print
+// diagnostics for debugging.
+func (c *ninjaStucknessChecker) check(ctx Context, config Config) {
+ info, err := os.Stat(c.logPath)
+ var newModTime time.Time
if err == nil {
- newTime = info.ModTime()
+ newModTime = info.ModTime()
}
- if newTime == c.prevTime {
- // ninja may be stuck
- dumpStucknessDiagnostics(ctx, config, pathToCheck, newTime)
+ if newModTime == c.prevModTime {
+ // The Ninja file hasn't been modified since the last time it was
+ // checked, so Ninja could be stuck. Output some diagnostics.
+ ctx.Verbosef("ninja may be stuck; last update to %v was %v. dumping process tree...", c.logPath, newModTime)
+
+ // The "pstree" command doesn't exist on Mac, but "pstree" on Linux
+ // gives more convenient output than "ps" So, we try pstree first, and
+ // ps second
+ commandText := fmt.Sprintf("pstree -pal %v || ps -ef", os.Getpid())
+
+ cmd := Command(ctx, config, "dump process tree", "bash", "-c", commandText)
+ output := cmd.CombinedOutputOrFatal()
+ ctx.Verbose(string(output))
+
+ ctx.Verbosef("done\n")
}
- c.prevTime = newTime
-}
-
-// dumpStucknessDiagnostics gets called when it is suspected that Ninja is stuck and we want to output some diagnostics
-func dumpStucknessDiagnostics(ctx Context, config Config, statusPath string, lastUpdated time.Time) {
-
- ctx.Verbosef("ninja may be stuck; last update to %v was %v. dumping process tree...", statusPath, lastUpdated)
-
- // The "pstree" command doesn't exist on Mac, but "pstree" on Linux gives more convenient output than "ps"
- // So, we try pstree first, and ps second
- pstreeCommandText := fmt.Sprintf("pstree -pal %v", os.Getpid())
- psCommandText := "ps -ef"
- commandText := pstreeCommandText + " || " + psCommandText
-
- cmd := Command(ctx, config, "dump process tree", "bash", "-c", commandText)
- output := cmd.CombinedOutputOrFatal()
- ctx.Verbose(string(output))
-
- ctx.Verbosef("done\n")
+ c.prevModTime = newModTime
}
diff --git a/ui/build/path.go b/ui/build/path.go
index 6f5cf78..86e61c0 100644
--- a/ui/build/path.go
+++ b/ui/build/path.go
@@ -29,6 +29,9 @@
"android/soong/ui/metrics"
)
+// parsePathDir returns the list of filenames of readable files in a directory.
+// This does not recurse into subdirectories, and does not contain subdirectory
+// names in the list.
func parsePathDir(dir string) []string {
f, err := os.Open(dir)
if err != nil {
@@ -54,10 +57,12 @@
return ret
}
-// A "lite" version of SetupPath used for dumpvars, or other places that need
-// minimal overhead (but at the expense of logging). If tmpDir is empty, the
-// default TMPDIR is used from config.
+// SetupLitePath is the "lite" version of SetupPath used for dumpvars, or other
+// places that does not need the full logging capabilities of path_interposer,
+// wants the minimal performance overhead, and still get the benefits of $PATH
+// hermeticity.
func SetupLitePath(ctx Context, config Config, tmpDir string) {
+ // Don't replace the path twice.
if config.pathReplaced {
return
}
@@ -67,6 +72,7 @@
origPath, _ := config.Environment().Get("PATH")
+ // If tmpDir is empty, the default TMPDIR is used from config.
if tmpDir == "" {
tmpDir, _ = config.Environment().Get("TMPDIR")
}
@@ -74,8 +80,10 @@
ensureEmptyDirectoriesExist(ctx, myPath)
os.Setenv("PATH", origPath)
+ // Iterate over the ACL configuration of host tools for this build.
for name, pathConfig := range paths.Configuration {
if !pathConfig.Symlink {
+ // Excludes 'Forbidden' and 'LinuxOnlyPrebuilt' PathConfigs.
continue
}
@@ -88,6 +96,7 @@
continue
}
+ // Symlink allowed host tools into a directory for hermeticity.
err = os.Symlink(origExec, filepath.Join(myPath, name))
if err != nil {
ctx.Fatalln("Failed to create symlink:", err)
@@ -96,14 +105,26 @@
myPath, _ = filepath.Abs(myPath)
+ // Set up the checked-in prebuilts path directory for the current host OS.
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ // Set $PATH to be the directories containing the host tool symlinks, and
+ // the prebuilts directory for the current host OS.
config.Environment().Set("PATH", myPath)
config.pathReplaced = true
}
+// SetupPath uses the path_interposer to intercept calls to $PATH binaries, and
+// communicates with the interposer to validate allowed $PATH binaries at
+// runtime, using logs as a medium.
+//
+// This results in hermetic directories in $PATH containing only allowed host
+// tools for the build, and replaces $PATH to contain *only* these directories,
+// and enables an incremental restriction of tools allowed in the $PATH without
+// breaking existing use cases.
func SetupPath(ctx Context, config Config) {
+ // Don't replace $PATH twice.
if config.pathReplaced {
return
}
@@ -112,9 +133,11 @@
defer ctx.EndTrace()
origPath, _ := config.Environment().Get("PATH")
+ // The directory containing symlinks from binaries in $PATH to the interposer.
myPath := filepath.Join(config.OutDir(), ".path")
interposer := myPath + "_interposer"
+ // Bootstrap the path_interposer Go binary with microfactory.
var cfg microfactory.Config
cfg.Map("android/soong", "build/soong")
cfg.TrimPath, _ = filepath.Abs(".")
@@ -122,15 +145,20 @@
ctx.Fatalln("Failed to build path interposer:", err)
}
+ // Save the original $PATH in a file.
if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
ctx.Fatalln("Failed to write original path:", err)
}
+ // Communication with the path interposer works over log entries. Set up the
+ // listener channel for the log entries here.
entries, err := paths.LogListener(ctx.Context, interposer+"_log")
if err != nil {
ctx.Fatalln("Failed to listen for path logs:", err)
}
+ // Loop over all log entry listener channels to validate usage of only
+ // allowed PATH tools at runtime.
go func() {
for log := range entries {
curPid := os.Getpid()
@@ -140,6 +168,8 @@
break
}
}
+ // Compute the error message along with the process tree, including
+ // parents, for this log line.
procPrints := []string{
"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
}
@@ -150,6 +180,7 @@
}
}
+ // Validate usage against disallowed or missing PATH tools.
config := paths.GetConfig(log.Basename)
if config.Error {
ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
@@ -165,8 +196,10 @@
}
}()
+ // Create the .path directory.
ensureEmptyDirectoriesExist(ctx, myPath)
+ // Compute the full list of binaries available in the original $PATH.
var execs []string
for _, pathEntry := range filepath.SplitList(origPath) {
if pathEntry == "" {
@@ -185,8 +218,14 @@
ctx.Fatalln("TEMPORARY_DISABLE_PATH_RESTRICTIONS was a temporary migration method, and is now obsolete.")
}
+ // Create symlinks from the path_interposer binary to all binaries for each
+ // directory in the original $PATH. This ensures that during the build,
+ // every call to a binary that's expected to be in the $PATH will be
+ // intercepted by the path_interposer binary, and validated with the
+ // LogEntry listener above at build time.
for _, name := range execs {
if !paths.GetConfig(name).Symlink {
+ // Ignore host tools that shouldn't be symlinked.
continue
}
@@ -200,11 +239,13 @@
myPath, _ = filepath.Abs(myPath)
- // We put some prebuilts in $PATH, since it's infeasible to add dependencies for all of
- // them.
+ // We put some prebuilts in $PATH, since it's infeasible to add dependencies
+ // for all of them.
prebuiltsPath, _ := filepath.Abs("prebuilts/build-tools/path/" + runtime.GOOS + "-x86")
myPath = prebuiltsPath + string(os.PathListSeparator) + myPath
+ // Replace the $PATH variable with the path_interposer symlinks, and
+ // checked-in prebuilts.
config.Environment().Set("PATH", myPath)
config.pathReplaced = true
}
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 b20237c..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")
}
@@ -133,12 +150,23 @@
"-j", strconv.Itoa(config.Parallel()),
"--frontend_file", fifo,
"-f", filepath.Join(config.SoongOutDir(), file))
+
+ // For Bazel mixed builds.
+ cmd.Environment.Set("BAZEL_PATH", "./tools/bazel")
+ 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)
@@ -146,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 a9346e0..55ca800 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -30,39 +30,63 @@
)
const (
+ // Used to generate a raw protobuf file that contains information
+ // of the list of metrics files from host to destination storage.
uploadPbFilename = ".uploader.pb"
)
var (
- // For testing purpose
- getTmpDir = ioutil.TempDir
+ // For testing purpose.
+ 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 required to be specified in order to upload the set
-// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
-// environment variable. The metrics files are copied to a temporary directory
-// and the uploader is then executed in the background to allow the user to continue
-// working.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, files ...string) {
+// 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, paths ...string) {
ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
defer ctx.EndTrace()
uploader := config.MetricsUploaderApp()
- // No metrics to upload if the path to the uploader was not specified.
if uploader == "" {
+ // If the uploader path was not specified, no metrics shall be uploaded.
return
}
- // Some files 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
}
@@ -70,7 +94,7 @@
// The temporary directory cannot be deleted as the metrics uploader is started
// in the background and requires to exist until the operation is done. The
// uploader can delete the directory as it is specified in the upload proto.
- tmpDir, err := getTmpDir("", "upload_metrics")
+ tmpDir, err := tmpDir("", "upload_metrics")
if err != nil {
ctx.Fatalf("failed to create a temporary directory to store the list of metrics files: %v\n", err)
}
@@ -103,7 +127,7 @@
}
// Start the uploader in the background as it takes several milliseconds to start the uploader
- // and prepare the metrics for upload. This affects small commands like "lunch".
+ // and prepare the metrics for upload. This affects small shell commands like "lunch".
cmd := Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile)
if simpleOutput {
cmd.RunOrFatal()
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index dccf156..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 {
@@ -62,16 +107,16 @@
}
defer os.RemoveAll(outDir)
- // Supply our own getTmpDir to delete the temp dir once the test is done.
- orgGetTmpDir := getTmpDir
- getTmpDir = func(string, string) (string, error) {
+ // Supply our own tmpDir to delete the temp dir once the test is done.
+ orgTmpDir := tmpDir
+ tmpDir = func(string, string) (string, error) {
retDir := filepath.Join(outDir, "tmp_upload_dir")
if err := os.Mkdir(retDir, 0755); err != nil {
t.Fatalf("failed to create temporary directory %q: %v", retDir, err)
}
return retDir, nil
}
- defer func() { getTmpDir = orgGetTmpDir }()
+ defer func() { tmpDir = orgTmpDir }()
metricsUploadDir := filepath.Join(outDir, ".metrics_uploader")
if err := os.Mkdir(metricsUploadDir, 0755); err != nil {
@@ -134,11 +179,11 @@
}
defer os.RemoveAll(outDir)
- orgGetTmpDir := getTmpDir
- getTmpDir = func(string, string) (string, error) {
+ orgTmpDir := tmpDir
+ tmpDir = func(string, string) (string, error) {
return tt.tmpDir, tt.tmpDirErr
}
- defer func() { getTmpDir = orgGetTmpDir }()
+ defer func() { tmpDir = orgTmpDir }()
metricsFile := filepath.Join(outDir, "metrics_file_1")
if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {
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 9434924..efb572c 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -12,8 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Package metrics represents the metrics system for Android Platform Build Systems.
package metrics
+// This is the main heart of the metrics system for Android Platform Build Systems.
+// The starting of the soong_ui (cmd/soong_ui/main.go), the metrics system is
+// initialized by the invocation of New and is then stored in the context
+// (ui/build/context.go) to be used throughout the system. During the build
+// initialization phase, several functions in this file are invoked to store
+// information such as the environment, build configuration and build metadata.
+// There are several scoped code that has Begin() and defer End() functions
+// that captures the metrics and is them added as a perfInfo into the set
+// of the collected metrics. Finally, when soong_ui has finished the build,
+// the defer Dump function is invoked to store the collected metrics to the
+// raw protobuf file in the $OUT directory.
+//
+// There is one additional step that occurs after the raw protobuf file is written.
+// If the configuration environment variable ANDROID_ENABLE_METRICS_UPLOAD is
+// set with the path, the raw protobuf file is uploaded to the destination. See
+// ui/build/upload.go for more details. The filename of the raw protobuf file
+// and the list of files to be uploaded is defined in cmd/soong_ui/main.go.
+//
+// See ui/metrics/event.go for the explanation of what an event is and how
+// the metrics system is a stack based system.
+
import (
"io/ioutil"
"os"
@@ -23,70 +45,90 @@
"github.com/golang/protobuf/proto"
- soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+ "android/soong/ui/metrics/metrics_proto"
)
const (
- PrimaryNinja = "ninja"
- RunKati = "kati"
+ // Below is a list of names passed in to the Begin tracing functions. These
+ // names are used to group a set of metrics.
+
+ // Setup and tear down of the build systems.
RunSetupTool = "setup"
RunShutdownTool = "shutdown"
- RunSoong = "soong"
TestRun = "test"
- Total = "total"
+
+ // List of build system tools.
+ RunSoong = "soong"
+ PrimaryNinja = "ninja"
+ RunKati = "kati"
+ RunBazel = "bazel"
+
+ // Overall build from building the graph to building the target.
+ Total = "total"
)
+// Metrics is a struct that stores collected metrics during the course
+// of a build which later is dumped to a MetricsBase protobuf file.
+// See ui/metrics/metrics_proto/metrics.proto for further details
+// on what information is collected.
type Metrics struct {
- metrics soong_metrics_proto.MetricsBase
- EventTracer EventTracer
+ // The protobuf message that is later written to the file.
+ metrics soong_metrics_proto.MetricsBase
+
+ // A list of pending build events.
+ EventTracer *EventTracer
}
+// New returns a pointer of Metrics to store a set of metrics.
func New() (metrics *Metrics) {
m := &Metrics{
metrics: soong_metrics_proto.MetricsBase{},
- EventTracer: &eventTracerImpl{},
+ EventTracer: &EventTracer{},
}
return m
}
+// SetTimeMetrics stores performance information from an executed block of
+// code.
func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
switch perf.GetName() {
case RunKati:
m.metrics.KatiRuns = append(m.metrics.KatiRuns, &perf)
- break
case RunSoong:
m.metrics.SoongRuns = append(m.metrics.SoongRuns, &perf)
- break
+ case RunBazel:
+ m.metrics.BazelRuns = append(m.metrics.BazelRuns, &perf)
case PrimaryNinja:
m.metrics.NinjaRuns = append(m.metrics.NinjaRuns, &perf)
- break
+ case RunSetupTool:
+ m.metrics.SetupTools = append(m.metrics.SetupTools, &perf)
case Total:
m.metrics.Total = &perf
- default:
- // ignored
}
}
+// BuildConfig stores information about the build configuration.
func (m *Metrics) BuildConfig(b *soong_metrics_proto.BuildConfig) {
m.metrics.BuildConfig = b
}
+// SystemResourceInfo stores information related to the host system such
+// as total CPU and memory.
func (m *Metrics) SystemResourceInfo(b *soong_metrics_proto.SystemResourceInfo) {
m.metrics.SystemResourceInfo = b
}
+// SetMetadataMetrics sets information about the build such as the target
+// product, host architecture and out directory.
func (m *Metrics) SetMetadataMetrics(metadata map[string]string) {
for k, v := range metadata {
switch k {
case "BUILD_ID":
m.metrics.BuildId = proto.String(v)
- break
case "PLATFORM_VERSION_CODENAME":
m.metrics.PlatformVersionCodename = proto.String(v)
- break
case "TARGET_PRODUCT":
m.metrics.TargetProduct = proto.String(v)
- break
case "TARGET_BUILD_VARIANT":
switch v {
case "user":
@@ -95,19 +137,17 @@
m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_USERDEBUG.Enum()
case "eng":
m.metrics.TargetBuildVariant = soong_metrics_proto.MetricsBase_ENG.Enum()
- default:
- // ignored
}
case "TARGET_ARCH":
- m.metrics.TargetArch = m.getArch(v)
+ m.metrics.TargetArch = arch(v)
case "TARGET_ARCH_VARIANT":
m.metrics.TargetArchVariant = proto.String(v)
case "TARGET_CPU_VARIANT":
m.metrics.TargetCpuVariant = proto.String(v)
case "HOST_ARCH":
- m.metrics.HostArch = m.getArch(v)
+ m.metrics.HostArch = arch(v)
case "HOST_2ND_ARCH":
- m.metrics.Host_2NdArch = m.getArch(v)
+ m.metrics.Host_2NdArch = arch(v)
case "HOST_OS_EXTRA":
m.metrics.HostOsExtra = proto.String(v)
case "HOST_CROSS_OS":
@@ -118,14 +158,14 @@
m.metrics.HostCross_2NdArch = proto.String(v)
case "OUT_DIR":
m.metrics.OutDir = proto.String(v)
- default:
- // ignored
}
}
}
-func (m *Metrics) getArch(arch string) *soong_metrics_proto.MetricsBase_Arch {
- switch arch {
+// arch returns the corresponding MetricsBase_Arch based on the string
+// parameter.
+func arch(a string) *soong_metrics_proto.MetricsBase_Arch {
+ switch a {
case "arm":
return soong_metrics_proto.MetricsBase_ARM.Enum()
case "arm64":
@@ -139,37 +179,51 @@
}
}
+// SetBuildDateTime sets the build date and time. The value written
+// to the protobuf file is in seconds.
func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
}
+// SetBuildCommand adds the build command specified by the user to the
+// list of collected metrics.
func (m *Metrics) SetBuildCommand(cmd []string) {
m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
}
-// exports the output to the file at outputPath
-func (m *Metrics) Dump(outputPath string) error {
+// Dump exports the collected metrics from the executed build to the file at
+// out path.
+func (m *Metrics) Dump(out string) error {
// ignore the error if the hostname could not be retrieved as it
// is not a critical metric to extract.
if hostname, err := os.Hostname(); err == nil {
m.metrics.Hostname = proto.String(hostname)
}
m.metrics.HostOs = proto.String(runtime.GOOS)
- return writeMessageToFile(&m.metrics, outputPath)
+
+ return save(&m.metrics, out)
}
+// SetSoongBuildMetrics sets the metrics collected from the soong_build
+// execution.
func (m *Metrics) SetSoongBuildMetrics(metrics *soong_metrics_proto.SoongBuildMetrics) {
m.metrics.SoongBuildMetrics = metrics
}
+// A CriticalUserJourneysMetrics is a struct that contains critical user journey
+// metrics. These critical user journeys are defined under cuj/cuj.go file.
type CriticalUserJourneysMetrics struct {
+ // A list of collected CUJ metrics.
cujs soong_metrics_proto.CriticalUserJourneysMetrics
}
+// NewCriticalUserJourneyMetrics returns a pointer of CriticalUserJourneyMetrics
+// to capture CUJs metrics.
func NewCriticalUserJourneysMetrics() *CriticalUserJourneysMetrics {
return &CriticalUserJourneysMetrics{}
}
+// Add adds a set of collected metrics from an executed critical user journey.
func (c *CriticalUserJourneysMetrics) Add(name string, metrics *Metrics) {
c.cujs.Cujs = append(c.cujs.Cujs, &soong_metrics_proto.CriticalUserJourneyMetrics{
Name: proto.String(name),
@@ -177,22 +231,25 @@
})
}
-func (c *CriticalUserJourneysMetrics) Dump(outputPath string) (err error) {
- return writeMessageToFile(&c.cujs, outputPath)
+// Dump saves the collected CUJs metrics to the raw protobuf file.
+func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) {
+ return save(&c.cujs, filename)
}
-func writeMessageToFile(pb proto.Message, outputPath string) (err error) {
+// save takes a protobuf message, marshals to an array of bytes
+// and is then saved to a file.
+func save(pb proto.Message, filename string) (err error) {
data, err := proto.Marshal(pb)
if err != nil {
return err
}
- tempPath := outputPath + ".tmp"
- err = ioutil.WriteFile(tempPath, []byte(data), 0644)
- if err != nil {
+
+ tempFilename := filename + ".tmp"
+ if err := ioutil.WriteFile(tempFilename, []byte(data), 0644 /* rw-r--r-- */); err != nil {
return err
}
- err = os.Rename(tempPath, outputPath)
- if err != nil {
+
+ if err := os.Rename(tempFilename, filename); err != nil {
return err
}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 5db5ae5..fc2cfa4 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -205,10 +205,12 @@
// The system resource information such as total physical memory.
SystemResourceInfo *SystemResourceInfo `protobuf:"bytes,25,opt,name=system_resource_info,json=systemResourceInfo" json:"system_resource_info,omitempty"`
// The build command that the user entered to the build system.
- BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
- XXX_NoUnkeyedLiteral struct{} `json:"-"`
- XXX_unrecognized []byte `json:"-"`
- XXX_sizecache int32 `json:"-"`
+ BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
+ // The metrics for calling Bazel.
+ BazelRuns []*PerfInfo `protobuf:"bytes,27,rep,name=bazel_runs,json=bazelRuns" json:"bazel_runs,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
}
func (m *MetricsBase) Reset() { *m = MetricsBase{} }
@@ -423,6 +425,13 @@
return ""
}
+func (m *MetricsBase) GetBazelRuns() []*PerfInfo {
+ if m != nil {
+ return m.BazelRuns
+ }
+ return nil
+}
+
type BuildConfig struct {
UseGoma *bool `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
UseRbe *bool `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
@@ -981,91 +990,92 @@
}
var fileDescriptor_6039342a2ba47b72 = []byte{
- // 1366 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x52, 0x1b, 0xc7,
- 0x12, 0xb6, 0x40, 0x20, 0xa9, 0xf5, 0x83, 0x18, 0xe0, 0xb0, 0xc6, 0xf6, 0x39, 0x1c, 0x25, 0x76,
- 0xa8, 0x54, 0x8c, 0x5d, 0xc4, 0x45, 0xb9, 0x28, 0x57, 0x2a, 0x20, 0x13, 0xc7, 0xa1, 0x40, 0xd4,
- 0x60, 0x1c, 0x27, 0xb9, 0x98, 0x8c, 0x56, 0x23, 0x58, 0x7b, 0x77, 0x67, 0x6b, 0x66, 0x96, 0x80,
- 0x1f, 0x25, 0x6f, 0x92, 0xeb, 0x3c, 0x4b, 0x9e, 0x20, 0x2f, 0x90, 0x9a, 0x9e, 0xdd, 0x65, 0xc1,
- 0x8a, 0xed, 0xf2, 0xdd, 0xee, 0xd7, 0xdf, 0xd7, 0xd3, 0xdd, 0x3b, 0xdd, 0x2d, 0x41, 0x3b, 0x12,
- 0x46, 0x05, 0xbe, 0x5e, 0x4f, 0x94, 0x34, 0x92, 0x2c, 0x68, 0x29, 0xe3, 0x13, 0x36, 0x4c, 0x83,
- 0x70, 0xc4, 0x32, 0x53, 0xef, 0xf7, 0x16, 0x34, 0xf7, 0xdd, 0xf3, 0x0e, 0xd7, 0x82, 0x3c, 0x84,
- 0x45, 0x47, 0x18, 0x71, 0x23, 0x98, 0x09, 0x22, 0xa1, 0x0d, 0x8f, 0x12, 0xaf, 0xb2, 0x5a, 0x59,
- 0x9b, 0xa6, 0x04, 0x6d, 0x4f, 0xb9, 0x11, 0x2f, 0x72, 0x0b, 0xb9, 0x09, 0x75, 0xa7, 0x08, 0x46,
- 0xde, 0xd4, 0x6a, 0x65, 0xad, 0x41, 0x6b, 0xf8, 0xfe, 0x7c, 0x44, 0xb6, 0xe0, 0x66, 0x12, 0x72,
- 0x33, 0x96, 0x2a, 0x62, 0x67, 0x42, 0xe9, 0x40, 0xc6, 0xcc, 0x97, 0x23, 0x11, 0xf3, 0x48, 0x78,
- 0xd3, 0xc8, 0x5d, 0xce, 0x09, 0x2f, 0x9d, 0xbd, 0x9f, 0x99, 0xc9, 0x5d, 0xe8, 0x18, 0xae, 0x4e,
- 0x84, 0x61, 0x89, 0x92, 0xa3, 0xd4, 0x37, 0x5e, 0x15, 0x05, 0x6d, 0x87, 0x1e, 0x3a, 0x90, 0x8c,
- 0x60, 0x31, 0xa3, 0xb9, 0x20, 0xce, 0xb8, 0x0a, 0x78, 0x6c, 0xbc, 0x99, 0xd5, 0xca, 0x5a, 0x67,
- 0xe3, 0xfe, 0xfa, 0x84, 0x9c, 0xd7, 0x4b, 0xf9, 0xae, 0xef, 0x58, 0xcb, 0x4b, 0x27, 0xda, 0x9a,
- 0xde, 0x3d, 0x78, 0x46, 0x89, 0xf3, 0x57, 0x36, 0x90, 0x01, 0x34, 0xb3, 0x53, 0xb8, 0xf2, 0x4f,
- 0xbd, 0x59, 0x74, 0x7e, 0xf7, 0x83, 0xce, 0xb7, 0x95, 0x7f, 0xba, 0x55, 0x3b, 0x3e, 0xd8, 0x3b,
- 0x18, 0xfc, 0x78, 0x40, 0xc1, 0xb9, 0xb0, 0x20, 0x59, 0x87, 0x85, 0x92, 0xc3, 0x22, 0xea, 0x1a,
- 0xa6, 0x38, 0x7f, 0x49, 0xcc, 0x03, 0xf8, 0x0a, 0xb2, 0xb0, 0x98, 0x9f, 0xa4, 0x05, 0xbd, 0x8e,
- 0xf4, 0xae, 0xb3, 0xf4, 0x93, 0x34, 0x67, 0xef, 0x41, 0xe3, 0x54, 0xea, 0x2c, 0xd8, 0xc6, 0x27,
- 0x05, 0x5b, 0xb7, 0x0e, 0x30, 0x54, 0x0a, 0x6d, 0x74, 0xb6, 0x11, 0x8f, 0x9c, 0x43, 0xf8, 0x24,
- 0x87, 0x4d, 0xeb, 0x64, 0x23, 0x1e, 0xa1, 0xcf, 0x65, 0xa8, 0xa1, 0x4f, 0xa9, 0xbd, 0x26, 0xe6,
- 0x30, 0x6b, 0x5f, 0x07, 0x9a, 0xf4, 0xb2, 0xc3, 0xa4, 0x66, 0xe2, 0xdc, 0x28, 0xee, 0xb5, 0xd0,
- 0xdc, 0x74, 0xe6, 0x5d, 0x0b, 0x15, 0x1c, 0x5f, 0x49, 0xad, 0xad, 0x8b, 0xf6, 0x25, 0xa7, 0x6f,
- 0xb1, 0x81, 0x26, 0xf7, 0x60, 0xae, 0xc4, 0xc1, 0xb0, 0x3b, 0xee, 0xfa, 0x14, 0x2c, 0x0c, 0xe4,
- 0x3e, 0x2c, 0x94, 0x78, 0x45, 0x8a, 0x73, 0xae, 0xb0, 0x05, 0xb7, 0x14, 0xb7, 0x4c, 0x0d, 0x1b,
- 0x05, 0xca, 0xeb, 0xba, 0xb8, 0x65, 0x6a, 0x9e, 0x06, 0x8a, 0x7c, 0x03, 0x4d, 0x2d, 0x4c, 0x9a,
- 0x30, 0x23, 0x65, 0xa8, 0xbd, 0xf9, 0xd5, 0xe9, 0xb5, 0xe6, 0xc6, 0x9d, 0x89, 0x25, 0x3a, 0x14,
- 0x6a, 0xfc, 0x3c, 0x1e, 0x4b, 0x0a, 0xa8, 0x78, 0x61, 0x05, 0x64, 0x0b, 0x1a, 0x6f, 0xb8, 0x09,
- 0x98, 0x4a, 0x63, 0xed, 0x91, 0x8f, 0x51, 0xd7, 0x2d, 0x9f, 0xa6, 0xb1, 0x26, 0x4f, 0x00, 0x1c,
- 0x13, 0xc5, 0x0b, 0x1f, 0x23, 0x6e, 0xa0, 0x35, 0x57, 0xc7, 0x41, 0xfc, 0x9a, 0x3b, 0xf5, 0xe2,
- 0x47, 0xa9, 0x51, 0x80, 0xea, 0xaf, 0x61, 0xc6, 0x48, 0xc3, 0x43, 0x6f, 0x69, 0xb5, 0xf2, 0x61,
- 0xa1, 0xe3, 0x92, 0x97, 0x30, 0x69, 0x14, 0x79, 0xff, 0x41, 0x17, 0xf7, 0x26, 0xba, 0x38, 0xb2,
- 0x18, 0xb6, 0x64, 0x76, 0xc3, 0xe8, 0xbc, 0xbe, 0x0e, 0x91, 0x3e, 0xb4, 0x9c, 0xca, 0x97, 0xf1,
- 0x38, 0x38, 0xf1, 0x96, 0xd1, 0xe1, 0xea, 0x44, 0x87, 0x28, 0xec, 0x23, 0x8f, 0x36, 0x87, 0x97,
- 0x2f, 0x64, 0x05, 0xf0, 0xea, 0xe3, 0x88, 0xf2, 0xf0, 0x1b, 0x17, 0xef, 0xe4, 0x27, 0x58, 0xd4,
- 0x17, 0xda, 0x88, 0x88, 0x29, 0xa1, 0x65, 0xaa, 0x7c, 0xc1, 0x82, 0x78, 0x2c, 0xbd, 0x9b, 0x78,
- 0xd0, 0x17, 0x93, 0x23, 0x47, 0x01, 0xcd, 0xf8, 0x58, 0x06, 0xa2, 0xdf, 0xc1, 0xc8, 0x67, 0xd0,
- 0xce, 0x63, 0x8f, 0x22, 0x1e, 0x8f, 0xbc, 0x15, 0x3c, 0xbb, 0x95, 0x85, 0x86, 0x58, 0xef, 0x21,
- 0xb4, 0xae, 0x8c, 0xa5, 0x3a, 0x54, 0x8f, 0x8f, 0x76, 0x69, 0xf7, 0x06, 0x69, 0x43, 0xc3, 0x3e,
- 0x3d, 0xdd, 0xdd, 0x39, 0x7e, 0xd6, 0xad, 0x90, 0x1a, 0xd8, 0x51, 0xd6, 0x9d, 0xea, 0x3d, 0x81,
- 0x2a, 0x5e, 0xdc, 0x26, 0xe4, 0x8d, 0xd8, 0xbd, 0x61, 0xad, 0xdb, 0x74, 0xbf, 0x5b, 0x21, 0x0d,
- 0x98, 0xd9, 0xa6, 0xfb, 0x9b, 0x8f, 0xba, 0x53, 0x16, 0x7b, 0xf5, 0x78, 0xb3, 0x3b, 0x4d, 0x00,
- 0x66, 0x5f, 0x3d, 0xde, 0x64, 0x9b, 0x8f, 0xba, 0xd5, 0xde, 0x09, 0x34, 0x4b, 0x75, 0xb2, 0x93,
- 0x3e, 0xd5, 0x82, 0x9d, 0xc8, 0x88, 0xe3, 0x3e, 0xa8, 0xd3, 0x5a, 0xaa, 0xc5, 0x33, 0x19, 0x71,
- 0xdb, 0x18, 0xd6, 0xa4, 0x86, 0x02, 0x77, 0x40, 0x9d, 0xce, 0xa6, 0x5a, 0xd0, 0xa1, 0x20, 0x9f,
- 0x43, 0x67, 0x2c, 0x6d, 0xa1, 0x0a, 0xe5, 0x34, 0xda, 0x5b, 0x88, 0x1e, 0x3b, 0x79, 0x4f, 0x02,
- 0x79, 0xb7, 0x4e, 0x64, 0x03, 0x96, 0xf0, 0xc2, 0xb0, 0xe4, 0xf4, 0x42, 0x07, 0x3e, 0x0f, 0x59,
- 0x24, 0x22, 0xa9, 0x2e, 0xf0, 0xf0, 0x2a, 0x5d, 0x40, 0xe3, 0x61, 0x66, 0xdb, 0x47, 0x93, 0x5d,
- 0x1b, 0xfc, 0x8c, 0x07, 0x21, 0x1f, 0x86, 0xc2, 0xce, 0x4a, 0x8d, 0xf1, 0xcc, 0xd0, 0x76, 0x81,
- 0xf6, 0x93, 0x54, 0xf7, 0xfe, 0xae, 0x40, 0x3d, 0xbf, 0x96, 0x84, 0x40, 0x75, 0x24, 0xb4, 0x8f,
- 0x6e, 0x1b, 0x14, 0x9f, 0x2d, 0x86, 0x57, 0xc0, 0x6d, 0x34, 0x7c, 0x26, 0x77, 0x00, 0xb4, 0xe1,
- 0xca, 0xe0, 0x5a, 0xc4, 0x3c, 0xaa, 0xb4, 0x81, 0x88, 0xdd, 0x86, 0xe4, 0x16, 0x34, 0x94, 0xe0,
- 0xa1, 0xb3, 0x56, 0xd1, 0x5a, 0xb7, 0x00, 0x1a, 0xff, 0x0f, 0xe0, 0x82, 0xb7, 0x85, 0xc0, 0xed,
- 0x54, 0xdd, 0x99, 0xf2, 0x2a, 0xb4, 0xe1, 0xd0, 0x63, 0x2d, 0xc8, 0xaf, 0xb0, 0x9c, 0x28, 0xe9,
- 0x0b, 0xad, 0x85, 0xbe, 0x76, 0xc1, 0x66, 0xb1, 0x2d, 0xd7, 0x26, 0x77, 0x97, 0xd3, 0x5c, 0xb9,
- 0x61, 0x4b, 0x85, 0xa3, 0x32, 0xdc, 0xfb, 0x63, 0x1a, 0x16, 0x26, 0xd0, 0x8b, 0x64, 0x2b, 0xa5,
- 0x64, 0xd7, 0xa0, 0x9b, 0x6a, 0xa1, 0x30, 0x1b, 0x16, 0x05, 0x76, 0x40, 0x62, 0x31, 0xaa, 0xb4,
- 0x63, 0x71, 0x9b, 0xd4, 0x3e, 0xa2, 0x76, 0x37, 0x65, 0x5d, 0x51, 0xe6, 0xba, 0xf2, 0x74, 0x9d,
- 0xa5, 0xc4, 0xbe, 0x0d, 0x10, 0xf1, 0x73, 0xa6, 0xb4, 0x66, 0x6f, 0x86, 0x79, 0x99, 0x22, 0x7e,
- 0x4e, 0xb5, 0xde, 0x1b, 0x92, 0x2f, 0x61, 0x3e, 0x0a, 0x62, 0xa9, 0x58, 0xc2, 0x4f, 0x04, 0x1b,
- 0xf3, 0x34, 0x34, 0xda, 0x55, 0x8b, 0xce, 0xa1, 0xe1, 0x90, 0x9f, 0x88, 0xef, 0x10, 0x46, 0x2e,
- 0x7f, 0x7d, 0x8d, 0x3b, 0x9b, 0x71, 0xad, 0xa1, 0xc4, 0xfd, 0x2f, 0x34, 0x03, 0xc9, 0x82, 0x38,
- 0x49, 0x8d, 0x3d, 0xb6, 0xe6, 0xbe, 0x5d, 0x20, 0x9f, 0x5b, 0x64, 0x6f, 0x48, 0x56, 0xa1, 0x15,
- 0x48, 0x26, 0x53, 0x93, 0x11, 0xea, 0x48, 0x80, 0x40, 0x0e, 0x10, 0xda, 0x1b, 0x92, 0x27, 0xb0,
- 0x72, 0x26, 0xc3, 0x34, 0x36, 0x5c, 0x5d, 0xd8, 0x01, 0x63, 0xc4, 0xb9, 0x61, 0xfa, 0xb7, 0xc0,
- 0xf8, 0xa7, 0x42, 0xe3, 0x92, 0xad, 0x52, 0xaf, 0x60, 0xf4, 0x1d, 0xe1, 0x28, 0xb3, 0x93, 0x6f,
- 0xe1, 0x76, 0x10, 0xbf, 0x47, 0x0f, 0xa8, 0x5f, 0x29, 0x71, 0xae, 0x79, 0xe8, 0xfd, 0x55, 0x81,
- 0xce, 0xbe, 0x1c, 0xa5, 0xa1, 0x78, 0x71, 0x91, 0xb8, 0xcf, 0xf6, 0x4b, 0x3e, 0xef, 0x5c, 0x91,
- 0xf1, 0xf3, 0x75, 0x36, 0x1e, 0x4c, 0x5e, 0xcc, 0x57, 0xa4, 0x6e, 0xfc, 0xb9, 0x96, 0x2b, 0xad,
- 0xe8, 0xe1, 0x25, 0x4a, 0xfe, 0x07, 0xcd, 0x08, 0x35, 0xcc, 0x5c, 0x24, 0x79, 0x1f, 0x40, 0x54,
- 0xb8, 0xb1, 0x9d, 0x1d, 0xa7, 0x11, 0x93, 0x63, 0xe6, 0x40, 0xf7, 0xc9, 0xdb, 0xb4, 0x15, 0xa7,
- 0xd1, 0x60, 0xec, 0xce, 0xd3, 0xbd, 0x07, 0xd9, 0x08, 0xc9, 0xbc, 0x5e, 0x99, 0x43, 0x0d, 0x98,
- 0x39, 0x1a, 0x0c, 0x0e, 0xec, 0xc0, 0xaa, 0x43, 0x75, 0x7f, 0x7b, 0x6f, 0xb7, 0x3b, 0xd5, 0x0b,
- 0x61, 0xa5, 0xaf, 0x02, 0x63, 0x5b, 0xfa, 0x58, 0x0b, 0xf5, 0x83, 0x4c, 0x55, 0x2c, 0x2e, 0xf2,
- 0x11, 0x3f, 0xe9, 0xa6, 0x6e, 0x41, 0x2d, 0x5f, 0x21, 0x53, 0xef, 0x99, 0xf8, 0xa5, 0x9f, 0x26,
- 0x34, 0x17, 0xf4, 0x86, 0x70, 0x6b, 0xc2, 0x69, 0xfa, 0x72, 0xa3, 0x54, 0xfd, 0xf4, 0xb5, 0xf6,
- 0x2a, 0xd8, 0x7f, 0x93, 0x2b, 0xfb, 0xef, 0xd1, 0x52, 0x14, 0xf7, 0xfe, 0xac, 0xc0, 0xfc, 0x3b,
- 0xfb, 0x8b, 0x78, 0x50, 0xcb, 0xeb, 0x56, 0xc1, 0xba, 0xe5, 0xaf, 0x76, 0x03, 0x65, 0x3f, 0xf0,
- 0x5c, 0x42, 0x6d, 0x5a, 0xbc, 0xdb, 0x3b, 0xef, 0x46, 0x22, 0x0f, 0x43, 0xe9, 0x33, 0x5f, 0xa6,
- 0xb1, 0xc9, 0x5a, 0x6d, 0x0e, 0x0d, 0xdb, 0x16, 0xef, 0x5b, 0xd8, 0x76, 0x70, 0x99, 0xab, 0x83,
- 0xb7, 0xf9, 0x58, 0xea, 0x5c, 0x52, 0x8f, 0x82, 0xb7, 0xc2, 0xfe, 0xa2, 0xb2, 0x3d, 0x79, 0x2a,
- 0x78, 0xe2, 0x68, 0xae, 0xe3, 0x9a, 0x11, 0x3f, 0xff, 0x5e, 0xf0, 0xc4, 0x72, 0x76, 0x96, 0x7e,
- 0xce, 0x96, 0x76, 0x96, 0x37, 0xc3, 0x3f, 0x15, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x86,
- 0x34, 0xa9, 0x64, 0x0c, 0x00, 0x00,
+ // 1380 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xef, 0x52, 0x1b, 0x37,
+ 0x10, 0x8f, 0xc1, 0x60, 0x7b, 0xfd, 0x07, 0x23, 0xa0, 0x5c, 0x48, 0xd2, 0x52, 0xb7, 0x49, 0x99,
+ 0x4e, 0x43, 0x32, 0x34, 0xc3, 0x64, 0x98, 0x4c, 0xa7, 0xe0, 0xd0, 0x34, 0x65, 0xc0, 0x8c, 0x08,
+ 0x69, 0xda, 0x7e, 0x50, 0xe5, 0xb3, 0x0c, 0x97, 0xdc, 0x9d, 0x6e, 0x24, 0x1d, 0xc5, 0x79, 0xb3,
+ 0x7e, 0xee, 0x4b, 0xf4, 0x05, 0xfa, 0x04, 0x7d, 0x81, 0x8e, 0x56, 0x77, 0xe6, 0x20, 0x6e, 0xc2,
+ 0xe4, 0xdb, 0xe9, 0xb7, 0xbf, 0xdf, 0x6a, 0xb5, 0xd2, 0xee, 0xda, 0xd0, 0x8c, 0x84, 0x51, 0x81,
+ 0xaf, 0xd7, 0x13, 0x25, 0x8d, 0x24, 0x0b, 0x5a, 0xca, 0xf8, 0x84, 0xf5, 0xd3, 0x20, 0x1c, 0xb0,
+ 0xcc, 0xd4, 0xf9, 0xbb, 0x01, 0xf5, 0x7d, 0xf7, 0xbd, 0xc3, 0xb5, 0x20, 0x0f, 0x61, 0xd1, 0x11,
+ 0x06, 0xdc, 0x08, 0x66, 0x82, 0x48, 0x68, 0xc3, 0xa3, 0xc4, 0x2b, 0xad, 0x96, 0xd6, 0xa6, 0x29,
+ 0x41, 0xdb, 0x53, 0x6e, 0xc4, 0x8b, 0xdc, 0x42, 0x6e, 0x42, 0xd5, 0x29, 0x82, 0x81, 0x37, 0xb5,
+ 0x5a, 0x5a, 0xab, 0xd1, 0x0a, 0xae, 0x9f, 0x0f, 0xc8, 0x16, 0xdc, 0x4c, 0x42, 0x6e, 0x86, 0x52,
+ 0x45, 0xec, 0x4c, 0x28, 0x1d, 0xc8, 0x98, 0xf9, 0x72, 0x20, 0x62, 0x1e, 0x09, 0x6f, 0x1a, 0xb9,
+ 0xcb, 0x39, 0xe1, 0xa5, 0xb3, 0x77, 0x33, 0x33, 0xb9, 0x0b, 0x2d, 0xc3, 0xd5, 0x89, 0x30, 0x2c,
+ 0x51, 0x72, 0x90, 0xfa, 0xc6, 0x2b, 0xa3, 0xa0, 0xe9, 0xd0, 0x43, 0x07, 0x92, 0x01, 0x2c, 0x66,
+ 0x34, 0x17, 0xc4, 0x19, 0x57, 0x01, 0x8f, 0x8d, 0x37, 0xb3, 0x5a, 0x5a, 0x6b, 0x6d, 0xdc, 0x5f,
+ 0x9f, 0x70, 0xe6, 0xf5, 0xc2, 0x79, 0xd7, 0x77, 0xac, 0xe5, 0xa5, 0x13, 0x6d, 0x4d, 0xef, 0x1e,
+ 0x3c, 0xa3, 0xc4, 0xf9, 0x2b, 0x1a, 0x48, 0x0f, 0xea, 0xd9, 0x2e, 0x5c, 0xf9, 0xa7, 0xde, 0x2c,
+ 0x3a, 0xbf, 0xfb, 0x41, 0xe7, 0xdb, 0xca, 0x3f, 0xdd, 0xaa, 0x1c, 0x1f, 0xec, 0x1d, 0xf4, 0x7e,
+ 0x3e, 0xa0, 0xe0, 0x5c, 0x58, 0x90, 0xac, 0xc3, 0x42, 0xc1, 0xe1, 0x38, 0xea, 0x0a, 0x1e, 0x71,
+ 0xfe, 0x82, 0x98, 0x07, 0xf0, 0x0d, 0x64, 0x61, 0x31, 0x3f, 0x49, 0xc7, 0xf4, 0x2a, 0xd2, 0xdb,
+ 0xce, 0xd2, 0x4d, 0xd2, 0x9c, 0xbd, 0x07, 0xb5, 0x53, 0xa9, 0xb3, 0x60, 0x6b, 0x1f, 0x15, 0x6c,
+ 0xd5, 0x3a, 0xc0, 0x50, 0x29, 0x34, 0xd1, 0xd9, 0x46, 0x3c, 0x70, 0x0e, 0xe1, 0xa3, 0x1c, 0xd6,
+ 0xad, 0x93, 0x8d, 0x78, 0x80, 0x3e, 0x97, 0xa1, 0x82, 0x3e, 0xa5, 0xf6, 0xea, 0x78, 0x86, 0x59,
+ 0xbb, 0xec, 0x69, 0xd2, 0xc9, 0x36, 0x93, 0x9a, 0x89, 0x73, 0xa3, 0xb8, 0xd7, 0x40, 0x73, 0xdd,
+ 0x99, 0x77, 0x2d, 0x34, 0xe6, 0xf8, 0x4a, 0x6a, 0x6d, 0x5d, 0x34, 0x2f, 0x38, 0x5d, 0x8b, 0xf5,
+ 0x34, 0xb9, 0x07, 0x73, 0x05, 0x0e, 0x86, 0xdd, 0x72, 0xcf, 0x67, 0xcc, 0xc2, 0x40, 0xee, 0xc3,
+ 0x42, 0x81, 0x37, 0x3e, 0xe2, 0x9c, 0x4b, 0xec, 0x98, 0x5b, 0x88, 0x5b, 0xa6, 0x86, 0x0d, 0x02,
+ 0xe5, 0xb5, 0x5d, 0xdc, 0x32, 0x35, 0x4f, 0x03, 0x45, 0xbe, 0x83, 0xba, 0x16, 0x26, 0x4d, 0x98,
+ 0x91, 0x32, 0xd4, 0xde, 0xfc, 0xea, 0xf4, 0x5a, 0x7d, 0xe3, 0xce, 0xc4, 0x14, 0x1d, 0x0a, 0x35,
+ 0x7c, 0x1e, 0x0f, 0x25, 0x05, 0x54, 0xbc, 0xb0, 0x02, 0xb2, 0x05, 0xb5, 0x37, 0xdc, 0x04, 0x4c,
+ 0xa5, 0xb1, 0xf6, 0xc8, 0x75, 0xd4, 0x55, 0xcb, 0xa7, 0x69, 0xac, 0xc9, 0x13, 0x00, 0xc7, 0x44,
+ 0xf1, 0xc2, 0x75, 0xc4, 0x35, 0xb4, 0xe6, 0xea, 0x38, 0x88, 0x5f, 0x73, 0xa7, 0x5e, 0xbc, 0x96,
+ 0x1a, 0x05, 0xa8, 0xfe, 0x16, 0x66, 0x8c, 0x34, 0x3c, 0xf4, 0x96, 0x56, 0x4b, 0x1f, 0x16, 0x3a,
+ 0x2e, 0x79, 0x09, 0x93, 0x5a, 0x91, 0xf7, 0x09, 0xba, 0xb8, 0x37, 0xd1, 0xc5, 0x91, 0xc5, 0xb0,
+ 0x24, 0xb3, 0x17, 0x46, 0xe7, 0xf5, 0x55, 0x88, 0x74, 0xa1, 0xe1, 0x54, 0xbe, 0x8c, 0x87, 0xc1,
+ 0x89, 0xb7, 0x8c, 0x0e, 0x57, 0x27, 0x3a, 0x44, 0x61, 0x17, 0x79, 0xb4, 0xde, 0xbf, 0x58, 0x90,
+ 0x15, 0xc0, 0xa7, 0x8f, 0x2d, 0xca, 0xc3, 0x3b, 0x1e, 0xaf, 0xc9, 0x2f, 0xb0, 0xa8, 0x47, 0xda,
+ 0x88, 0x88, 0x29, 0xa1, 0x65, 0xaa, 0x7c, 0xc1, 0x82, 0x78, 0x28, 0xbd, 0x9b, 0xb8, 0xd1, 0x57,
+ 0x93, 0x23, 0x47, 0x01, 0xcd, 0xf8, 0x98, 0x06, 0xa2, 0xdf, 0xc1, 0xc8, 0x17, 0xd0, 0xcc, 0x63,
+ 0x8f, 0x22, 0x1e, 0x0f, 0xbc, 0x15, 0xdc, 0xbb, 0x91, 0x85, 0x86, 0x98, 0xbd, 0xab, 0x3e, 0x7f,
+ 0x2b, 0x42, 0x77, 0x57, 0xb7, 0xae, 0x75, 0x57, 0x28, 0xb0, 0x77, 0xd5, 0x79, 0x08, 0x8d, 0x4b,
+ 0x4d, 0xad, 0x0a, 0xe5, 0xe3, 0xa3, 0x5d, 0xda, 0xbe, 0x41, 0x9a, 0x50, 0xb3, 0x5f, 0x4f, 0x77,
+ 0x77, 0x8e, 0x9f, 0xb5, 0x4b, 0xa4, 0x02, 0xb6, 0x11, 0xb6, 0xa7, 0x3a, 0x4f, 0xa0, 0x8c, 0xcf,
+ 0xbe, 0x0e, 0x79, 0x19, 0xb7, 0x6f, 0x58, 0xeb, 0x36, 0xdd, 0x6f, 0x97, 0x48, 0x0d, 0x66, 0xb6,
+ 0xe9, 0xfe, 0xe6, 0xa3, 0xf6, 0x94, 0xc5, 0x5e, 0x3d, 0xde, 0x6c, 0x4f, 0x13, 0x80, 0xd9, 0x57,
+ 0x8f, 0x37, 0xd9, 0xe6, 0xa3, 0x76, 0xb9, 0x73, 0x02, 0xf5, 0x42, 0x96, 0xed, 0x9c, 0x48, 0xb5,
+ 0x60, 0x27, 0x32, 0xe2, 0x38, 0x4d, 0xaa, 0xb4, 0x92, 0x6a, 0xf1, 0x4c, 0x46, 0xdc, 0x96, 0x95,
+ 0x35, 0xa9, 0xbe, 0xc0, 0x09, 0x52, 0xa5, 0xb3, 0xa9, 0x16, 0xb4, 0x2f, 0xc8, 0x97, 0xd0, 0x1a,
+ 0x4a, 0x9b, 0xe6, 0xb1, 0x72, 0x1a, 0xed, 0x0d, 0x44, 0x8f, 0x9d, 0xbc, 0x23, 0x81, 0xbc, 0x9b,
+ 0x65, 0xb2, 0x01, 0x4b, 0xf8, 0xdc, 0x58, 0x72, 0x3a, 0xd2, 0x81, 0xcf, 0x43, 0x16, 0x89, 0x48,
+ 0xaa, 0x11, 0x6e, 0x5e, 0xa6, 0x0b, 0x68, 0x3c, 0xcc, 0x6c, 0xfb, 0x68, 0xb2, 0x43, 0x87, 0x9f,
+ 0xf1, 0x20, 0xe4, 0xfd, 0x50, 0xd8, 0x4e, 0xab, 0x31, 0x9e, 0x19, 0xda, 0x1c, 0xa3, 0xdd, 0x24,
+ 0xd5, 0x9d, 0x7f, 0x4b, 0x50, 0xcd, 0x33, 0x4c, 0x08, 0x94, 0x07, 0x42, 0xfb, 0xe8, 0xb6, 0x46,
+ 0xf1, 0xdb, 0x62, 0xf8, 0x80, 0xdc, 0x3c, 0xc4, 0x6f, 0x72, 0x07, 0x40, 0x1b, 0xae, 0x0c, 0x0e,
+ 0x55, 0x3c, 0x47, 0x99, 0xd6, 0x10, 0xb1, 0xb3, 0x94, 0xdc, 0x82, 0x9a, 0x12, 0x3c, 0x74, 0xd6,
+ 0x32, 0x5a, 0xab, 0x16, 0x40, 0xe3, 0xe7, 0x00, 0x2e, 0x78, 0x9b, 0x08, 0x9c, 0x6d, 0xe5, 0x9d,
+ 0x29, 0xaf, 0x44, 0x6b, 0x0e, 0x3d, 0xd6, 0x82, 0xfc, 0x0e, 0xcb, 0x89, 0x92, 0xbe, 0xd0, 0x5a,
+ 0xe8, 0x2b, 0xcf, 0x73, 0x16, 0x1f, 0xca, 0xda, 0xe4, 0x87, 0xe2, 0x34, 0x97, 0xde, 0xe7, 0xd2,
+ 0xd8, 0x51, 0x11, 0xee, 0xfc, 0x39, 0x0d, 0x0b, 0x13, 0xe8, 0xe3, 0xc3, 0x96, 0x0a, 0x87, 0x5d,
+ 0x83, 0x76, 0xaa, 0x85, 0xc2, 0xd3, 0xb0, 0x28, 0xb0, 0xed, 0x15, 0x93, 0x51, 0xa6, 0x2d, 0x8b,
+ 0xdb, 0x43, 0xed, 0x23, 0x6a, 0x27, 0x5b, 0x56, 0x53, 0x45, 0xae, 0x4b, 0x4f, 0xdb, 0x59, 0x0a,
+ 0xec, 0xdb, 0x00, 0x11, 0x3f, 0x67, 0x4a, 0x6b, 0xf6, 0xa6, 0x9f, 0xa7, 0x29, 0xe2, 0xe7, 0x54,
+ 0xeb, 0xbd, 0x3e, 0xf9, 0x1a, 0xe6, 0xa3, 0x20, 0x96, 0x8a, 0x25, 0xfc, 0x44, 0xb0, 0x21, 0x4f,
+ 0x43, 0xa3, 0x5d, 0xb6, 0xe8, 0x1c, 0x1a, 0x0e, 0xf9, 0x89, 0xf8, 0x01, 0x61, 0xe4, 0xf2, 0xd7,
+ 0x57, 0xb8, 0xb3, 0x19, 0xd7, 0x1a, 0x0a, 0xdc, 0x4f, 0xa1, 0x1e, 0x48, 0x16, 0xc4, 0x49, 0x6a,
+ 0xec, 0xb6, 0x15, 0x77, 0x77, 0x81, 0x7c, 0x6e, 0x91, 0xbd, 0x3e, 0x59, 0x85, 0x46, 0x20, 0x99,
+ 0x4c, 0x4d, 0x46, 0xa8, 0x22, 0x01, 0x02, 0xd9, 0x43, 0x68, 0xaf, 0x4f, 0x9e, 0xc0, 0xca, 0x99,
+ 0x0c, 0xd3, 0xd8, 0x70, 0x35, 0xb2, 0xed, 0xc9, 0x88, 0x73, 0xc3, 0xf4, 0x1f, 0x81, 0xf1, 0x4f,
+ 0x85, 0xc6, 0x11, 0x5d, 0xa6, 0xde, 0x98, 0xd1, 0x75, 0x84, 0xa3, 0xcc, 0x4e, 0xbe, 0x87, 0xdb,
+ 0x41, 0xfc, 0x1e, 0x3d, 0xa0, 0x7e, 0xa5, 0xc0, 0xb9, 0xe2, 0xa1, 0xf3, 0x4f, 0x09, 0x5a, 0xfb,
+ 0x72, 0x90, 0x86, 0xe2, 0xc5, 0x28, 0x71, 0xd7, 0xf6, 0x5b, 0xde, 0x2d, 0x5d, 0x92, 0xf1, 0xfa,
+ 0x5a, 0x1b, 0x0f, 0x26, 0x8f, 0xf5, 0x4b, 0x52, 0xd7, 0x3c, 0x5d, 0xc9, 0x15, 0x06, 0x7c, 0xff,
+ 0x02, 0x25, 0x9f, 0x41, 0x3d, 0x42, 0x0d, 0x33, 0xa3, 0x24, 0xaf, 0x03, 0x88, 0xc6, 0x6e, 0x6c,
+ 0x65, 0xc7, 0x69, 0xc4, 0xe4, 0x90, 0x39, 0xd0, 0x5d, 0x79, 0x93, 0x36, 0xe2, 0x34, 0xea, 0x0d,
+ 0xdd, 0x7e, 0xba, 0xf3, 0x20, 0x6b, 0x21, 0x99, 0xd7, 0x4b, 0x7d, 0xa8, 0x06, 0x33, 0x47, 0xbd,
+ 0xde, 0x81, 0x6d, 0x58, 0x55, 0x28, 0xef, 0x6f, 0xef, 0xed, 0xb6, 0xa7, 0x3a, 0x21, 0xac, 0x74,
+ 0x55, 0x60, 0x6c, 0x49, 0x1f, 0x6b, 0xa1, 0x7e, 0x92, 0xa9, 0x8a, 0xc5, 0x28, 0x1f, 0x10, 0x93,
+ 0x5e, 0xea, 0x16, 0x54, 0xf2, 0x01, 0x34, 0xf5, 0x9e, 0x79, 0x51, 0xf8, 0x61, 0x43, 0x73, 0x41,
+ 0xa7, 0x0f, 0xb7, 0x26, 0xec, 0xa6, 0x2f, 0xe6, 0x51, 0xd9, 0x4f, 0x5f, 0x6b, 0xaf, 0x84, 0xf5,
+ 0x37, 0x39, 0xb3, 0xff, 0x1f, 0x2d, 0x45, 0x71, 0xe7, 0xaf, 0x12, 0xcc, 0xbf, 0x33, 0xfd, 0x88,
+ 0x07, 0x95, 0x3c, 0x6f, 0x25, 0xcc, 0x5b, 0xbe, 0xb4, 0xf3, 0x2b, 0xfb, 0x79, 0xe8, 0x0e, 0xd4,
+ 0xa4, 0xe3, 0xb5, 0x7d, 0xf3, 0xae, 0x25, 0xf2, 0x30, 0x94, 0x3e, 0xf3, 0x65, 0x1a, 0x9b, 0xac,
+ 0xd4, 0xe6, 0xd0, 0xb0, 0x6d, 0xf1, 0xae, 0x85, 0x6d, 0x05, 0x17, 0xb9, 0x3a, 0x78, 0x9b, 0xb7,
+ 0xa5, 0xd6, 0x05, 0xf5, 0x28, 0x78, 0x2b, 0xec, 0xef, 0x31, 0x5b, 0x93, 0xa7, 0x82, 0x27, 0x8e,
+ 0xe6, 0x2a, 0xae, 0x1e, 0xf1, 0xf3, 0x1f, 0x05, 0x4f, 0x2c, 0x67, 0x67, 0xe9, 0xd7, 0x6c, 0xe4,
+ 0x67, 0xe7, 0x66, 0xf8, 0x97, 0xe4, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xfb, 0x8e, 0xf5,
+ 0xa2, 0x0c, 0x00, 0x00,
}
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 8d3933f..91d8dd9 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -105,6 +105,9 @@
// The build command that the user entered to the build system.
optional string build_command = 26;
+
+ // The metrics for calling Bazel.
+ repeated PerfInfo bazel_runs = 27;
}
message BuildConfig {
diff --git a/xml/xml_test.go b/xml/xml_test.go
index abcb108..138503c 100644
--- a/xml/xml_test.go
+++ b/xml/xml_test.go
@@ -57,10 +57,10 @@
"baz.xml": nil,
}
config := android.TestArchConfig(buildDir, nil, bp, fs)
- ctx := android.NewTestArchContext()
+ ctx := android.NewTestArchContext(config)
ctx.RegisterModuleType("prebuilt_etc", etc.PrebuiltEtcFactory)
ctx.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory)
- ctx.Register(config)
+ ctx.Register()
_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index d603586..fc976f6 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -12,6 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// soong_zip is a utility used during the build to create a zip archive by pulling the entries from
+// various sources:
+// * explicitly specified files
+// * files whose paths are read from a file
+// * directories traversed recursively
+// It can optionally change the recorded path of an entry.
+
package main
import (
diff --git a/zip/zip.go b/zip/zip.go
index e27432c..cb85f5c 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -126,6 +126,7 @@
return b
}
+// List reads the file names from the given file and adds them to the source files list.
func (b *FileArgsBuilder) List(name string) *FileArgsBuilder {
if b.err != nil {
return b
@@ -150,6 +151,7 @@
return b
}
+// RspFile reads the file names from given .rsp file and adds them to the source files list.
func (b *FileArgsBuilder) RspFile(name string) *FileArgsBuilder {
if b.err != nil {
return b
@@ -291,7 +293,7 @@
return args
}
-func ZipTo(args ZipArgs, w io.Writer) error {
+func zipTo(args ZipArgs, w io.Writer) error {
if args.EmulateJar {
args.AddDirectoryEntriesToZip = true
}
@@ -392,6 +394,7 @@
return z.write(w, pathMappings, args.ManifestSourcePath, args.EmulateJar, args.SrcJar, args.NumParallelJobs)
}
+// Zip creates an output zip archive from given sources.
func Zip(args ZipArgs) error {
if args.OutputFilePath == "" {
return fmt.Errorf("output file path must be nonempty")
@@ -416,7 +419,7 @@
out = f
}
- err := ZipTo(args, out)
+ err := zipTo(args, out)
if err != nil {
return err
}
@@ -450,7 +453,6 @@
RelativeRoot: fa.SourcePrefixToStrip,
}
}
-
}
dest = filepath.Join(fa.PathPrefixInZip, dest)
@@ -465,10 +467,9 @@
}
func jarSort(mappings []pathMapping) {
- less := func(i int, j int) (smaller bool) {
+ sort.SliceStable(mappings, func(i int, j int) bool {
return jar.EntryNamesLess(mappings[i].dest, mappings[j].dest)
- }
- sort.SliceStable(mappings, less)
+ })
}
func (z *ZipWriter) write(f io.Writer, pathMappings []pathMapping, manifest string, emulateJar, srcJar bool,
@@ -709,7 +710,7 @@
}
}
-func (z *ZipWriter) addManifest(dest string, src string, method uint16) error {
+func (z *ZipWriter) addManifest(dest string, src string, _ uint16) error {
if prev, exists := z.createdDirs[dest]; exists {
return fmt.Errorf("destination %q is both a directory %q and a file %q", dest, prev, src)
}
@@ -963,7 +964,7 @@
dir = filepath.Clean(dir)
// discover any uncreated directories in the path
- zipDirs := []string{}
+ var zipDirs []string
for dir != "" && dir != "." {
if _, exists := z.createdDirs[dir]; exists {
break
diff --git a/zip/zip_test.go b/zip/zip_test.go
index 302a749..a16e092 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -442,7 +442,7 @@
args.Stderr = &bytes.Buffer{}
buf := &bytes.Buffer{}
- err := ZipTo(args, buf)
+ err := zipTo(args, buf)
if (err != nil) != (test.err != nil) {
t.Fatalf("want error %v, got %v", test.err, err)
@@ -627,7 +627,7 @@
args.Stderr = &bytes.Buffer{}
buf := &bytes.Buffer{}
- err := ZipTo(args, buf)
+ err := zipTo(args, buf)
if err != nil {
t.Fatalf("got error %v", err)
}