Merge "Rename __ANDROID_SDK_VERSION__."
diff --git a/android/androidmk.go b/android/androidmk.go
index 063830b..6ab4a24 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)
+// Utility funcs to manipulate Android.mk variable entries.
+
+// SetString sets a Make variable with the given name to the given value.
func (a *AndroidMkEntries) SetString(name, value string) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -101,6 +153,7 @@
a.EntryMap[name] = []string{value}
}
+// SetPath sets a Make variable with the given name to the given path string.
func (a *AndroidMkEntries) SetPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -108,12 +161,15 @@
a.EntryMap[name] = []string{path.String()}
}
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.SetPath(name, path.Path())
}
}
+// AddPath appends the given path string to a Make variable with the given name.
func (a *AndroidMkEntries) AddPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -121,12 +177,15 @@
a.EntryMap[name] = append(a.EntryMap[name], path.String())
}
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.AddPath(name, path.Path())
}
}
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
func (a *AndroidMkEntries) SetPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -134,12 +193,15 @@
a.EntryMap[name] = paths.Strings()
}
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) {
if len(paths) > 0 {
a.SetPaths(name, paths)
}
}
+// AddPaths appends the given path strings to a Make variable with the given name.
func (a *AndroidMkEntries) AddPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -147,6 +209,8 @@
a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
}
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
if flag {
if _, ok := a.EntryMap[name]; !ok {
@@ -156,6 +220,7 @@
}
}
+// SetBool sets a Make variable with the given name to if the given bool flag value.
func (a *AndroidMkEntries) SetBool(name string, flag bool) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -167,6 +232,7 @@
}
}
+// AddStrings appends the given strings to a Make variable with the given name.
func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
if len(value) == 0 {
return
@@ -177,6 +243,18 @@
a.EntryMap[name] = append(a.EntryMap[name], value...)
}
+// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
+// for partial MTS test suites.
+func (a *AndroidMkEntries) AddCompatibilityTestSuites(suites ...string) {
+ // MTS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
+ // To reduce repetition, if we find a partial MTS test suite without an full MTS test suite,
+ // we add the full test suite to our list.
+ if PrefixInList(suites, "mts-") && !InList("mts", suites) {
+ suites = append(suites, "mts")
+ }
+ a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
+}
+
// The contributions to the dist.
type distContributions struct {
// List of goals and the dist copy instructions.
@@ -356,6 +434,8 @@
return generateDistContributionsForMake(distContributions)
}
+// fillInEntries goes through the common variable processing and calls the extra data funcs to
+// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
a.EntryMap = make(map[string][]string)
amod := mod.(Module).base()
@@ -476,6 +556,8 @@
}
}
+// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
func (a *AndroidMkEntries) write(w io.Writer) {
if a.Disabled {
return
@@ -496,6 +578,8 @@
return strings.Split(string(a.footer.Bytes()), "\n")
}
+// AndroidMkSingleton is a singleton to collect Android.mk data from all modules and dump them into
+// the final Android-<product_name>.mk file output.
func AndroidMkSingleton() Singleton {
return &androidMkSingleton{}
}
@@ -503,6 +587,7 @@
type androidMkSingleton struct{}
func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
+ // Skip if Soong wasn't invoked from Make.
if !ctx.Config().KatiEnabled() {
return
}
@@ -513,6 +598,8 @@
androidMkModulesList = append(androidMkModulesList, module)
})
+ // Sort the module list by the module names to eliminate random churns, which may erroneously
+ // invoke additional build processes.
sort.SliceStable(androidMkModulesList, func(i, j int) bool {
return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
})
@@ -605,6 +692,8 @@
}
}
+// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
+// m by making them phony targets.
func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
goBinary bootstrap.GoBinaryTool) error {
@@ -637,6 +726,8 @@
data.Target_required = data.Entries.Target_required
}
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
provider AndroidMkDataProvider) error {
@@ -683,6 +774,8 @@
return nil
}
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
if data.Disabled {
return
@@ -730,11 +823,14 @@
module.Os() == LinuxBionic
}
+// A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
+// to use this func.
func AndroidMkDataPaths(data []DataPath) []string {
var testFiles []string
for _, d := range data {
rel := d.SrcPath.Rel()
path := d.SrcPath.String()
+ // LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path.
if !strings.HasSuffix(path, rel) {
panic(fmt.Errorf("path %q does not end with %q", path, rel))
}
diff --git a/android/arch.go b/android/arch.go
index df407d4..34f9b29 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1337,17 +1337,6 @@
addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
- // An optional host target that uses the Bionic glibc runtime.
- if Bool(config.Host_bionic) {
- addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
- }
-
- // An optional cross-compiled host target that uses the Bionic glibc runtime on an arm64
- // architecture.
- if Bool(config.Host_bionic_arm64) {
- addTarget(LinuxBionic, "arm64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
- }
-
// Optional cross-compiled host targets, generally Windows.
if String(variables.CrossHost) != "" {
crossHostOs := osByName(*variables.CrossHost)
@@ -1437,53 +1426,6 @@
abi []string
}
-// getMegaDeviceConfig returns a list of archConfigs for every architecture simultaneously.
-func getMegaDeviceConfig() []archConfig {
- return []archConfig{
- {"arm", "armv7-a", "generic", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "generic", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a7", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a8", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a9", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a72", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a75", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "cortex-a76", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "kryo385", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "exynos-m1", []string{"armeabi-v7a"}},
- {"arm", "armv7-a-neon", "exynos-m2", []string{"armeabi-v7a"}},
- {"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "cortex-a72", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "cortex-a73", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "exynos-m1", []string{"arm64-v8a"}},
- {"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a55", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a75", []string{"arm64-v8a"}},
- {"arm64", "armv8-2a-dotprod", "cortex-a76", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
- {"x86", "atom", "", []string{"x86"}},
- {"x86", "haswell", "", []string{"x86"}},
- {"x86", "ivybridge", "", []string{"x86"}},
- {"x86", "sandybridge", "", []string{"x86"}},
- {"x86", "silvermont", "", []string{"x86"}},
- {"x86", "stoneyridge", "", []string{"x86"}},
- {"x86", "x86_64", "", []string{"x86"}},
- {"x86_64", "", "", []string{"x86_64"}},
- {"x86_64", "haswell", "", []string{"x86_64"}},
- {"x86_64", "ivybridge", "", []string{"x86_64"}},
- {"x86_64", "sandybridge", "", []string{"x86_64"}},
- {"x86_64", "silvermont", "", []string{"x86_64"}},
- {"x86_64", "stoneyridge", "", []string{"x86_64"}},
- }
-}
-
// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
func getNdkAbisConfig() []archConfig {
return []archConfig{
diff --git a/android/config.go b/android/config.go
index 04c9129..9882d55 100644
--- a/android/config.go
+++ b/android/config.go
@@ -54,28 +54,9 @@
isPreview: true,
}
-// configFileName is the name the file containing FileConfigurableOptions from
-// soong_ui for the soong_build primary builder.
-const configFileName = "soong.config"
-
-// productVariablesFileName contain the product configuration variables from soong_ui for the
-// soong_build primary builder and Kati.
+// The product variables file name, containing product config from Kati.
const productVariablesFileName = "soong.variables"
-// A FileConfigurableOptions contains options which can be configured by the
-// config file. These will be included in the config struct.
-type FileConfigurableOptions struct {
- Mega_device *bool `json:",omitempty"`
- Host_bionic *bool `json:",omitempty"`
- Host_bionic_arm64 *bool `json:",omitempty"`
-}
-
-// SetDefaultConfig resets the receiving FileConfigurableOptions to default
-// values.
-func (f *FileConfigurableOptions) SetDefaultConfig() {
- *f = FileConfigurableOptions{}
-}
-
// A Config object represents the entire build configuration for Android.
type Config struct {
*config
@@ -97,12 +78,8 @@
type VendorConfig soongconfig.SoongConfig
// Definition of general build configuration for soong_build. Some of these
-// configuration values are generated from soong_ui for soong_build,
-// communicated over JSON files like soong.config or soong.variables.
+// product configuration values are read from Kati-generated soong.variables.
type config struct {
- // Options configurable with soong.confg
- FileConfigurableOptions
-
// Options configurable with soong.variables
productVariables productVariables
@@ -113,7 +90,6 @@
// purposes.
BazelContext BazelContext
- ConfigFileName string
ProductVariablesFileName string
Targets map[OsType][]Target
@@ -170,11 +146,6 @@
}
func loadConfig(config *config) error {
- err := loadFromConfigFile(&config.FileConfigurableOptions, absolutePath(config.ConfigFileName))
- if err != nil {
- return err
- }
-
return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
@@ -384,7 +355,6 @@
func NewConfig(srcDir, buildDir string, moduleListFile string) (Config, error) {
// Make a config with default options.
config := &config{
- ConfigFileName: filepath.Join(buildDir, configFileName),
ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
env: originalEnv,
@@ -439,9 +409,7 @@
targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]}
var archConfig []archConfig
- if Bool(config.Mega_device) {
- archConfig = getMegaDeviceConfig()
- } else if config.NdkAbis() {
+ if config.NdkAbis() {
archConfig = getNdkAbisConfig()
} else if config.AmlAbis() {
archConfig = getAmlAbisConfig()
@@ -864,11 +832,6 @@
return c.Targets[Android][0].Arch.ArchType
}
-func (c *config) SkipMegaDeviceInstall(path string) bool {
- return Bool(c.Mega_device) &&
- strings.HasPrefix(path, filepath.Join(c.buildDir, "target", "product"))
-}
-
func (c *config) SanitizeHost() []string {
return append([]string(nil), c.productVariables.SanitizeHost...)
}
@@ -1321,6 +1284,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)
}
diff --git a/android/config_test.go b/android/config_test.go
index 68f68a0..7bfc800 100644
--- a/android/config_test.go
+++ b/android/config_test.go
@@ -78,11 +78,6 @@
if err != nil {
t.Errorf(err.Error())
}
-
- validateConfigAnnotations(&FileConfigurableOptions{})
- if err != nil {
- t.Errorf(err.Error())
- }
}
func TestMissingVendorConfig(t *testing.T) {
diff --git a/android/csuite_config.go b/android/csuite_config.go
index a5b1533..bf24d98 100644
--- a/android/csuite_config.go
+++ b/android/csuite_config.go
@@ -44,7 +44,7 @@
if me.properties.Test_config != nil {
entries.SetString("LOCAL_TEST_CONFIG", *me.properties.Test_config)
}
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "csuite")
+ entries.AddCompatibilityTestSuites("csuite")
},
}
return []AndroidMkEntries{androidMkEntries}
diff --git a/android/defs.go b/android/defs.go
index 631dfe8..f5bd362 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -15,6 +15,7 @@
package android
import (
+ "fmt"
"strings"
"testing"
@@ -97,7 +98,7 @@
// content to file.
writeFile = pctx.AndroidStaticRule("writeFile",
blueprint.RuleParams{
- Command: `/bin/bash -c 'echo -e "$$0" > $out' $content`,
+ Command: `/bin/bash -c 'echo -e -n "$$0" > $out' $content`,
Description: "writing file $out",
},
"content")
@@ -133,9 +134,7 @@
shellUnescaper = strings.NewReplacer(`'\''`, `'`)
)
-// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
-// so that the file contains exactly the contents passed to the function, plus a trailing newline.
-func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+func buildWriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
content = echoEscaper.Replace(content)
content = proptools.ShellEscape(content)
if content == "" {
@@ -151,6 +150,31 @@
})
}
+// WriteFileRule creates a ninja rule to write contents to a file. The contents will be escaped
+// so that the file contains exactly the contents passed to the function, plus a trailing newline.
+func WriteFileRule(ctx BuilderContext, outputFile WritablePath, content string) {
+ // This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes
+ const SHARD_SIZE = 131072 - 10000
+
+ content += "\n"
+ if len(content) > SHARD_SIZE {
+ var chunks WritablePaths
+ for i, c := range ShardString(content, SHARD_SIZE) {
+ tempPath := outputFile.ReplaceExtension(ctx, fmt.Sprintf("%s.%d", outputFile.Ext(), i))
+ buildWriteFileRule(ctx, tempPath, c)
+ chunks = append(chunks, tempPath)
+ }
+ ctx.Build(pctx, BuildParams{
+ Rule: Cat,
+ Inputs: chunks.Paths(),
+ Output: outputFile,
+ Description: "Merging to " + outputFile.Base(),
+ })
+ return
+ }
+ buildWriteFileRule(ctx, outputFile, content)
+}
+
// shellUnescape reverses proptools.ShellEscape
func shellUnescape(s string) string {
// Remove leading and trailing quotes if present
diff --git a/android/module.go b/android/module.go
index d680baa..b6729c0 100644
--- a/android/module.go
+++ b/android/module.go
@@ -2399,10 +2399,6 @@
if m.Config().KatiEnabled() && !m.InstallBypassMake() {
return true
}
-
- if m.Config().SkipMegaDeviceInstall(fullInstallPath.String()) {
- return true
- }
}
return false
diff --git a/android/packaging.go b/android/packaging.go
index 512e4ba..09432e6 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -21,10 +21,10 @@
"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.
+// 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
@@ -40,15 +40,25 @@
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.
- AddDeps(ctx BottomUpMutatorContext)
+ // 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,
+ // 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
@@ -59,9 +69,9 @@
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.
+ // 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
}
@@ -82,10 +92,6 @@
Multilib packagingMultilibProperties `android:"arch_variant"`
}
-type packagingDependencyTag struct{ blueprint.BaseDependencyTag }
-
-var depTag = packagingDependencyTag{}
-
func InitPackageModule(p PackageModule) {
base := p.packagingBase()
p.AddProperties(&base.properties)
@@ -95,10 +101,10 @@
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.
+// 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 {
@@ -134,7 +140,7 @@
}
// See PackageModule.AddDeps
-func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext) {
+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) {
@@ -147,15 +153,9 @@
// See PackageModule.CopyDepsToZip
func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
- var supportedArches []string
- for _, t := range p.getSupportedTargets(ctx) {
- supportedArches = append(supportedArches, t.Arch.ArchType.String())
- }
m := make(map[string]PackagingSpec)
ctx.WalkDeps(func(child Module, parent Module) bool {
- // Don't track modules with unsupported arch
- // TODO(jiyong): remove this when aosp/1501613 lands.
- if !InList(child.Target().Arch.ArchType.String(), supportedArches) {
+ if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
return false
}
for _, ps := range child.PackagingSpecs() {
@@ -166,7 +166,7 @@
return true
})
- builder := NewRuleBuilder()
+ builder := NewRuleBuilder(pctx, ctx)
dir := PathForModuleOut(ctx, ".zip").OutputPath
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
@@ -193,13 +193,13 @@
}
builder.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipOut).
FlagWithArg("-C ", dir.String()).
Flag("-L 0"). // no compression because this will be unzipped soon
FlagWithArg("-D ", dir.String())
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
- builder.Build(pctx, ctx, "zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
+ builder.Build("zip_deps", fmt.Sprintf("Zipping deps for %s", ctx.ModuleName()))
return entries
}
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 7710c7f..7269bfb 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -17,6 +17,8 @@
import (
"reflect"
"testing"
+
+ "github.com/google/blueprint"
)
// Module to be packaged
@@ -27,6 +29,12 @@
}
}
+// dep tag used in this test. All dependencies are considered as installable.
+type installDepTag struct {
+ blueprint.BaseDependencyTag
+ InstallAlwaysNeededDependencyTag
+}
+
func componentTestModuleFactory() Module {
m := &componentTestModule{}
m.AddProperties(&m.props)
@@ -35,7 +43,7 @@
}
func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+ ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
}
func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -61,7 +69,7 @@
}
func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
- m.AddDeps(ctx)
+ m.AddDeps(ctx, installDepTag{})
}
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
diff --git a/android/paths.go b/android/paths.go
index b7117a3..5a41cf1 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 {
@@ -1249,6 +1251,10 @@
return p.config.buildDir
}
+func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
+ panic("Not implemented")
+}
+
var _ Path = InstallPath{}
var _ WritablePath = InstallPath{}
@@ -1511,6 +1517,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/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/rule_builder.go b/android/rule_builder.go
index 3efe9f8..e2d8187 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -37,6 +37,9 @@
// RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
// graph.
type RuleBuilder struct {
+ pctx PackageContext
+ ctx BuilderContext
+
commands []*RuleBuilderCommand
installs RuleBuilderInstalls
temporariesSet map[WritablePath]bool
@@ -44,14 +47,16 @@
sbox bool
highmem bool
remoteable RemoteRuleSupports
- sboxOutDir WritablePath
+ outDir WritablePath
sboxManifestPath WritablePath
missingDeps []string
}
// NewRuleBuilder returns a newly created RuleBuilder.
-func NewRuleBuilder() *RuleBuilder {
+func NewRuleBuilder(pctx PackageContext, ctx BuilderContext) *RuleBuilder {
return &RuleBuilder{
+ pctx: pctx,
+ ctx: ctx,
temporariesSet: make(map[WritablePath]bool),
}
}
@@ -130,7 +135,7 @@
panic("Sbox() is not compatible with Restat()")
}
r.sbox = true
- r.sboxOutDir = outputDir
+ r.outDir = outputDir
r.sboxManifestPath = manifestPath
return r
}
@@ -146,8 +151,7 @@
// race with any call to Build.
func (r *RuleBuilder) Command() *RuleBuilderCommand {
command := &RuleBuilderCommand{
- sbox: r.sbox,
- outDir: r.sboxOutDir,
+ rule: r,
}
r.commands = append(r.commands, command)
return command
@@ -395,19 +399,19 @@
var _ BuilderContext = ModuleContext(nil)
var _ BuilderContext = SingletonContext(nil)
-func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
+func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- BuiltTool(ctx, "dep_fixer").
+ BuiltTool("dep_fixer").
Inputs(depFiles.Paths())
}
// Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
// Outputs.
-func (r *RuleBuilder) Build(pctx PackageContext, ctx BuilderContext, name string, desc string) {
+func (r *RuleBuilder) Build(name string, desc string) {
name = ninjaNameEscape(name)
if len(r.missingDeps) > 0 {
- ctx.Build(pctx, BuildParams{
+ r.ctx.Build(pctx, BuildParams{
Rule: ErrorRule,
Outputs: r.Outputs(),
OrderOnly: r.OrderOnlys(),
@@ -426,13 +430,13 @@
depFormat = blueprint.DepsGCC
if len(depFiles) > 1 {
// Add a command locally that merges all depfiles together into the first depfile.
- r.depFileMergerCmd(ctx, depFiles)
+ r.depFileMergerCmd(depFiles)
if r.sbox {
// Check for Rel() errors, as all depfiles should be in the output dir. Errors
// will be reported to the ctx.
for _, path := range depFiles[1:] {
- Rel(ctx, r.sboxOutDir.String(), path.String())
+ Rel(r.ctx, r.outDir.String(), path.String())
}
}
}
@@ -468,7 +472,7 @@
// to the output directory.
sboxOutputs := make([]string, len(outputs))
for i, output := range outputs {
- rel := Rel(ctx, r.sboxOutDir.String(), output.String())
+ rel := Rel(r.ctx, r.outDir.String(), output.String())
sboxOutputs[i] = filepath.Join(sboxOutDir, rel)
command.CopyAfter = append(command.CopyAfter, &sbox_proto.Copy{
From: proto.String(filepath.Join(sboxOutSubDir, rel)),
@@ -483,23 +487,27 @@
// Verify that the manifest textproto is not inside the sbox output directory, otherwise
// it will get deleted when the sbox rule clears its output directory.
- _, manifestInOutDir := MaybeRel(ctx, r.sboxOutDir.String(), r.sboxManifestPath.String())
+ _, manifestInOutDir := MaybeRel(r.ctx, r.outDir.String(), r.sboxManifestPath.String())
if manifestInOutDir {
- ReportPathErrorf(ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
- name, r.sboxManifestPath.String(), r.sboxOutDir.String())
+ ReportPathErrorf(r.ctx, "sbox rule %q manifestPath %q must not be in outputDir %q",
+ name, r.sboxManifestPath.String(), r.outDir.String())
}
// Create a rule to write the manifest as a the textproto.
- WriteFileRule(ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
+ WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
// Generate a new string to use as the command line of the sbox rule. This uses
// a RuleBuilderCommand as a convenience method of building the command line, then
// converts it to a string to replace commandString.
- sboxCmd := &RuleBuilderCommand{}
- sboxCmd.Text("rm -rf").Output(r.sboxOutDir)
+ sboxCmd := &RuleBuilderCommand{
+ rule: &RuleBuilder{
+ ctx: r.ctx,
+ },
+ }
+ sboxCmd.Text("rm -rf").Output(r.outDir)
sboxCmd.Text("&&")
- sboxCmd.BuiltTool(ctx, "sbox").
- Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
+ sboxCmd.BuiltTool("sbox").
+ Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
Flag("--manifest").Input(r.sboxManifestPath)
// Replace the command string, and add the sbox tool and manifest textproto to the
@@ -528,19 +536,19 @@
}
var pool blueprint.Pool
- if ctx.Config().UseGoma() && r.remoteable.Goma {
+ if r.ctx.Config().UseGoma() && r.remoteable.Goma {
// When USE_GOMA=true is set and the rule is supported by goma, allow jobs to run outside the local pool.
- } else if ctx.Config().UseRBE() && r.remoteable.RBE {
+ } else if r.ctx.Config().UseRBE() && r.remoteable.RBE {
// When USE_RBE=true is set and the rule is supported by RBE, use the remotePool.
pool = remotePool
} else if r.highmem {
pool = highmemPool
- } else if ctx.Config().UseRemoteBuild() {
+ } else if r.ctx.Config().UseRemoteBuild() {
pool = localPool
}
- ctx.Build(pctx, BuildParams{
- Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
+ r.ctx.Build(r.pctx, BuildParams{
+ Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
Command: commandString,
CommandDeps: tools.Strings(),
Restat: r.restat,
@@ -564,6 +572,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
@@ -576,16 +586,11 @@
// spans [start,end) of the command that should not be ninja escaped
unescapedSpans [][2]int
-
- sbox bool
- // outDir is the directory that will contain the output files of the rules. sbox will copy
- // the output files from the sandbox directory to this directory when it finishes.
- outDir WritablePath
}
func (c *RuleBuilderCommand) addInput(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
@@ -594,8 +599,8 @@
}
func (c *RuleBuilderCommand) addImplicit(path Path) string {
- if c.sbox {
- if rel, isRel, _ := maybeRelErr(c.outDir.String(), path.String()); isRel {
+ if c.rule.sbox {
+ if rel, isRel, _ := maybeRelErr(c.rule.outDir.String(), path.String()); isRel {
return filepath.Join(sboxOutDir, rel)
}
}
@@ -607,22 +612,19 @@
c.orderOnlys = append(c.orderOnlys, path)
}
-func (c *RuleBuilderCommand) outputStr(path WritablePath) string {
- if c.sbox {
- return SboxPathForOutput(path, c.outDir)
+// PathForOutput takes an output path and returns the appropriate path to use on the command
+// line. If sbox was enabled via a call to RuleBuilder.Sbox(), it returns a path with the
+// placeholder prefix used for outputs in sbox. If sbox is not enabled it returns the
+// original path.
+func (c *RuleBuilderCommand) PathForOutput(path WritablePath) string {
+ if c.rule.sbox {
+ // Errors will be handled in RuleBuilder.Build where we have a context to report them
+ rel, _, _ := maybeRelErr(c.rule.outDir.String(), path.String())
+ return filepath.Join(sboxOutDir, rel)
}
return path.String()
}
-// SboxPathForOutput takes an output path and the out directory passed to RuleBuilder.Sbox(),
-// and returns the corresponding path for the output in the sbox sandbox. This can be used
-// on the RuleBuilder command line to reference the output.
-func SboxPathForOutput(path WritablePath, outDir WritablePath) string {
- // Errors will be handled in RuleBuilder.Build where we have a context to report them
- rel, _, _ := maybeRelErr(outDir.String(), path.String())
- return filepath.Join(sboxOutDir, rel)
-}
-
// 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 {
@@ -699,8 +701,8 @@
//
// It is equivalent to:
// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
-func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
- return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+ return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
}
// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
@@ -768,7 +770,7 @@
// RuleBuilder.Outputs.
func (c *RuleBuilderCommand) Output(path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(c.outputStr(path))
+ return c.Text(c.PathForOutput(path))
}
// Outputs adds the specified output paths to the command line, separated by spaces. The paths will also be added to
@@ -783,7 +785,7 @@
// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
// and will be the temporary output directory managed by sbox, not the final one.
func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
- if !c.sbox {
+ if !c.rule.sbox {
panic("OutputDir only valid with Sbox")
}
return c.Text(sboxOutDir)
@@ -794,7 +796,7 @@
// commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the depfiles together.
func (c *RuleBuilderCommand) DepFile(path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(c.outputStr(path))
+ return c.Text(c.PathForOutput(path))
}
// ImplicitOutput adds the specified output path to the dependencies returned by RuleBuilder.Outputs without modifying
@@ -885,14 +887,14 @@
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithOutput(flag string, path WritablePath) *RuleBuilderCommand {
c.outputs = append(c.outputs, path)
- return c.Text(flag + c.outputStr(path))
+ return c.Text(flag + c.PathForOutput(path))
}
// FlagWithDepFile adds the specified flag and depfile path to the command line, with no separator between them. The path
// will also be added to the outputs returned by RuleBuilder.Outputs.
func (c *RuleBuilderCommand) FlagWithDepFile(flag string, path WritablePath) *RuleBuilderCommand {
c.depFiles = append(c.depFiles, path)
- return c.Text(flag + c.outputStr(path))
+ return c.Text(flag + c.PathForOutput(path))
}
// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
@@ -987,3 +989,21 @@
h.Write([]byte(srcFileList))
return fmt.Sprintf("%x", h.Sum(nil))
}
+
+// BuilderContextForTesting returns a BuilderContext for the given config that can be used for tests
+// that need to call methods that take a BuilderContext.
+func BuilderContextForTesting(config Config) BuilderContext {
+ pathCtx := PathContextForTesting(config)
+ return builderContextForTests{
+ PathContext: pathCtx,
+ }
+}
+
+type builderContextForTests struct {
+ PathContext
+}
+
+func (builderContextForTests) Rule(PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
+ return nil
+}
+func (builderContextForTests) Build(PackageContext, BuildParams) {}
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index dc360c3..e676e4a 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -27,8 +27,8 @@
"android/soong/shared"
)
-func pathContext() PathContext {
- return PathContextForTesting(TestConfig("out", nil, "", map[string][]byte{
+func builderContext() BuilderContext {
+ return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
"ld": nil,
"a.o": nil,
"b.o": nil,
@@ -44,9 +44,9 @@
}
func ExampleRuleBuilder() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ld")).
@@ -55,7 +55,7 @@
rule.Command().Text("echo success")
// To add the command to the build graph:
- // rule.Build(pctx, ctx, "link", "link")
+ // rule.Build("link", "link")
fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
fmt.Printf("tools: %q\n", rule.Tools())
@@ -70,9 +70,9 @@
}
func ExampleRuleBuilder_SymlinkOutputs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "ln")).
@@ -96,9 +96,9 @@
}
func ExampleRuleBuilder_Temporary() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -123,9 +123,9 @@
}
func ExampleRuleBuilder_DeleteTemporaryFiles() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
rule.Command().
Tool(PathForSource(ctx, "cp")).
@@ -151,9 +151,9 @@
}
func ExampleRuleBuilder_Installs() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
out := PathForOutput(ctx, "linked")
@@ -171,9 +171,9 @@
}
func ExampleRuleBuilderCommand() {
- rule := NewRuleBuilder()
+ ctx := builderContext()
- ctx := pathContext()
+ rule := NewRuleBuilder(pctx, ctx)
// chained
rule.Command().
@@ -194,24 +194,24 @@
}
func ExampleRuleBuilderCommand_Flag() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).Flag("-l"))
// Output:
// ls -l
}
func ExampleRuleBuilderCommand_Flags() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"}))
// Output:
// ls -l -a
}
func ExampleRuleBuilderCommand_FlagWithArg() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagWithArg("--sort=", "time"))
// Output:
@@ -219,8 +219,8 @@
}
func ExampleRuleBuilderCommand_FlagForEachArg() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagForEachArg("--sort=", []string{"time", "size"}))
// Output:
@@ -228,8 +228,8 @@
}
func ExampleRuleBuilderCommand_FlagForEachInput() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "turbine")).
FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar")))
// Output:
@@ -237,8 +237,8 @@
}
func ExampleRuleBuilderCommand_FlagWithInputList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "java")).
FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":"))
// Output:
@@ -246,8 +246,8 @@
}
func ExampleRuleBuilderCommand_FlagWithInput() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "java")).
FlagWithInput("-classpath=", PathForSource(ctx, "a")))
// Output:
@@ -255,8 +255,8 @@
}
func ExampleRuleBuilderCommand_FlagWithList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "ls")).
FlagWithList("--sort=", []string{"time", "size"}, ","))
// Output:
@@ -264,8 +264,8 @@
}
func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
- ctx := pathContext()
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Tool(PathForSource(ctx, "javac")).
FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
NinjaEscapedString())
@@ -274,7 +274,8 @@
}
func ExampleRuleBuilderCommand_String() {
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Text("FOO=foo").
Text("echo $FOO").
String())
@@ -283,7 +284,8 @@
}
func ExampleRuleBuilderCommand_NinjaEscapedString() {
- fmt.Println(NewRuleBuilder().Command().
+ ctx := builderContext()
+ fmt.Println(NewRuleBuilder(pctx, ctx).Command().
Text("FOO=foo").
Text("echo $FOO").
NinjaEscapedString())
@@ -305,7 +307,10 @@
"input3": nil,
}
- ctx := PathContextForTesting(TestConfig("out", nil, "", fs))
+ pathCtx := PathContextForTesting(TestConfig("out", nil, "", fs))
+ ctx := builderContextForTests{
+ PathContext: pathCtx,
+ }
addCommands := func(rule *RuleBuilder) {
cmd := rule.Command().
@@ -355,7 +360,7 @@
wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
t.Run("normal", func(t *testing.T) {
- rule := NewRuleBuilder()
+ rule := NewRuleBuilder(pctx, ctx)
addCommands(rule)
wantCommands := []string{
@@ -389,13 +394,13 @@
t.Errorf("\nwant rule.OrderOnlys() = %#v\n got %#v", w, g)
}
- if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+ if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g)
}
})
t.Run("sbox", func(t *testing.T) {
- rule := NewRuleBuilder().Sbox(PathForOutput(ctx, ""),
+ rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
PathForOutput(ctx, "sbox.textproto"))
addCommands(rule)
@@ -427,7 +432,7 @@
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)
}
})
@@ -476,7 +481,7 @@
}
func testRuleBuilder_Build(ctx BuilderContext, in Paths, out, outDep, outDir, manifestPath WritablePath, restat, sbox bool) {
- rule := NewRuleBuilder()
+ rule := NewRuleBuilder(pctx, ctx)
if sbox {
rule.Sbox(outDir, manifestPath)
@@ -488,7 +493,7 @@
rule.Restat()
}
- rule.Build(pctx, ctx, "rule", "desc")
+ rule.Build("rule", "desc")
}
func TestRuleBuilder_Build(t *testing.T) {
diff --git a/android/test_suites.go b/android/test_suites.go
index 19444a8..7ecb8d2 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -63,13 +63,13 @@
testCasesDir := pathForInstall(ctx, BuildOs, X86, "testcases", false).ToMakePath()
outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
- rule := NewRuleBuilder()
- rule.Command().BuiltTool(ctx, "soong_zip").
+ rule := NewRuleBuilder(pctx, ctx)
+ rule.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", outputFile).
FlagWithArg("-P ", "host/testcases").
FlagWithArg("-C ", testCasesDir.String()).
FlagWithRspFileInputList("-r ", installedPaths.Paths())
- rule.Build(pctx, ctx, "robolectric_tests_zip", "robolectric-tests.zip")
+ rule.Build("robolectric_tests_zip", "robolectric-tests.zip")
return outputFile
}
diff --git a/android/util.go b/android/util.go
index 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/variable.go b/android/variable.go
index a9a9c87..0df5272 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -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/apex/builder.go b/apex/builder.go
index b858135..dc8e5e0 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -262,7 +262,7 @@
}
output := android.PathForModuleOut(ctx, "file_contexts")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
switch a.properties.ApexType {
case imageApex:
@@ -294,7 +294,7 @@
panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
}
- rule.Build(pctx, ctx, "file_contexts."+a.Name(), "Generate file_contexts")
+ rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
return output.OutputPath
}
@@ -329,14 +329,14 @@
// included in the APEX without actually downloading and extracting it.
func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
output := android.PathForModuleOut(ctx, "installed-files.txt")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Implicit(builtApex).
Text("(cd " + imageDir.String() + " ; ").
Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") ").
Text(" | sort -nr > ").
Output(output)
- rule.Build(pctx, ctx, "installed-files."+a.Name(), "Installed files")
+ rule.Build("installed-files."+a.Name(), "Installed files")
return output.OutputPath
}
diff --git a/cc/Android.bp b/cc/Android.bp
index ff2cdf3..88104e2 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -72,6 +72,8 @@
"vendor_public_library.go",
"testing.go",
+
+ "stub_library.go",
],
testSrcs: [
"cc_test.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index d32e4de..320e69b 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -334,8 +334,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 +359,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())
diff --git a/cc/binary.go b/cc/binary.go
index fbd293e..fa3966f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -373,7 +373,7 @@
if String(binary.Properties.Prefix_symbols) != "" {
afterPrefixSymbols := outputFile
outputFile = android.PathForModuleOut(ctx, "unprefixed", fileName)
- TransformBinaryPrefixSymbols(ctx, String(binary.Properties.Prefix_symbols), outputFile,
+ transformBinaryPrefixSymbols(ctx, String(binary.Properties.Prefix_symbols), outputFile,
builderFlags, afterPrefixSymbols)
}
@@ -428,13 +428,13 @@
linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
// Register link action.
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
builderFlags, outputFile, nil)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
- binary.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
+ binary.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, binary.getStem(ctx))
// Need to determine symlinks early since some targets (ie APEX) need this
// information but will not call 'install'
diff --git a/cc/builder.go b/cc/builder.go
index 81c09b1..439e372 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\"",
@@ -220,13 +236,15 @@
Labels: map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
ExecStrategy: "${config.REAbiDumperExecStrategy}",
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 +263,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 +277,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 +299,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 +333,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 +348,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 +359,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 +400,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 +430,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 +503,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{
@@ -562,6 +598,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 +676,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 +719,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 +800,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 +847,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 +859,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 +911,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 +940,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 +965,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 +983,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 +1020,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 +1032,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 +1052,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 +1070,3 @@
func gccCmd(toolchain config.Toolchain, cmd string) string {
return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
}
-
-func splitListForSize(list android.Paths, limit int) (lists []android.Paths, err error) {
- var i int
-
- start := 0
- bytes := 0
- for i = range list {
- l := len(list[i].String())
- if l > limit {
- return nil, fmt.Errorf("list element greater than size limit (%d)", limit)
- }
- if bytes+l > limit {
- lists = append(lists, list[start:i])
- start = i
- bytes = 0
- }
- bytes += l + 1 // count a space between each list element
- }
-
- lists = append(lists, list[start:])
-
- totalLen := 0
- for _, l := range lists {
- totalLen += len(l)
- }
- if totalLen != len(list) {
- panic(fmt.Errorf("Failed breaking up list, %d != %d", len(list), totalLen))
- }
- return lists, nil
-}
diff --git a/cc/cc.go b/cc/cc.go
index 6deb1b4..f922ea5 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -91,6 +91,14 @@
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 +111,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
@@ -122,6 +131,11 @@
DynamicLinker 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 +196,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 +216,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++
@@ -358,6 +380,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
@@ -412,6 +438,8 @@
ModuleContextIntf
}
+// feature represents additional (optional) steps to building cc-related modules, such as invocation
+// of clang-tidy.
type feature interface {
begin(ctx BaseModuleContext)
deps(ctx DepsContext, deps Deps) Deps
@@ -419,6 +447,9 @@
props() []interface{}
}
+// compiler is the interface for a compiler helper object. Different module decorators may implement
+// this helper differently. For example, compiling a `cc_library` may use a different build
+// statement than building a `toolchain_library`.
type compiler interface {
compilerInit(ctx BaseModuleContext)
compilerDeps(ctx DepsContext, deps Deps) Deps
@@ -430,6 +461,9 @@
compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects
}
+// linker is the interface for a linker decorator object. Individual module types can provide
+// their own implementation for this decorator, and thus specify custom logic regarding build
+// statements pertaining to linking.
type linker interface {
linkerInit(ctx BaseModuleContext)
linkerDeps(ctx DepsContext, deps Deps) Deps
@@ -445,15 +479,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)
@@ -584,7 +622,7 @@
genHeaderExportDepTag = dependencyTag{name: "gen header export"}
objDepTag = dependencyTag{name: "obj"}
linkerFlagsDepTag = dependencyTag{name: "linker flags file"}
- dynamicLinkerDepTag = dependencyTag{name: "dynamic linker"}
+ dynamicLinkerDepTag = installDependencyTag{name: "dynamic linker"}
reuseObjTag = dependencyTag{name: "reuse objects"}
staticVariantTag = dependencyTag{name: "static variant"}
vndkExtDepTag = dependencyTag{name: "vndk extends"}
@@ -626,7 +664,18 @@
// Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
-// to construct the output file. Behavior can be customized with a Customizer interface
+// to construct the output file. Behavior can be customized with a Customizer, or "decorator",
+// interface.
+//
+// To define a C/C++ related module, construct a new Module object and point its delegates to
+// type-specific structs. These delegates will be invoked to register module-specific build
+// statements which may be unique to the module type. For example, module.compiler.compile() should
+// be defined so as to register build statements which are responsible for compiling the module.
+//
+// Another example: to construct a cc_binary module, one can create a `cc.binaryDecorator` struct
+// which implements the `linker` and `installer` interfaces, and points the `linker` and `installer`
+// members of the cc.Module to this decorator. Thus, a cc_binary module has custom linker and
+// installer logic.
type Module struct {
android.ModuleBase
android.DefaultableModuleBase
@@ -643,18 +692,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
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7c98585..af9b943 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2913,114 +2913,6 @@
}
}
-var (
- str11 = "01234567891"
- str10 = str11[:10]
- str9 = str11[:9]
- str5 = str11[:5]
- str4 = str11[:4]
-)
-
-var splitListForSizeTestCases = []struct {
- in []string
- out [][]string
- size int
-}{
- {
- in: []string{str10},
- out: [][]string{{str10}},
- size: 10,
- },
- {
- in: []string{str9},
- out: [][]string{{str9}},
- size: 10,
- },
- {
- in: []string{str5},
- out: [][]string{{str5}},
- size: 10,
- },
- {
- in: []string{str11},
- out: nil,
- size: 10,
- },
- {
- in: []string{str10, str10},
- out: [][]string{{str10}, {str10}},
- size: 10,
- },
- {
- in: []string{str9, str10},
- out: [][]string{{str9}, {str10}},
- size: 10,
- },
- {
- in: []string{str10, str9},
- out: [][]string{{str10}, {str9}},
- size: 10,
- },
- {
- in: []string{str5, str4},
- out: [][]string{{str5, str4}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5},
- out: [][]string{{str5, str4}, {str5}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5, str4},
- out: [][]string{{str5, str4}, {str5, str4}},
- size: 10,
- },
- {
- in: []string{str5, str4, str5, str5},
- out: [][]string{{str5, str4}, {str5}, {str5}},
- size: 10,
- },
- {
- in: []string{str5, str5, str5, str4},
- out: [][]string{{str5}, {str5}, {str5, str4}},
- size: 10,
- },
- {
- in: []string{str9, str11},
- out: nil,
- size: 10,
- },
- {
- in: []string{str11, str9},
- out: nil,
- size: 10,
- },
-}
-
-func TestSplitListForSize(t *testing.T) {
- for _, testCase := range splitListForSizeTestCases {
- out, _ := splitListForSize(android.PathsForTesting(testCase.in...), testCase.size)
-
- var outStrings [][]string
-
- if len(out) > 0 {
- outStrings = make([][]string, len(out))
- for i, o := range out {
- outStrings[i] = o.Strings()
- }
- }
-
- if !reflect.DeepEqual(outStrings, testCase.out) {
- t.Errorf("incorrect output:")
- t.Errorf(" input: %#v", testCase.in)
- t.Errorf(" size: %d", testCase.size)
- t.Errorf(" expected: %#v", testCase.out)
- t.Errorf(" got: %#v", outStrings)
- }
- }
-}
-
var staticLinkDepOrderTestCases = []struct {
// This is a string representation of a map[moduleName][]moduleDependency .
// It models the dependencies declared in an Android.bp file.
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 e6ff1bd..b78bb6c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -649,7 +649,7 @@
func compileObjs(ctx android.ModuleContext, flags builderFlags,
subdir string, srcFiles, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
- return TransformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
+ return transformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
}
var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
diff --git a/cc/fuzz.go b/cc/fuzz.go
index dddfe94..6b17c48 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -246,25 +246,25 @@
fuzz.binaryDecorator.baseInstaller.install(ctx, file)
fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
intermediateDir := android.PathForModuleOut(ctx, "corpus")
for _, entry := range fuzz.corpus {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Base()))
}
- builder.Build(pctx, ctx, "copy_corpus", "copy corpus")
+ builder.Build("copy_corpus", "copy corpus")
fuzz.corpusIntermediateDir = intermediateDir
fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
- builder = android.NewRuleBuilder()
+ builder = android.NewRuleBuilder(pctx, ctx)
intermediateDir = android.PathForModuleOut(ctx, "data")
for _, entry := range fuzz.data {
builder.Command().Text("cp").
Input(entry).
Output(intermediateDir.Join(ctx, entry.Rel()))
}
- builder.Build(pctx, ctx, "copy_data", "copy data")
+ builder.Build("copy_data", "copy data")
fuzz.dataIntermediateDir = intermediateDir
if fuzz.Properties.Dictionary != nil {
@@ -419,12 +419,12 @@
sharedLibraries := collectAllSharedDependencies(ctx, module)
var files []fileToZip
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
// Package the corpora into a zipfile.
if fuzzModule.corpus != nil {
corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", corpusZip)
command.FlagWithRspFileInputList("-r ", fuzzModule.corpus)
@@ -434,7 +434,7 @@
// Package the data into a zipfile.
if fuzzModule.data != nil {
dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", dataZip)
for _, f := range fuzzModule.data {
intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
@@ -492,7 +492,7 @@
}
fuzzZip := archDir.Join(ctx, module.Name()+".zip")
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", fuzzZip)
for _, file := range files {
@@ -504,7 +504,7 @@
command.FlagWithInput("-f ", file.SourceFilePath)
}
- builder.Build(pctx, ctx, "create-"+fuzzZip.String(),
+ builder.Build("create-"+fuzzZip.String(),
"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
// Don't add modules to 'make haiku' that are set to not be exported to the
@@ -531,11 +531,11 @@
filesToZip := archDirs[archOs]
arch := archOs.arch
hostOrTarget := archOs.hostOrTarget
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip")
s.packages = append(s.packages, outputFile)
- command := builder.Command().BuiltTool(ctx, "soong_zip").
+ command := builder.Command().BuiltTool("soong_zip").
Flag("-j").
FlagWithOutput("-o ", outputFile).
Flag("-L 0") // No need to try and re-compress the zipfiles.
@@ -549,7 +549,7 @@
command.FlagWithInput("-f ", fileToZip.SourceFilePath)
}
- builder.Build(pctx, ctx, "create-fuzz-package-"+arch+"-"+hostOrTarget,
+ builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
"Create fuzz target packages for "+arch+"-"+hostOrTarget)
}
}
diff --git a/cc/gen.go b/cc/gen.go
index 5895b31..75b9e49 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -75,10 +75,9 @@
cmd := rule.Command()
// Fix up #line markers to not use the sbox temporary directory
- // android.SboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
+ // android.sboxPathForOutput(outDir, outDir) returns the sbox placeholder for the out
// directory itself, without any filename appended.
- // TODO(ccross): make this cmd.PathForOutput(outDir) instead.
- sboxOutDir := android.SboxPathForOutput(outDir, outDir)
+ sboxOutDir := cmd.PathForOutput(outDir)
sedCmd := "sed -i.bak 's#" + sboxOutDir + "#" + outDir.String() + "#'"
rule.Command().Text(sedCmd).Input(outFile)
rule.Command().Text(sedCmd).Input(headerFile)
@@ -137,7 +136,7 @@
}
cmd := rule.Command()
- cmd.BuiltTool(ctx, "aidl-cpp").
+ cmd.BuiltTool("aidl-cpp").
FlagWithDepFile("-d", depFile).
Flag("--ninja").
Flag(aidlFlags).
@@ -232,7 +231,7 @@
var yaccRule_ *android.RuleBuilder
yaccRule := func() *android.RuleBuilder {
if yaccRule_ == nil {
- yaccRule_ = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "yacc"),
+ yaccRule_ = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "yacc"),
android.PathForModuleGen(ctx, "yacc.sbox.textproto"))
}
return yaccRule_
@@ -262,7 +261,7 @@
deps = append(deps, headerFile)
case ".aidl":
if aidlRule == nil {
- aidlRule = android.NewRuleBuilder().Sbox(android.PathForModuleGen(ctx, "aidl"),
+ aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
}
cppFile := android.GenPathWithExt(ctx, "aidl", srcFile, "cpp")
@@ -285,11 +284,11 @@
}
if aidlRule != nil {
- aidlRule.Build(pctx, ctx, "aidl", "gen aidl")
+ aidlRule.Build("aidl", "gen aidl")
}
if yaccRule_ != nil {
- yaccRule_.Build(pctx, ctx, "yacc", "gen yacc")
+ yaccRule_.Build("yacc", "gen yacc")
}
if len(rsFiles) > 0 {
diff --git a/cc/library.go b/cc/library.go
index 7ae75f2..c626b03 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -897,9 +897,9 @@
}
}
- 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)
@@ -974,7 +974,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
library.tocFile = android.OptionalPathForPath(tocFile)
- TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
stripFlags := flagsToStripFlags(flags)
if library.stripper.NeedsStrip(ctx) {
@@ -1019,7 +1019,7 @@
if Bool(library.Properties.Sort_bss_symbols_by_size) {
unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
@@ -1029,7 +1029,7 @@
linkerDeps = append(linkerDeps, symbolOrderingFile)
}
- TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
+ transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
@@ -1039,7 +1039,7 @@
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.StaticLibObjs.sAbiDumpFiles...)
objs.sAbiDumpFiles = append(objs.sAbiDumpFiles, deps.WholeStaticLibObjs.sAbiDumpFiles...)
- library.coverageOutputFile = TransformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
+ library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
var staticAnalogue *StaticLibraryInfo
@@ -1115,7 +1115,7 @@
return refAbiDumpTextFile.Path()
}
if refAbiDumpGzipFile.Valid() {
- return UnzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
+ return unzipRefDump(ctx, refAbiDumpGzipFile.Path(), fileName)
}
return nil
}
@@ -1141,7 +1141,7 @@
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)
@@ -1150,7 +1150,7 @@
refAbiDumpFile := getRefAbiDumpFile(ctx, vndkVersion, fileName)
if refAbiDumpFile != nil {
- library.sAbiDiff = SourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
+ library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
refAbiDumpFile, fileName, exportedHeaderFlags,
Bool(library.Properties.Header_abi_checker.Check_all_apis),
ctx.isLlndk(ctx.Config()), ctx.isNdk(ctx.Config()), ctx.isVndkExt())
@@ -1754,13 +1754,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/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..37df4ba 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -146,7 +146,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
- TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
+ transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
if ctx.Windows() && p.properties.Windows_import_lib != nil {
// Consumers of this library actually links to the import library in build
diff --git a/cc/proto.go b/cc/proto.go
index 9c102a2..4466144 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -50,11 +50,11 @@
depFile := ccFile.ReplaceExtension(ctx, "d")
outputs := android.WritablePaths{ccFile, headerFile}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
- android.ProtoRule(ctx, rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
+ android.ProtoRule(rule, protoFile, flags.proto, protoDeps, outDir, depFile, outputs)
- rule.Build(pctx, ctx, "protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
+ rule.Build("protoc_"+protoFile.Rel(), "protoc "+protoFile.Rel())
return ccFile, headerFile
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index dbc52a5..22ee25f 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -169,14 +169,14 @@
Cfi *bool `android:"arch_variant"`
Integer_overflow *bool `android:"arch_variant"`
Misc_undefined []string `android:"arch_variant"`
- No_recover []string
- }
+ No_recover []string `android:"arch_variant"`
+ } `android:"arch_variant"`
// Sanitizers to run with flag configuration specified
Config struct {
// Enables CFI support flags for assembly-heavy libraries
Cfi_assembly_support *bool `android:"arch_variant"`
- }
+ } `android:"arch_variant"`
// value to pass to -fsanitize-recover=
Recover []string
diff --git a/cc/strip.go b/cc/strip.go
index e9aec91..1f10a74 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -54,7 +54,7 @@
func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
flags StripFlags, isStaticLib bool) {
if actx.Darwin() {
- TransformDarwinStrip(actx, in, out)
+ transformDarwinStrip(actx, in, out)
} else {
if Bool(stripper.StripProperties.Strip.Keep_symbols) {
flags.StripKeepSymbols = true
@@ -68,7 +68,7 @@
if actx.Config().Debuggable() && !flags.StripKeepMiniDebugInfo && !isStaticLib {
flags.StripAddGnuDebuglink = true
}
- TransformStrip(actx, in, out, flags)
+ transformStrip(actx, in, out, flags)
}
}
diff --git a/cc/stub_library.go b/cc/stub_library.go
new file mode 100644
index 0000000..76d236c
--- /dev/null
+++ b/cc/stub_library.go
@@ -0,0 +1,82 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+func init() {
+ // Use singleton type to gather all generated soong modules.
+ android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+}
+
+type stubLibraries struct {
+ stubLibraryMap map[string]bool
+}
+
+// Check if the module defines stub, or itself is stub
+func isStubTarget(m *Module) bool {
+ if m.IsStubs() || m.HasStubsVariants() {
+ return true
+ }
+
+ // Library which defines LLNDK Stub is also Stub target.
+ // Pure LLNDK Stub target would not contain any packaging
+ // with target file path.
+ if library, ok := m.linker.(*libraryDecorator); ok {
+ if library.Properties.Llndk_stubs != nil {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Get target file name to be installed from this module
+func getInstalledFileName(m *Module) string {
+ for _, ps := range m.PackagingSpecs() {
+ if name := ps.FileName(); name != "" {
+ return name
+ }
+ }
+ return ""
+}
+
+func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) {
+ // Visit all generated soong modules and store stub library file names.
+ ctx.VisitAllModules(func(module android.Module) {
+ if m, ok := module.(*Module); ok {
+ if isStubTarget(m) {
+ if name := getInstalledFileName(m); name != "" {
+ s.stubLibraryMap[name] = true
+ }
+ }
+ }
+ })
+}
+
+func stubLibrariesSingleton() android.Singleton {
+ return &stubLibraries{
+ stubLibraryMap: make(map[string]bool),
+ }
+}
+
+func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
+ // Convert stub library file names into Makefile variable.
+ ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedStringKeys(s.stubLibraryMap), " "))
+}
diff --git a/cc/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/vendor_snapshot.go b/cc/vendor_snapshot.go
index 6563f6e..3ef0b62 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -339,7 +339,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)
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: in,
@@ -1124,7 +1124,7 @@
ctx,
snapshotDir,
c.name+"-"+ctx.Config().DeviceName()+".zip")
- zipRule := android.NewRuleBuilder()
+ zipRule := android.NewRuleBuilder(pctx, ctx)
// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
snapshotOutputList := android.PathForOutput(
@@ -1140,12 +1140,12 @@
zipRule.Temporary(snapshotOutputList)
zipRule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipPath).
FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
FlagWithInput("-l ", snapshotOutputList)
- zipRule.Build(pctx, ctx, zipPath.String(), c.name+" snapshot "+zipPath.String())
+ zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
c.snapshotZipFile = android.OptionalPathForPath(zipPath)
}
diff --git a/cc/vndk.go b/cc/vndk.go
index 4a005f3..d57cdf7 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -727,7 +727,7 @@
var txtBuilder strings.Builder
for idx, k := range android.SortedStringKeys(m) {
if idx > 0 {
- txtBuilder.WriteString("\\n")
+ txtBuilder.WriteString("\n")
}
txtBuilder.WriteString(k)
txtBuilder.WriteString(" ")
@@ -762,7 +762,7 @@
})
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
snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "android-vndk-"+ctx.DeviceConfig().DeviceArch()+"_list")
@@ -775,12 +775,12 @@
zipRule.Temporary(snapshotOutputList)
zipRule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
FlagWithOutput("-o ", zipPath).
FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
FlagWithInput("-l ", snapshotOutputList)
- zipRule.Build(pctx, ctx, zipPath.String(), "vndk snapshot "+zipPath.String())
+ zipRule.Build(zipPath.String(), "vndk snapshot "+zipPath.String())
zipRule.DeleteTemporaryFiles()
c.vndkSnapshotZipFile = android.OptionalPathForPath(zipPath)
}
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index c0320eb..e6e2ad8 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -154,7 +154,7 @@
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
- TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+ transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
p.androidMkSuffix = p.NameSuffix()
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 633c6b2..a4f57ea 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -16,6 +16,8 @@
import (
"bytes"
+ "crypto/sha1"
+ "encoding/hex"
"errors"
"flag"
"fmt"
@@ -121,7 +123,22 @@
return fmt.Errorf("failed to create %q: %w", sandboxesRoot, err)
}
- tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox")
+ // 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)))
+
+ err = os.RemoveAll(tempDir)
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(tempDir, 0777)
if err != nil {
return fmt.Errorf("failed to create temporary dir in %q: %w", sandboxesRoot, err)
}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index b88803a..d758de2 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -79,7 +79,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.
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 22f712c..3759217 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -22,17 +22,214 @@
"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,
}
@@ -55,30 +252,6 @@
// last). We use the converntional "current" SDK level (10000), but any big number would do as well.
const AnySdkVersion int = android.FutureApiLevelInt
-// ClassLoaderContext is a tree of libraries used by the dexpreopted module with their dependencies.
-// The context is used by dex2oat to compile the module and recorded in the AOT-compiled files, so
-// that it can be checked agains the run-time class loader context on device. If there is a mismatch
-// at runtime, AOT-compiled code is rejected.
-type ClassLoaderContext struct {
- // The name of the library (same as the name of the module that contains it).
- 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 class loader subcontexts for dependencies.
- Subcontexts []*ClassLoaderContext
-}
-
-// ClassLoaderContextMap is a map from SDK version to a class loader context.
-// There is a special entry with key AnySdkVersion that stores unconditional class loader context.
-// Other entries store conditional contexts that should be added for some apps that have
-// targetSdkVersion in the manifest lower than the key SDK version.
-type ClassLoaderContextMap map[int][]*ClassLoaderContext
-
// 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 {
@@ -257,6 +430,7 @@
return true, nil
}
+// 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 {
@@ -296,6 +470,7 @@
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
@@ -320,7 +495,7 @@
return clcHost, clcTarget, paths
}
-// Paths to a <uses-library> on host and on device.
+// JSON representation of <uses-library> paths on host and on device.
type jsonLibraryPath struct {
Host string
Device string
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 65380fe..b0a684e 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -51,7 +51,7 @@
// GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
// ModuleConfig. The produced files and their install locations will be available through rule.Installs().
-func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig,
+func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
defer func() {
@@ -67,7 +67,7 @@
}
}()
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
generateProfile := module.ProfileClassListing.Valid() && !global.DisableGenerateProfile
generateBootProfile := module.ProfileBootListing.Valid() && !global.DisableGenerateProfile
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index e89f045..32c4f84 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -27,6 +27,7 @@
"android/soong/android"
"android/soong/dexpreopt"
+ "github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
)
@@ -38,12 +39,16 @@
outDir = flag.String("out_dir", "", "path to output directory")
)
-type pathContext struct {
+type builderContext struct {
config android.Config
}
-func (x *pathContext) Config() android.Config { return x.config }
-func (x *pathContext) AddNinjaFileDeps(...string) {}
+func (x *builderContext) Config() android.Config { return x.config }
+func (x *builderContext) AddNinjaFileDeps(...string) {}
+func (x *builderContext) Build(android.PackageContext, android.BuildParams) {}
+func (x *builderContext) Rule(android.PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
+ return nil
+}
func main() {
flag.Parse()
@@ -76,7 +81,7 @@
usage("--module configuration file is required")
}
- ctx := &pathContext{android.NullConfig(*outDir)}
+ ctx := &builderContext{android.NullConfig(*outDir)}
globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
if err != nil {
@@ -133,7 +138,7 @@
writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
}
-func writeScripts(ctx android.PathContext, globalSoong *dexpreopt.GlobalSoongConfig,
+func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
if err != nil {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index feabd70..59278fd 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -60,7 +60,7 @@
func TestDexPreopt(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
@@ -82,7 +82,7 @@
func TestDexPreoptSystemOther(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
systemModule := testSystemModuleConfig(ctx, "Stest")
@@ -142,7 +142,7 @@
func TestDexPreoptProfile(t *testing.T) {
config := android.TestConfig("out", nil, "", nil)
- ctx := android.PathContextForTesting(config)
+ ctx := android.BuilderContextForTesting(config)
globalSoong := GlobalSoongConfigForTests(config)
global := GlobalConfigForTests(ctx)
module := testSystemModuleConfig(ctx, "test")
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index a1605b4..c6181bc 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -18,6 +18,8 @@
"fmt"
"android/soong/android"
+
+ "github.com/google/blueprint"
)
func init() {
@@ -27,8 +29,16 @@
type filesystem struct {
android.ModuleBase
android.PackagingBase
+
+ output android.OutputPath
+ installDir android.InstallPath
}
+// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
+// image. The filesystem images are expected to be mounted in the target device, which means the
+// modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
+// The modules are placed in the filesystem image just like they are installed to the ordinary
+// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func filesystemFactory() android.Module {
module := &filesystem{}
android.InitPackageModule(module)
@@ -36,8 +46,14 @@
return module
}
+var dependencyTag = struct{ blueprint.BaseDependencyTag }{}
+
func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) {
- f.AddDeps(ctx)
+ f.AddDeps(ctx, dependencyTag)
+}
+
+func (f *filesystem) installFileName() string {
+ return f.BaseModuleName() + ".img"
}
var pctx = android.NewPackageContext("android/soong/filesystem")
@@ -47,9 +63,9 @@
f.CopyDepsToZip(ctx, zipFile)
rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- builder := android.NewRuleBuilder()
+ builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().
- BuiltTool(ctx, "zipsync").
+ BuiltTool("zipsync").
FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
Input(zipFile)
@@ -64,13 +80,32 @@
Text(">").Output(propFile).
Implicit(mkuserimg)
- image := android.PathForModuleOut(ctx, "filesystem.img").OutputPath
- builder.Command().BuiltTool(ctx, "build_image").
+ f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+ builder.Command().BuiltTool("build_image").
Text(rootDir.String()). // input directory
Input(propFile).
- Output(image).
+ Output(f.output).
Text(rootDir.String()) // directory where to find fs_config_files|dirs
// rootDir is not deleted. Might be useful for quick inspection.
- builder.Build(pctx, ctx, "build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+ builder.Build("build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+
+ f.installDir = android.PathForModuleInstall(ctx, "etc")
+ ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+}
+
+var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
+ return []android.AndroidMkEntries{android.AndroidMkEntries{
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(f.output),
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+ },
+ },
+ }}
}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e5ee3fc..93938c9 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -342,8 +342,27 @@
return
}
+ // Pick a unique path outside the task.genDir for the sbox manifest textproto,
+ // a unique rule name, and the user-visible description.
+ manifestName := "genrule.sbox.textproto"
+ desc := "generate"
+ name := "generator"
+ if task.shards > 0 {
+ manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
+ desc += " " + strconv.Itoa(task.shard)
+ name += strconv.Itoa(task.shard)
+ } else if len(task.out) == 1 {
+ desc += " " + task.out[0].Base()
+ }
+
+ manifestPath := android.PathForModuleOut(ctx, manifestName)
+
+ // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)
+ cmd := rule.Command()
+
for _, out := range task.out {
- addLocationLabel(out.Rel(), []string{android.SboxPathForOutput(out, task.genDir)})
+ addLocationLabel(out.Rel(), []string{cmd.PathForOutput(out)})
}
referencedDepfile := false
@@ -374,7 +393,7 @@
case "out":
var sandboxOuts []string
for _, out := range task.out {
- sandboxOuts = append(sandboxOuts, android.SboxPathForOutput(out, task.genDir))
+ sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
}
return strings.Join(sandboxOuts, " "), nil
case "depfile":
@@ -384,7 +403,7 @@
}
return "__SBOX_DEPFILE__", nil
case "genDir":
- return android.SboxPathForOutput(task.genDir, task.genDir), nil
+ return cmd.PathForOutput(task.genDir), nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -426,24 +445,6 @@
}
g.rawCommands = append(g.rawCommands, rawCommand)
- // Pick a unique path outside the task.genDir for the sbox manifest textproto,
- // a unique rule name, and the user-visible description.
- manifestName := "genrule.sbox.textproto"
- desc := "generate"
- name := "generator"
- if task.shards > 0 {
- manifestName = "genrule_" + strconv.Itoa(task.shard) + ".sbox.textproto"
- desc += " " + strconv.Itoa(task.shard)
- name += strconv.Itoa(task.shard)
- } else if len(task.out) == 1 {
- desc += " " + task.out[0].Base()
- }
-
- manifestPath := android.PathForModuleOut(ctx, manifestName)
-
- // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
- rule := android.NewRuleBuilder().Sbox(task.genDir, manifestPath)
- cmd := rule.Command()
cmd.Text(rawCommand)
cmd.ImplicitOutputs(task.out)
cmd.Implicits(task.in)
@@ -454,7 +455,7 @@
}
// Create the rule to run the genrule command inside sbox.
- rule.Build(pctx, ctx, name, desc)
+ rule.Build(name, desc)
if len(task.copyTo) > 0 {
// If copyTo is set, multiple shards need to be copied into a single directory.
@@ -612,6 +613,10 @@
}
genDir := android.PathForModuleGen(ctx, genSubDir)
+ // TODO(ccross): this RuleBuilder is a hack to be able to call
+ // rule.Command().PathForOutput. Replace this with passing the rule into the
+ // generator.
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil)
for _, in := range shard {
outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -634,11 +639,11 @@
case "in":
return in.String(), nil
case "out":
- return android.SboxPathForOutput(outFile, genDir), nil
+ return rule.Command().PathForOutput(outFile), nil
case "depfile":
// Generate a depfile for each output file. Store the list for
// later in order to combine them all into a single depfile.
- depFile := android.SboxPathForOutput(outFile.ReplaceExtension(ctx, "d"), genDir)
+ depFile := rule.Command().PathForOutput(outFile.ReplaceExtension(ctx, "d"))
commandDepFiles = append(commandDepFiles, depFile)
return depFile, nil
default:
@@ -703,7 +708,7 @@
Shard_size *int64
}
-const defaultShardSize = 100
+const defaultShardSize = 50
func NewGenRule() *Module {
properties := &genRuleProperties{}
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/androidmk.go b/java/androidmk.go
index 9ad3f9b..fc573c8 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -139,9 +139,9 @@
func testSuiteComponent(entries *android.AndroidMkEntries, test_suites []string) {
entries.SetString("LOCAL_MODULE_TAGS", "tests")
if len(test_suites) > 0 {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...)
+ entries.AddCompatibilityTestSuites(test_suites...)
} else {
- entries.SetString("LOCAL_COMPATIBILITY_SUITE", "null-suite")
+ entries.AddCompatibilityTestSuites("null-suite")
}
}
diff --git a/java/app.go b/java/app.go
index 3384bbd..4bf9d33 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1115,8 +1115,8 @@
}
fixedConfig := android.PathForModuleOut(ctx, "test_config_fixer", "AndroidTest.xml")
- rule := android.NewRuleBuilder()
- command := rule.Command().BuiltTool(ctx, "test_config_fixer").Input(testConfig).Output(fixedConfig)
+ rule := android.NewRuleBuilder(pctx, ctx)
+ command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig)
fixNeeded := false
if ctx.ModuleName() != a.installApkName {
@@ -1131,7 +1131,7 @@
}
if fixNeeded {
- rule.Build(pctx, ctx, "fix_test_config", "fix test config")
+ rule.Build("fix_test_config", "fix test config")
return fixedConfig
}
return testConfig
@@ -1440,15 +1440,15 @@
})
return
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'lib/**/*.so'").
Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build(pctx, ctx, "uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
+ rule.Build("uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
}
// Returns whether this module should have the dex file stored uncompressed in the APK.
@@ -1467,15 +1467,15 @@
func (a *AndroidAppImport) uncompressDex(
ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'classes*.dex'").
Textf(`; else cp -f %s %s; fi`, inputPath, outputPath)
- rule.Build(pctx, ctx, "uncompress-dex", "Uncompress dex files")
+ rule.Build("uncompress-dex", "Uncompress dex files")
}
func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2015,8 +2015,8 @@
func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
- rule := android.NewRuleBuilder()
- cmd := rule.Command().BuiltTool(ctx, "manifest_check").
+ rule := android.NewRuleBuilder(pctx, ctx)
+ cmd := rule.Command().BuiltTool("manifest_check").
Flag("--enforce-uses-libraries").
Input(manifest).
FlagWithOutput("-o ", outputFile)
@@ -2029,7 +2029,7 @@
cmd.FlagWithArg("--optional-uses-library ", lib)
}
- rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>")
+ rule.Build("verify_uses_libraries", "verify <uses-library>")
return outputFile
}
@@ -2039,7 +2039,7 @@
func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
aapt := ctx.Config().HostToolPath(ctx, "aapt")
rule.Command().
Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
@@ -2048,7 +2048,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/boot_jars.go b/java/boot_jars.go
index e706547..823275b 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -85,8 +85,8 @@
timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
- rule := android.NewRuleBuilder()
- checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars").
+ rule := android.NewRuleBuilder(pctx, ctx)
+ checkBootJars := rule.Command().BuiltTool("check_boot_jars").
Input(ctx.Config().HostToolPath(ctx, "dexdump")).
Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
@@ -109,7 +109,7 @@
}
checkBootJars.Text("&& touch").Output(timestamp)
- rule.Build(pctx, ctx, "boot_jars_package_check", "check boot jar packages")
+ rule.Build("boot_jars_package_check", "check boot jar packages")
// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
ctx.Phony("check-boot-jars", timestamp)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index a21fb76..67738d4 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -216,7 +216,7 @@
return dexJarFile
}
- dexpreoptRule.Build(pctx, ctx, "dexpreopt", "dexpreopt")
+ dexpreoptRule.Build("dexpreopt", "dexpreopt")
d.builtInstalled = dexpreoptRule.Installs().String()
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f9975ba..f16ddf1 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
@@ -334,9 +514,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 +539,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 +568,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 +610,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 +689,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 +713,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 +744,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 +766,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
@@ -609,7 +794,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
@@ -654,7 +839,7 @@
// WriteFileRule automatically adds the last end-of-line.
android.WriteFileRule(ctx, updatableBcpPackages, strings.Join(updatablePackages, "\n"))
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
rule.Install(updatableBcpPackages, "/system/etc/"+updatableBcpPackagesName)
// TODO: Rename `profileInstalls` to `extraInstalls`?
@@ -678,25 +863,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)
}
@@ -717,7 +902,9 @@
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())
@@ -731,6 +918,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/droiddoc.go b/java/droiddoc.go
index da8489a..9c88a3c 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -671,7 +671,7 @@
j.stubsSrcJar = nil
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Text(outDir.String())
rule.Command().Text("mkdir -p").Text(outDir.String())
@@ -689,7 +689,7 @@
Flag("-Xdoclint:none")
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", j.docZip).
@@ -700,7 +700,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", "javadoc")
+ rule.Build("javadoc", "javadoc")
}
//
@@ -845,7 +845,7 @@
outDir, srcJarDir, srcJarList android.Path, sourcepaths android.Paths) *android.RuleBuilderCommand {
cmd := rule.Command().
- BuiltTool(ctx, "soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
+ BuiltTool("soong_javac_wrapper").Tool(config.JavadocCmd(ctx)).
Flag(config.JavacVmFlags).
FlagWithArg("-encoding ", "UTF-8").
FlagWithRspFileInputList("@", srcs).
@@ -914,7 +914,7 @@
dokkaClasspath := append(bootclasspath.Paths(), classpath.Paths()...)
return rule.Command().
- BuiltTool(ctx, "dokka").
+ BuiltTool("dokka").
Flag(config.JavacVmFlags).
Flag(srcJarDir.String()).
FlagWithInputList("-classpath ", dokkaClasspath, ":").
@@ -934,7 +934,7 @@
outDir := android.PathForModuleOut(ctx, "out")
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
@@ -968,7 +968,7 @@
}
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", d.docZip).
@@ -979,7 +979,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "javadoc", desc)
+ rule.Build("javadoc", desc)
}
//
@@ -1278,7 +1278,7 @@
}).NoVarTemplate(ctx.Config()))
}
- cmd.BuiltTool(ctx, "metalava").
+ cmd.BuiltTool("metalava").
Flag(config.JavacVmFlags).
Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
FlagWithArg("-encoding ", "UTF-8").
@@ -1333,7 +1333,7 @@
srcJarDir := android.PathForModuleOut(ctx, "srcjars")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
if BoolDefault(d.properties.High_mem, false) {
// This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
@@ -1480,19 +1480,19 @@
cmd.FlagWithArg("--error-message:compatibility:released ", msg)
}
- impRule := android.NewRuleBuilder()
+ impRule := android.NewRuleBuilder(pctx, ctx)
impCmd := impRule.Command()
// An action that copies the ninja generated rsp file to a new location. This allows us to
// add a large number of inputs to a file without exceeding bash command length limits (which
// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
// rsp file to be ${output}.rsp.
impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
- impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
+ impRule.Build("implicitsGen", "implicits generation")
cmd.Implicit(implicitsRsp)
if generateStubs {
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-jar").
FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
@@ -1503,7 +1503,7 @@
if Bool(d.properties.Write_sdk_values) {
d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-d").
FlagWithOutput("-o ", d.metadataZip).
@@ -1524,7 +1524,7 @@
zipSyncCleanupCmd(rule, srcJarDir)
- rule.Build(pctx, ctx, "metalava", "metalava merged")
+ rule.Build("metalava", "metalava merged")
if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
@@ -1542,7 +1542,7 @@
d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "check_current_api.timestamp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
// Diff command line.
// -F matches the closest "opening" line, such as "package android {"
@@ -1564,7 +1564,7 @@
` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
` to the new methods, etc. shown in the above diff.\n\n`+
` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
- ` make %s-update-current-api\n\n`+
+ ` m %s-update-current-api\n\n`+
` To submit the revised current.txt to the main Android repository,\n`+
` you will need approval.\n`+
`******************************\n`, ctx.ModuleName())
@@ -1576,12 +1576,12 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "metalavaCurrentApiCheck", "check current API")
+ rule.Build("metalavaCurrentApiCheck", "check current API")
d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "update_current_api.timestamp")
// update API rule
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("( true")
@@ -1602,7 +1602,7 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "metalavaCurrentApiUpdate", "update current API")
+ rule.Build("metalavaCurrentApiUpdate", "update current API")
}
if String(d.properties.Check_nullability_warnings) != "" {
@@ -1625,7 +1625,7 @@
` and submitting the updated file as part of your change.`,
d.nullabilityWarningsFile, checkNullabilityWarnings)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("(").
@@ -1637,7 +1637,7 @@
Text("; exit 38").
Text(")")
- rule.Build(pctx, ctx, "nullabilityWarningsCheck", "nullability warnings check")
+ rule.Build("nullabilityWarningsCheck", "nullability warnings check")
}
}
@@ -1722,7 +1722,7 @@
rule.Temporary(srcJarList)
- rule.Command().BuiltTool(ctx, "zipsync").
+ rule.Command().BuiltTool("zipsync").
FlagWithArg("-d ", srcJarDir.String()).
FlagWithOutput("-l ", srcJarList).
FlagWithArg("-f ", `"*.java"`).
@@ -1783,9 +1783,9 @@
srcGlob := localSrcDir + "/**/*"
srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-write_if_changed").
Flag("-jar").
FlagWithOutput("-o ", p.stubsSrcJar).
@@ -1794,7 +1794,7 @@
rule.Restat()
- rule.Build(pctx, ctx, "zip src", "Create srcjar from prebuilt source")
+ rule.Build("zip src", "Create srcjar from prebuilt source")
}
func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
diff --git a/java/gen.go b/java/gen.go
index d50a665..5766a94 100644
--- a/java/gen.go
+++ b/java/gen.go
@@ -57,7 +57,7 @@
outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
@@ -98,7 +98,7 @@
ruleDesc += " " + strconv.Itoa(i)
}
- rule.Build(pctx, ctx, ruleName, ruleDesc)
+ rule.Build(ruleName, ruleDesc)
}
return srcJarFiles
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 63b801a..71f1e57 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -135,12 +135,12 @@
})
h.metadataCSVPath = metadataCSV
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithInput("--zip_input=", classesJar).
FlagWithOutput("--output=", indexCSV)
- rule.Build(pctx, ctx, "merged-hiddenapi-index", "Merged Hidden API index")
+ rule.Build("merged-hiddenapi-index", "Merged Hidden API index")
h.indexCSVPath = indexCSV
}
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 8f0e09c..ce8410e 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -189,7 +189,7 @@
}
// Singleton rule which applies hiddenapi on all boot class path dex files.
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).stubFlags
tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
@@ -208,7 +208,7 @@
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIStubFlagsFile", "hiddenapi stub flags")
+ rule.Build("hiddenAPIStubFlagsFile", "hiddenapi stub flags")
}
// flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
@@ -236,7 +236,7 @@
ctx.Errorf("Failed to find combined-removed-dex.")
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).flags
tempPath := android.PathForOutput(ctx, outputPath.Rel()+".tmp")
@@ -266,7 +266,7 @@
commitChangeForRestat(rule, tempPath, outputPath)
- rule.Build(pctx, ctx, "hiddenAPIFlagsFile", "hiddenapi flags")
+ rule.Build("hiddenAPIFlagsFile", "hiddenapi flags")
return outputPath
}
@@ -274,14 +274,14 @@
// emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that
// have a partial manifest without frameworks/base but still need to build a boot image.
func emptyFlagsRule(ctx android.SingletonContext) android.Path {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).flags
rule.Command().Text("rm").Flag("-f").Output(outputPath)
rule.Command().Text("touch").Output(outputPath)
- rule.Build(pctx, ctx, "emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
+ rule.Build("emptyHiddenAPIFlagsFile", "empty hiddenapi flags")
return outputPath
}
@@ -299,16 +299,16 @@
}
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := hiddenAPISingletonPaths(ctx).metadata
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithOutput("--output=", outputPath).
Inputs(metadataCSV)
- rule.Build(pctx, ctx, "hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
+ rule.Build("hiddenAPIGreylistMetadataFile", "hiddenapi greylist metadata")
return outputPath
}
@@ -399,13 +399,13 @@
}
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
- BuiltTool(ctx, "merge_csv").
+ BuiltTool("merge_csv").
FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index).
Inputs(indexes)
- rule.Build(pctx, ctx, "singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
+ rule.Build("singleton-merged-hiddenapi-index", "Singleton merged Hidden API index")
h.index = hiddenAPISingletonPaths(ctx).index
}
diff --git a/java/java.go b/java/java.go
index 8738e00..95a4e88 100644
--- a/java/java.go
+++ b/java/java.go
@@ -772,6 +772,37 @@
libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
+ if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
+ // Require java_sdk_library at inter-partition java dependency to ensure stable
+ // interface between partitions. If inter-partition java_library dependency is detected,
+ // raise build error because java_library doesn't have a stable interface.
+ //
+ // Inputs:
+ // PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
+ // if true, enable enforcement
+ // PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
+ // exception list of java_library names to allow inter-partition dependency
+ for idx, lib := range j.properties.Libs {
+ if libDeps[idx] == nil {
+ continue
+ }
+
+ if _, ok := syspropPublicStubs[lib]; ok {
+ continue
+ }
+
+ if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
+ // java_sdk_library is always allowed at inter-partition dependency.
+ // So, skip check.
+ if _, ok := javaDep.(*SdkLibrary); ok {
+ continue
+ }
+
+ j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
+ }
+ }
+ }
+
// For library dependencies that are component libraries (like stubs), add the implementation
// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
for _, dep := range libDeps {
@@ -3049,21 +3080,21 @@
dexOutputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar")
if j.dexpreopter.uncompressedDex {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
temporary := android.PathForModuleOut(ctx, ctx.ModuleName()+".jar.unaligned")
rule.Temporary(temporary)
// use zip2zip to uncompress classes*.dex files
rule.Command().
- BuiltTool(ctx, "zip2zip").
+ BuiltTool("zip2zip").
FlagWithInput("-i ", inputJar).
FlagWithOutput("-o ", temporary).
FlagWithArg("-0 ", "'classes*.dex'")
// use zipalign to align uncompressed classes*.dex files
rule.Command().
- BuiltTool(ctx, "zipalign").
+ BuiltTool("zipalign").
Flag("-f").
Text("4").
Input(temporary).
@@ -3071,7 +3102,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,
diff --git a/java/java_test.go b/java/java_test.go
index 83db443..f7cf03f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -805,6 +805,165 @@
})
}
+func TestJavaSdkLibraryEnforce(t *testing.T) {
+ partitionToBpOption := func(partition string) string {
+ switch partition {
+ case "system":
+ return ""
+ case "vendor":
+ return "soc_specific: true,"
+ case "product":
+ return "product_specific: true,"
+ default:
+ panic("Invalid partition group name: " + partition)
+ }
+ }
+
+ type testConfigInfo struct {
+ libraryType string
+ fromPartition string
+ toPartition string
+ enforceVendorInterface bool
+ enforceProductInterface bool
+ enforceJavaSdkLibraryCheck bool
+ allowList []string
+ }
+
+ createTestConfig := func(info testConfigInfo) android.Config {
+ bpFileTemplate := `
+ java_library {
+ name: "foo",
+ srcs: ["foo.java"],
+ libs: ["bar"],
+ sdk_version: "current",
+ %s
+ }
+
+ %s {
+ name: "bar",
+ srcs: ["bar.java"],
+ sdk_version: "current",
+ %s
+ }
+ `
+
+ bpFile := fmt.Sprintf(bpFileTemplate,
+ partitionToBpOption(info.fromPartition),
+ info.libraryType,
+ partitionToBpOption(info.toPartition))
+
+ config := testConfig(nil, bpFile, nil)
+ configVariables := config.TestProductVariables
+
+ configVariables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
+ if info.enforceVendorInterface {
+ configVariables.DeviceVndkVersion = proptools.StringPtr("current")
+ }
+ configVariables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
+ configVariables.InterPartitionJavaLibraryAllowList = info.allowList
+
+ return config
+ }
+
+ isValidDependency := func(configInfo testConfigInfo) bool {
+ if configInfo.enforceVendorInterface == false {
+ return true
+ }
+
+ if configInfo.enforceJavaSdkLibraryCheck == false {
+ return true
+ }
+
+ if inList("bar", configInfo.allowList) {
+ return true
+ }
+
+ if configInfo.libraryType == "java_library" {
+ if configInfo.fromPartition != configInfo.toPartition {
+ if !configInfo.enforceProductInterface &&
+ ((configInfo.fromPartition == "system" && configInfo.toPartition == "product") ||
+ (configInfo.fromPartition == "product" && configInfo.toPartition == "system")) {
+ return true
+ }
+ return false
+ }
+ }
+
+ return true
+ }
+
+ errorMessage := "is not allowed across the partitions"
+
+ allPartitionCombinations := func() [][2]string {
+ var result [][2]string
+ partitions := []string{"system", "vendor", "product"}
+
+ for _, fromPartition := range partitions {
+ for _, toPartition := range partitions {
+ result = append(result, [2]string{fromPartition, toPartition})
+ }
+ }
+
+ return result
+ }
+
+ allFlagCombinations := func() [][3]bool {
+ var result [][3]bool
+ flagValues := [2]bool{false, true}
+
+ for _, vendorInterface := range flagValues {
+ for _, productInterface := range flagValues {
+ for _, enableEnforce := range flagValues {
+ result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce})
+ }
+ }
+ }
+
+ return result
+ }
+
+ for _, libraryType := range []string{"java_library", "java_sdk_library"} {
+ for _, partitionValues := range allPartitionCombinations() {
+ for _, flagValues := range allFlagCombinations() {
+ testInfo := testConfigInfo{
+ libraryType: libraryType,
+ fromPartition: partitionValues[0],
+ toPartition: partitionValues[1],
+ enforceVendorInterface: flagValues[0],
+ enforceProductInterface: flagValues[1],
+ enforceJavaSdkLibraryCheck: flagValues[2],
+ }
+
+ if isValidDependency(testInfo) {
+ testJavaWithConfig(t, createTestConfig(testInfo))
+ } else {
+ testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo))
+ }
+ }
+ }
+ }
+
+ testJavaWithConfig(t, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ allowList: []string{"bar"},
+ }))
+
+ testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+ libraryType: "java_library",
+ fromPartition: "vendor",
+ toPartition: "system",
+ enforceVendorInterface: true,
+ enforceProductInterface: true,
+ enforceJavaSdkLibraryCheck: true,
+ allowList: []string{"foo"},
+ }))
+}
+
func TestDefaults(t *testing.T) {
ctx, _ := testJava(t, `
java_defaults {
diff --git a/java/kotlin.go b/java/kotlin.go
index e8c030a..8067ad5 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -63,9 +63,9 @@
// we can't use the rsp file because it is already being used for srcs.
// Insert a second rule to write out the list of resources to a file.
commonSrcsList := android.PathForModuleOut(ctx, "kotlinc_common_srcs.list")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("cp").FlagWithRspFileInputList("", commonSrcFiles).Output(commonSrcsList)
- rule.Build(pctx, ctx, "kotlin_common_srcs_list", "kotlin common_srcs list")
+ rule.Build("kotlin_common_srcs_list", "kotlin common_srcs list")
return android.OptionalPathForPath(commonSrcsList)
}
return android.OptionalPath{}
diff --git a/java/lint.go b/java/lint.go
index 11f92e5..cd2a904 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -176,9 +176,9 @@
// we can't use the rsp file because it is already being used for srcs.
// Insert a second rule to write out the list of resources to a file.
resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
- resListRule := android.NewRuleBuilder()
+ resListRule := android.NewRuleBuilder(pctx, ctx)
resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
- resListRule.Build(pctx, ctx, "lint_resources_list", "lint resources list")
+ resListRule.Build("lint_resources_list", "lint resources list")
deps = append(deps, l.resources...)
}
@@ -192,7 +192,7 @@
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
cmd := rule.Command().
- BuiltTool(ctx, "lint-project-xml").
+ BuiltTool("lint-project-xml").
FlagWithOutput("--project_out ", projectXMLPath).
FlagWithOutput("--config_out ", configXMLPath).
FlagWithArg("--name ", ctx.ModuleName())
@@ -284,7 +284,7 @@
}
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
if l.manifest == nil {
manifest := l.generateManifest(ctx, rule)
@@ -347,7 +347,7 @@
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
- rule.Build(pctx, ctx, "lint", "lint")
+ rule.Build("lint", "lint")
l.outputs = lintOutputs{
html: html,
@@ -511,12 +511,12 @@
return paths[i].String() < paths[j].String()
})
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().BuiltTool(ctx, "soong_zip").
+ rule.Command().BuiltTool("soong_zip").
FlagWithOutput("-o ", outputPath).
FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
FlagWithRspFileInputList("-r ", paths)
- rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+ rule.Build(outputPath.Base(), outputPath.Base())
}
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index cb8e684..9bc821d 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -86,15 +86,15 @@
return
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
outputPath := platformCompatConfigPath(ctx)
rule.Command().
- BuiltTool(ctx, "process-compat-config").
+ BuiltTool("process-compat-config").
FlagForEachInput("--xml ", compatConfigMetadata).
FlagWithOutput("--merged-config ", outputPath)
- rule.Build(pctx, ctx, "merged-compat-config", "Merge compat config")
+ rule.Build("merged-compat-config", "Merge compat config")
p.metadata = outputPath
}
@@ -106,7 +106,7 @@
}
func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
configFileName := p.Name() + ".xml"
metadataFileName := p.Name() + "_meta.xml"
@@ -115,13 +115,13 @@
path := android.PathForModuleSrc(ctx, String(p.properties.Src))
rule.Command().
- BuiltTool(ctx, "process-compat-config").
+ BuiltTool("process-compat-config").
FlagWithInput("--jar ", path).
FlagWithOutput("--device-config ", p.configFile).
FlagWithOutput("--merged-config ", p.metadataFile)
p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
- rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
+ rule.Build(configFileName, "Extract compat/compat_config.xml and install it")
}
diff --git a/java/proto.go b/java/proto.go
index 4d735eb..cc9abbe 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -34,7 +34,7 @@
outDir := srcJarFile.ReplaceExtension(ctx, "tmp")
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().Text("rm -rf").Flag(outDir.String())
rule.Command().Text("mkdir -p").Flag(outDir.String())
@@ -42,13 +42,13 @@
for _, protoFile := range shard {
depFile := srcJarFile.InSameDir(ctx, protoFile.String()+".d")
rule.Command().Text("mkdir -p").Flag(filepath.Dir(depFile.String()))
- android.ProtoRule(ctx, rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
+ android.ProtoRule(rule, protoFile, flags, flags.Deps, outDir, depFile, nil)
}
// Proto generated java files have an unknown package name in the path, so package the entire output directory
// into a srcjar.
rule.Command().
- BuiltTool(ctx, "soong_zip").
+ BuiltTool("soong_zip").
Flag("-jar").
Flag("-write_if_changed").
FlagWithOutput("-o ", srcJarFile).
@@ -66,7 +66,7 @@
ruleDesc += " " + strconv.Itoa(i)
}
- rule.Build(pctx, ctx, ruleName, ruleDesc)
+ rule.Build(ruleName, ruleDesc)
}
return srcJarFiles
diff --git a/java/robolectric.go b/java/robolectric.go
index 62d1d99..419efda 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,
diff --git a/java/sdk.go b/java/sdk.go
index 971791f..32a4b5a 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -544,7 +544,7 @@
commitChangeForRestat(rule, tempPath, combinedAidl)
- rule.Build(pctx, ctx, "framework_aidl", "generate framework.aidl")
+ rule.Build("framework_aidl", "generate framework.aidl")
}
// Creates a version of framework.aidl for the non-updatable part of the platform.
@@ -558,7 +558,7 @@
commitChangeForRestat(rule, tempPath, combinedAidl)
- rule.Build(pctx, ctx, "framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
+ rule.Build("framework_non_updatable_aidl", "generate framework_non_updatable.aidl")
}
func createFrameworkAidl(stubsModules []string, path android.OutputPath, ctx android.SingletonContext) *android.RuleBuilder {
@@ -586,7 +586,7 @@
}
}
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.MissingDeps(missingDeps)
var aidls android.Paths
@@ -597,7 +597,7 @@
rule.Command().
Text("rm -f").Output(aidl)
rule.Command().
- BuiltTool(ctx, "sdkparcelables").
+ BuiltTool("sdkparcelables").
Input(jar).
Output(aidl)
@@ -632,7 +632,7 @@
func createAPIFingerprint(ctx android.SingletonContext) {
out := ApiFingerprintPath(ctx)
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("rm -f").Output(out)
@@ -659,7 +659,7 @@
Output(out)
}
- rule.Build(pctx, ctx, "api_fingerprint", "generate api_fingerprint.txt")
+ rule.Build("api_fingerprint", "generate api_fingerprint.txt")
}
func ApiFingerprintPath(ctx android.PathContext) android.OutputPath {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 32bc077..4e33d74 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2176,12 +2176,12 @@
xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx))
module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > ").
Output(module.outputFilePath)
- rule.Build(pctx, ctx, "java_sdk_xml", "Permission XML")
+ rule.Build("java_sdk_xml", "Permission XML")
module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
}
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
new file mode 100644
index 0000000..2934936
--- /dev/null
+++ b/java/sdk_library_external.go
@@ -0,0 +1,109 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "android/soong/android"
+)
+
+type partitionGroup int
+
+// Representation of partition group for checking inter-partition library dependencies.
+// Between system and system_ext, there are no restrictions of dependencies,
+// so we can treat these partitions as the same in terms of inter-partition dependency.
+// Same policy is applied between vendor and odm partiton.
+const (
+ partitionGroupNone partitionGroup = iota
+ // group for system, and system_ext partition
+ partitionGroupSystem
+ // group for vendor and odm partition
+ partitionGroupVendor
+ // product partition
+ partitionGroupProduct
+)
+
+func (g partitionGroup) String() string {
+ switch g {
+ case partitionGroupSystem:
+ return "system"
+ case partitionGroupVendor:
+ return "vendor"
+ case partitionGroupProduct:
+ return "product"
+ }
+
+ return ""
+}
+
+// Get partition group of java module that can be used at inter-partition dependency check.
+// We currently have three groups
+// (system, system_ext) => system partition group
+// (vendor, odm) => vendor partition group
+// (product) => product partition group
+func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
+ // system and system_ext partition can be treated as the same in terms of inter-partition dependency.
+ if j.Platform() || j.SystemExtSpecific() {
+ return partitionGroupSystem
+ }
+
+ // vendor and odm partition can be treated as the same in terms of inter-partition dependency.
+ if j.SocSpecific() || j.DeviceSpecific() {
+ return partitionGroupVendor
+ }
+
+ // product partition is independent.
+ if j.ProductSpecific() {
+ return partitionGroupProduct
+ }
+
+ panic("Cannot determine partition type")
+}
+
+func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool {
+ return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
+}
+
+type javaSdkLibraryEnforceContext interface {
+ Name() string
+ allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
+ partitionGroup(ctx android.EarlyModuleContext) partitionGroup
+}
+
+var _ javaSdkLibraryEnforceContext = (*Module)(nil)
+
+func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) {
+ if dep.allowListedInterPartitionJavaLibrary(ctx) {
+ return
+ }
+
+ // If product interface is not enforced, skip check between system and product partition.
+ // But still need to check between product and vendor partition because product interface flag
+ // just represents enforcement between product and system, and vendor interface enforcement
+ // that is enforced here by precondition is representing enforcement between vendor and other partitions.
+ if !ctx.Config().EnforceProductPartitionInterface() {
+ productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem
+ systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct
+
+ if productToSystem || systemToProduct {
+ return
+ }
+ }
+
+ // If module and dependency library is inter-partition
+ if j.partitionGroup(ctx) != dep.partitionGroup(ctx) {
+ errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead"
+ ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx))
+ }
+}
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 1c44c74..d538ce4 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -68,13 +68,13 @@
inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src))
l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath
l.installDirPath = android.PathForModuleInstall(ctx, "etc")
- linkerConfigRule := android.NewRuleBuilder()
+ linkerConfigRule := android.NewRuleBuilder(pctx, ctx)
linkerConfigRule.Command().
- BuiltTool(ctx, "conv_linker_config").
+ BuiltTool("conv_linker_config").
Flag("proto").
FlagWithInput("-s ", inputFile).
FlagWithOutput("-o ", l.outputFilePath)
- linkerConfigRule.Build(pctx, ctx, "conv_linker_config",
+ linkerConfigRule.Build("conv_linker_config",
"Generate linker config protobuf "+l.outputFilePath.String())
if proptools.BoolDefault(l.properties.Installable, true) {
@@ -102,6 +102,7 @@
entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
+ entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
},
},
}}
diff --git a/python/androidmk.go b/python/androidmk.go
index e60c538..60637d3 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -49,7 +49,7 @@
entries.Class = "EXECUTABLES"
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", p.binaryProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
})
base.subAndroidMk(entries, p.pythonInstaller)
}
@@ -58,7 +58,7 @@
entries.Class = "NATIVE_TESTS"
entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", p.binaryDecorator.binaryProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
if p.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
}
diff --git a/python/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/rust/androidmk.go b/rust/androidmk.go
index 4e8b14d..c181d67 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -90,7 +90,7 @@
test.binaryDecorator.AndroidMk(ctx, ret)
ret.Class = "NATIVE_TESTS"
ret.ExtraEntries = append(ret.ExtraEntries, func(entries *android.AndroidMkEntries) {
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test.Properties.Test_suites...)
+ entries.AddCompatibilityTestSuites(test.Properties.Test_suites...)
if test.testConfig != nil {
entries.SetString("LOCAL_FULL_TEST_CONFIG", test.testConfig.String())
}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 3ad32fa..e6643f5 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -6,6 +6,7 @@
// for an example.
// TODO(b/160223496): enable rustfmt globally.
RustAllowedPaths = []string{
+ "device/google/cuttlefish",
"external/adhd",
"external/crosvm",
"external/minijail",
diff --git a/rust/library.go b/rust/library.go
index ae33f0f..971588d 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -428,6 +428,7 @@
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]
} else {
srcPath, _ = srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
diff --git a/rust/protobuf.go b/rust/protobuf.go
index 7d6e1fd..0e79089 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -46,8 +46,8 @@
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 realtive paths to proto files that will be used to generate the source
+ Protos []string `android:"path,arch_variant"`
// List of additional flags to pass to aprotoc
Proto_flags []string `android:"arch_variant"`
@@ -66,6 +66,7 @@
func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
var protoFlags android.ProtoFlags
var pluginPaths android.Paths
+ var protoNames []string
protoFlags.OutTypeFlag = "--rust_out"
outDir := android.PathForModuleOut(ctx)
@@ -77,10 +78,7 @@
protoFlags.Deps = append(protoFlags.Deps, pluginPaths...)
- protoFile := android.OptionalPathForModuleSrc(ctx, proto.Properties.Proto)
- if !protoFile.Valid() {
- ctx.PropertyErrorf("proto", "invalid path to proto file")
- }
+ protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
// Add exported dependency include paths
for _, include := range deps.depIncludePaths {
@@ -88,35 +86,58 @@
}
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}
- if proto.plugin == Grpc {
- outputs = append(outputs, android.PathForModuleOut(ctx, stem+grpcSuffix+".rs"))
+
+ // 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")
+
+ // stemFile must be first here as the first path in BaseSourceProvider.OutputFiles is the library entry-point.
+ outputs := android.WritablePaths{stemFile}
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ for _, protoFile := range protoFiles {
+ protoName := strings.TrimSuffix(protoFile.Base(), ".proto")
+ protoNames = append(protoNames, protoName)
+
+ protoOut := android.PathForModuleOut(ctx, protoName+".rs")
+ ruleOutputs := android.WritablePaths{android.WritablePath(protoOut)}
+
+ if proto.plugin == Grpc {
+ grpcOut := android.PathForModuleOut(ctx, protoName+grpcSuffix+".rs")
+ ruleOutputs = append(ruleOutputs, android.WritablePath(grpcOut))
+ }
+
+ depFile := android.PathForModuleOut(ctx, protoName+".d")
+
+ android.ProtoRule(rule, protoFile, protoFlags, protoFlags.Deps, outDir, depFile, ruleOutputs)
+ outputs = append(outputs, ruleOutputs...)
}
- 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 '" + proto.getModFileContents(ctx) + "' >").Output(modFile)
- rule.Build(pctx, ctx, "protoc_"+protoFile.Path().Rel(), "protoc "+protoFile.Path().Rel())
+ rule.Command().
+ Implicits(outputs.Paths()).
+ Text("printf '" + proto.genModFileContents(ctx, protoNames) + "' >").
+ Output(stemFile)
- proto.BaseSourceProvider.OutputFiles = android.Paths{modFile, stemFile}
- return modFile
+ rule.Build("protoc_"+ctx.ModuleName(), "protoc "+ctx.ModuleName())
+
+ proto.BaseSourceProvider.OutputFiles = outputs.Paths()
+
+ // mod_stem.rs is the entry-point for our library modules, so this is what we return.
+ return stemFile
}
-func (proto *protobufDecorator) getModFileContents(ctx ModuleContext) string {
- stem := proto.BaseSourceProvider.getStem(ctx)
+func (proto *protobufDecorator) genModFileContents(ctx ModuleContext, protoNames []string) string {
lines := []string{
- "// @generated",
- fmt.Sprintf("pub mod %s;", stem),
+ "// @Soong generated Source",
+ }
+ for _, protoName := range protoNames {
+ lines = append(lines, fmt.Sprintf("pub mod %s;", protoName))
+
+ if proto.plugin == Grpc {
+ lines = append(lines, fmt.Sprintf("pub mod %s%s;", protoName, grpcSuffix))
+ }
}
if proto.plugin == Grpc {
- lines = append(lines, fmt.Sprintf("pub mod %s%s;", stem, grpcSuffix))
lines = append(
lines,
"pub mod empty {",
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 845911f..608a4e8 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -25,7 +25,7 @@
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"],
@@ -60,13 +60,20 @@
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 TestRustGrpcio(t *testing.T) {
ctx := testRust(t, `
rust_grpcio {
name: "librust_grpcio",
- proto: "buf.proto",
+ protos: ["buf.proto", "proto.proto"],
crate_name: "rust_grpcio",
source_stem: "buf",
shared_libs: ["libfoo_shared"],
@@ -117,4 +124,11 @@
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)
+ }
}
diff --git a/rust/rust.go b/rust/rust.go
index b277afc..38caad3 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -694,6 +694,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}
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 646b252..4edc6cd 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -106,13 +106,14 @@
// 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,
+ "buf.proto": nil,
+ "liby.so": nil,
+ "libz.so": nil,
}
}
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/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/sdk/update.go b/sdk/update.go
index ba63542..377aaae 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -92,7 +92,7 @@
}
func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
- rb := android.NewRuleBuilder()
+ rb := android.NewRuleBuilder(pctx, ctx)
content := gf.content.String()
@@ -108,7 +108,7 @@
Text("| sed 's/\\\\n/\\n/g' >").Output(gf.path)
rb.Command().
Text("chmod a+x").Output(gf.path)
- rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
+ rb.Build(gf.path.Base(), "Build "+gf.path.Base())
}
// Collect all the members.
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 7e5c344..f86e1fd 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -398,7 +398,7 @@
func(entries *android.AndroidMkEntries) {
s.customAndroidMkEntries(entries)
entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
+ entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
if s.testConfig != nil {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 828d1cf..6a53414 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -247,16 +247,16 @@
m.latestApiFile = android.PathForSource(ctx, ctx.ModuleDir(), "api", baseModuleName+"-latest.txt")
// dump API rule
- rule := android.NewRuleBuilder()
+ rule := android.NewRuleBuilder(pctx, ctx)
m.dumpedApiFile = android.PathForModuleOut(ctx, "api-dump.txt")
rule.Command().
- BuiltTool(ctx, "sysprop_api_dump").
+ BuiltTool("sysprop_api_dump").
Output(m.dumpedApiFile).
Inputs(android.PathsForModuleSrc(ctx, m.properties.Srcs))
- rule.Build(pctx, ctx, baseModuleName+"_api_dump", baseModuleName+" api dump")
+ rule.Build(baseModuleName+"_api_dump", baseModuleName+" api dump")
// check API rule
- rule = android.NewRuleBuilder()
+ rule = android.NewRuleBuilder(pctx, ctx)
// 1. compares current.txt to api-dump.txt
// current.txt should be identical to api-dump.txt.
@@ -284,7 +284,7 @@
rule.Command().
Text("( ").
- BuiltTool(ctx, "sysprop_api_checker").
+ BuiltTool("sysprop_api_checker").
Input(m.latestApiFile).
Input(m.currentApiFile).
Text(" || ( echo").Flag("-e").
@@ -297,7 +297,7 @@
Text("touch").
Output(m.checkApiFileTimeStamp)
- rule.Build(pctx, ctx, baseModuleName+"_check_api", baseModuleName+" check api")
+ rule.Build(baseModuleName+"_check_api", baseModuleName+" check api")
}
func (m *syspropLibrary) AndroidMk() android.AndroidMkData {
diff --git a/ui/build/build.go b/ui/build/build.go
index cfd0b83..e8f0fc4 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -28,7 +28,7 @@
func SetupOutDir(ctx Context, config Config) {
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
- if !config.SkipMake() {
+ if !config.SkipKati() {
// Run soong_build with Kati for a hybrid build, e.g. running the
// AndroidMk singleton and postinstall commands. Communicate this to
// soong_build by writing an empty .soong.kati_enabled marker file in the
@@ -44,7 +44,7 @@
ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
- err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
+ err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
if err != nil {
ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
}
@@ -67,8 +67,8 @@
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
- // If we're in SkipMake mode, skip creating this file if it already exists
- if config.SkipMake() {
+ // If we're in SkipKati mode, skip creating this file if it already exists
+ if config.SkipKati() {
if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
return
}
@@ -85,6 +85,7 @@
}
}
+// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
const (
BuildNone = iota
BuildProductConfig = 1 << iota
@@ -97,6 +98,7 @@
BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
)
+// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
func checkProblematicFiles(ctx Context) {
files := []string{"Android.mk", "CleanSpec.mk"}
for _, file := range files {
@@ -108,6 +110,7 @@
}
}
+// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
func checkCaseSensitivity(ctx Context, config Config) {
outDir := config.OutDir()
lowerCase := filepath.Join(outDir, "casecheck.txt")
@@ -115,13 +118,11 @@
lowerData := "a"
upperData := "B"
- err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
- if err != nil {
+ if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw
ctx.Fatalln("Failed to check case sensitivity:", err)
}
- err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
- if err != nil {
+ if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw
ctx.Fatalln("Failed to check case sensitivity:", err)
}
@@ -139,18 +140,15 @@
}
}
-func help(ctx Context, config Config, what int) {
+// help prints a help/usage message, via the build/make/help.sh script.
+func help(ctx Context, config Config) {
cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
cmd.Sandbox = dumpvarsSandbox
cmd.RunAndPrintOrFatal()
}
-// Build the tree. The 'what' argument can be used to chose which components of
-// the build to run.
-func Build(ctx Context, config Config, what int) {
- ctx.Verboseln("Starting build with args:", config.Arguments())
- ctx.Verboseln("Environment:", config.Environment().Environ())
-
+// checkRAM warns if there probably isn't enough RAM to complete a build.
+func checkRAM(ctx Context, config Config) {
if totalRAM := config.TotalRAM(); totalRAM != 0 {
ram := float32(totalRAM) / (1024 * 1024 * 1024)
ctx.Verbosef("Total RAM: %.3vGB", ram)
@@ -166,24 +164,24 @@
ctx.Println("-j value.")
ctx.Println("************************************************************")
} else if ram <= float32(config.Parallel()) {
+ // Want at least 1GB of RAM per job.
ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
ctx.Println("If you run into segfaults or other errors, try a lower -j value")
}
}
+}
+
+// Build the tree. The 'what' argument can be used to chose which components of
+// the build to run, via checking various bitmasks.
+func Build(ctx Context, config Config, what int) {
+ ctx.Verboseln("Starting build with args:", config.Arguments())
+ ctx.Verboseln("Environment:", config.Environment().Environ())
ctx.BeginTrace(metrics.Total, "total")
defer ctx.EndTrace()
- if config.SkipMake() {
- ctx.Verboseln("Skipping Make/Kati as requested")
- what = what & (BuildSoong | BuildNinja)
- }
-
if inList("help", config.Arguments()) {
- help(ctx, config, what)
- return
- } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
- clean(ctx, config)
+ help(ctx, config)
return
}
@@ -191,16 +189,35 @@
buildLock := BecomeSingletonOrFail(ctx, config)
defer buildLock.Unlock()
+ if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
+ clean(ctx, config)
+ return
+ }
+
+ // checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
checkProblematicFiles(ctx)
+ checkRAM(ctx, config)
+
SetupOutDir(ctx, config)
+ // checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
checkCaseSensitivity(ctx, config)
ensureEmptyDirectoriesExist(ctx, config.TempDir())
SetupPath(ctx, config)
+ if config.SkipConfig() {
+ ctx.Verboseln("Skipping Config as requested")
+ what = what &^ BuildProductConfig
+ }
+
+ if config.SkipKati() {
+ ctx.Verboseln("Skipping Kati as requested")
+ what = what &^ BuildKati
+ }
+
if config.StartGoma() {
// Ensure start Goma compiler_proxy
startGoma(ctx, config)
@@ -216,12 +233,16 @@
runMakeProductConfig(ctx, config)
}
+ // Everything below here depends on product config.
+
if inList("installclean", config.Arguments()) ||
inList("install-clean", config.Arguments()) {
installClean(ctx, config)
ctx.Println("Deleted images and staging directories.")
return
- } else if inList("dataclean", config.Arguments()) ||
+ }
+
+ if inList("dataclean", config.Arguments()) ||
inList("data-clean", config.Arguments()) {
dataClean(ctx, config)
ctx.Println("Deleted data files.")
@@ -240,7 +261,7 @@
runKatiBuild(ctx, config)
runKatiPackage(ctx, config)
- ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
+ ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
} else {
// Load last Kati Suffix if it exists
if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
@@ -259,7 +280,7 @@
}
if what&BuildNinja != 0 {
- if !config.SkipMake() {
+ if what&BuildKati != 0 {
installCleanIfNecessary(ctx, config)
}
@@ -267,6 +288,7 @@
runNinja(ctx, config)
}
+ // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
if what&BuildBazel != 0 {
runBazel(ctx, config)
}
@@ -282,14 +304,11 @@
subDir := filepath.Join(subDirs...)
destDir := filepath.Join(config.DistDir(), "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())
}
}
@@ -304,14 +323,11 @@
subDir := filepath.Join(subDirs...)
destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
- err := os.MkdirAll(destDir, 0777)
- if err != nil {
+ if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
-
}
- _, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
- if err != nil {
+ if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
}
}
diff --git a/ui/build/config.go b/ui/build/config.go
index 229bd5c..c9911f3 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
@@ -536,7 +537,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] == '-' {
@@ -697,7 +701,7 @@
}
func (c *configImpl) NinjaArgs() []string {
- if c.skipMake {
+ if c.skipKati {
return c.arguments
}
return c.ninjaArgs
@@ -740,8 +744,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 {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index bb5cbf0..08c2ee1 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")
}
@@ -145,7 +162,10 @@
cmd.RunAndStreamOrFatal()
}
+ // This build generates .bootstrap/build.ninja, which is used in the next step.
ninja("minibootstrap", ".minibootstrap/build.ninja")
+
+ // This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
ninja("bootstrap", ".bootstrap/build.ninja")
soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
@@ -153,7 +173,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")
}