Merge "AIDEGen: Collect dependencies info from APEX module."
diff --git a/Android.bp b/Android.bp
index 8f7f3e2..45e661e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,7 +92,7 @@
}
cc_genrule {
- name: "host_bionic_linker_flags",
+ name: "host_bionic_linker_script",
host_supported: true,
device_supported: false,
target: {
@@ -107,9 +107,9 @@
},
},
tools: ["extract_linker"],
- cmd: "$(location) -f $(out) $(in)",
+ cmd: "$(location) -T $(out) $(in)",
srcs: [":linker"],
- out: ["linker.flags"],
+ out: ["linker.script"],
}
// Instantiate the dex_bootjars singleton module.
diff --git a/OWNERS b/OWNERS
index e851bf7..bbfd011 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,7 +9,6 @@
eakammer@google.com
jingwen@google.com
joeo@google.com
-jungjw@google.com
lberki@google.com
ruperts@google.com
diff --git a/android/arch.go b/android/arch.go
index 1fbba19..340f136 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1572,20 +1572,20 @@
// with Neon will break those users.
func getNdkAbisConfig() []archConfig {
return []archConfig{
- {"arm", "armv7-a", "", []string{"armeabi-v7a"}},
{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
+ {"arm", "armv7-a", "", []string{"armeabi-v7a"}},
{"x86_64", "", "", []string{"x86_64"}},
+ {"x86", "", "", []string{"x86"}},
}
}
// getAmlAbisConfig returns a list of archConfigs for the ABIs supported by mainline modules.
func getAmlAbisConfig() []archConfig {
return []archConfig{
- {"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
- {"x86", "", "", []string{"x86"}},
+ {"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
{"x86_64", "", "", []string{"x86_64"}},
+ {"x86", "", "", []string{"x86"}},
}
}
diff --git a/android/bazel.go b/android/bazel.go
index 8c68055..992d8aa 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -179,7 +179,6 @@
// also depends on //system/libziparchive:libziparchive (http://b/186823656)
// also depends on //system/logging/liblog:liblog (http://b/186822772)
"libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
- "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
"libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
"libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
@@ -219,9 +218,7 @@
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
- mixedBuildsDisabledList = []string{
- // Currently empty, though should remain present to facilitate granular bp2build migration.
- }
+ mixedBuildsDisabledList = []string{}
// Used for quicker lookups
bp2buildModuleDoNotConvert = map[string]bool{}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index f906c8a..c6364af 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -205,7 +205,7 @@
func NewBazelContext(c *config) (BazelContext, error) {
// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
// are production ready.
- if c.Getenv("USE_BAZEL_ANALYSIS") != "1" {
+ if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
return noopBazelContext{}, nil
}
@@ -542,10 +542,13 @@
platform_name = build_options(target)["//command_line_option:platforms"][0].name
if platform_name == "host":
return "HOST"
- elif not platform_name.startswith("android_"):
- fail("expected platform name of the form 'android_<arch>', but was " + str(platforms))
+ elif platform_name.startswith("android_"):
+ return platform_name[len("android_"):]
+ elif platform_name.startswith("linux_"):
+ return platform_name[len("linux_"):]
+ else:
+ fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
return "UNKNOWN"
- return platform_name[len("android_"):]
def format(target):
id_string = str(target.label) + "|" + get_arch(target)
@@ -742,8 +745,17 @@
}
rule := NewRuleBuilder(pctx, ctx)
cmd := rule.Command()
- cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
- ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+ // cd into Bazel's execution root, which is the action cwd.
+ cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && ", ctx.Config().BazelContext.OutputBase()))
+
+ for _, pair := range buildStatement.Env {
+ // Set per-action env variables, if any.
+ cmd.Flag(pair.Key + "=" + pair.Value)
+ }
+
+ // The actual Bazel action.
+ cmd.Text(" " + buildStatement.Command)
for _, outputPath := range buildStatement.OutputPaths {
cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
@@ -756,6 +768,10 @@
cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
}
+ for _, symlinkPath := range buildStatement.SymlinkPaths {
+ cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
+ }
+
// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
// some Bazel builtins (such as files in the bazel_tools directory) have far-future
// timestamps. Without restat, Ninja would emit warnings that the input files of a
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f4b2a7c..f74fed1 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -83,6 +83,36 @@
// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
// module within the given ctx.
func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+ return bazelLabelForModuleDeps(ctx, modules, false)
+}
+
+// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>"
+// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
+// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so
+// they can be handled as whole static libraries.
+func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+ return bazelLabelForModuleDeps(ctx, modules, true)
+}
+
+// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the
+// list), and excludes (modules to exclude from the list). Both of these should contain references
+// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which
+// corresponds to dependencies on the module within the given ctx, and the excluded dependencies.
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+ return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false)
+}
+
+// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
+// the list), and excludes (modules to exclude from the list). Both of these should contain
+// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
+// list which corresponds to dependencies on the module within the given ctx, and the excluded
+// dependencies. Prebuilt dependencies will be appended with _alwayslink so they can be handled as
+// whole static libraries.
+func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+ return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true)
+}
+
+func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
var labels bazel.LabelList
for _, module := range modules {
bpText := module
@@ -90,7 +120,7 @@
module = ":" + module
}
if m, t := SrcIsModuleWithTag(module); m != "" {
- l := getOtherModuleLabel(ctx, m, t)
+ l := getOtherModuleLabel(ctx, m, t, isWholeLibs)
l.OriginalModuleName = bpText
labels.Includes = append(labels.Includes, l)
} else {
@@ -100,6 +130,18 @@
return labels
}
+func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList {
+ moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs)
+ if len(excludes) == 0 {
+ return moduleLabels
+ }
+ excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs)
+ return bazel.LabelList{
+ Includes: moduleLabels.Includes,
+ Excludes: excludeLabels.Includes,
+ }
+}
+
func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
}
@@ -257,7 +299,7 @@
for _, p := range paths {
if m, tag := SrcIsModuleWithTag(p); m != "" {
- l := getOtherModuleLabel(ctx, m, tag)
+ l := getOtherModuleLabel(ctx, m, tag, false)
if !InList(l.Label, expandedExcludes) {
l.OriginalModuleName = fmt.Sprintf(":%s", m)
labels.Includes = append(labels.Includes, l)
@@ -288,7 +330,7 @@
// getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
// module. The label will be relative to the current directory if appropriate. The dependency must
// already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label {
m, _ := ctx.GetDirectDep(dep)
if m == nil {
panic(fmt.Errorf(`Cannot get direct dep %q of %q.
@@ -297,6 +339,11 @@
}
otherLabel := bazelModuleLabel(ctx, m, tag)
label := bazelModuleLabel(ctx, ctx.Module(), "")
+ if isWholeLibs {
+ if m, ok := m.(Module); ok && IsModulePrebuilt(m) {
+ otherLabel += "_alwayslink"
+ }
+ }
if samePackage(label, otherLabel) {
otherLabel = bazelShortLabel(otherLabel)
}
diff --git a/android/config.go b/android/config.go
index da78c7a..ed90c31 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1690,6 +1690,16 @@
return paths
}
+// BuildPathsByModule returns a map from module name to build paths based on the given directory
+// prefix.
+func (l *ConfiguredJarList) BuildPathsByModule(ctx PathContext, dir OutputPath) map[string]WritablePath {
+ paths := map[string]WritablePath{}
+ for _, jar := range l.jars {
+ paths[jar] = dir.Join(ctx, ModuleStem(jar)+".jar")
+ }
+ return paths
+}
+
// UnmarshalJSON converts JSON configuration from raw bytes into a
// ConfiguredJarList structure.
func (l *ConfiguredJarList) UnmarshalJSON(b []byte) error {
diff --git a/android/deapexer.go b/android/deapexer.go
index 63508d7..de3f635 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -15,49 +15,75 @@
package android
import (
- "fmt"
- "strings"
-
"github.com/google/blueprint"
)
// Provides support for interacting with the `deapexer` module to which a `prebuilt_apex` module
// will delegate the work to export files from a prebuilt '.apex` file.
+//
+// The actual processing that is done is quite convoluted but it is all about combining information
+// from multiple different sources in order to allow a prebuilt module to use a file extracted from
+// an apex file. As follows:
+//
+// 1. A prebuilt module, e.g. prebuilt_bootclasspath_fragment or java_import needs to use a file
+// from a prebuilt_apex/apex_set. It knows the path of the file within the apex but does not know
+// where the apex file is or what apex to use.
+//
+// 2. The connection between the prebuilt module and the prebuilt_apex/apex_set is created through
+// use of an exported_... property on the latter. That causes four things to occur:
+// a. A `deapexer` mopdule is created by the prebuilt_apex/apex_set to extract files from the
+// apex file.
+// b. A dependency is added from the prebuilt_apex/apex_set modules onto the prebuilt modules
+// listed in those properties.
+// c. An APEX variant is created for each of those prebuilt modules.
+// d. A dependency is added from the prebuilt modules to the `deapexer` module.
+//
+// 3. The prebuilt_apex/apex_set modules do not know which files are available in the apex file.
+// That information could be specified on the prebuilt_apex/apex_set modules but without
+// automated generation of those modules it would be expensive to maintain. So, instead they
+// obtain that information from the prebuilt modules. They do not know what files are actually in
+// the apex file either but they know what files they need from it. So, the
+// prebuilt_apex/apex_set modules obtain the files that should be in the apex file from those
+// modules and then pass those onto the `deapexer` module.
+//
+// 4. The `deapexer` module's ninja rule extracts all the files from the apex file into an output
+// directory and checks that all the expected files are there. The expected files are declared as
+// the outputs of the ninja rule so they are available to other modules.
+//
+// 5. The prebuilt modules then retrieve the paths to the files that they needed from the `deapexer`
+// module.
+//
+// The files that are passed to `deapexer` and those that are passed back have a unique identifier
+// that links them together. e.g. If the `deapexer` is passed something like this:
+// javalib/core-libart.jar -> javalib/core-libart.jar
+// it will return something like this:
+// javalib/core-libart.jar -> out/soong/.....deapexer.../javalib/core-libart.jar
+//
+// The reason why the `deapexer` module is separate from the prebuilt_apex/apex_set is to avoid
+// cycles. e.g.
+// prebuilt_apex "com.android.art" depends upon java_import "core-libart":
+// This is so it can create an APEX variant of the latter and obtain information about the
+// files that it needs from the apex file.
+// java_import "core-libart" depends upon `deapexer` module:
+// This is so it can retrieve the paths to the files it needs.
// The information exported by the `deapexer` module, access it using `DeapxerInfoProvider`.
type DeapexerInfo struct {
// map from the name of an exported file from a prebuilt_apex to the path to that file. The
- // exported file name is of the form <module>{<tag>} where <tag> is currently only allowed to be
- // ".dexjar".
+ // exported file name is the apex relative path, e.g. javalib/core-libart.jar.
//
// See Prebuilt.ApexInfoMutator for more information.
exports map[string]Path
}
-// The set of supported prebuilt export tags. Used to verify the tag parameter for
-// `PrebuiltExportPath`.
-var supportedPrebuiltExportTags = map[string]struct{}{
- ".dexjar": {},
-}
-
// PrebuiltExportPath provides the path, or nil if not available, of a file exported from the
// prebuilt_apex that created this ApexInfo.
//
-// The exported file is identified by the module name and the tag:
-// * The module name is the name of the module that contributed the file when the .apex file
-// referenced by the prebuilt_apex was built. It must be specified in one of the exported_...
-// properties on the prebuilt_apex module.
-// * The tag identifies the type of file and is dependent on the module type.
+// The exported file is identified by the apex relative path, e.g. "javalib/core-libart.jar".
//
// See apex/deapexer.go for more information.
-func (i DeapexerInfo) PrebuiltExportPath(name, tag string) Path {
-
- if _, ok := supportedPrebuiltExportTags[tag]; !ok {
- panic(fmt.Errorf("unsupported prebuilt export tag %q, expected one of %s",
- tag, strings.Join(SortedStringKeys(supportedPrebuiltExportTags), ", ")))
- }
-
- path := i.exports[name+"{"+tag+"}"]
+func (i DeapexerInfo) PrebuiltExportPath(apexRelativePath string) Path {
+ path := i.exports[apexRelativePath]
return path
}
@@ -79,5 +105,31 @@
blueprint.BaseDependencyTag
}
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t deapexerTagStruct) ExcludeFromApexContents() {}
+
+var _ ExcludeFromApexContentsTag = DeapexerTag
+
// A tag that is used for dependencies on the `deapexer` module.
var DeapexerTag = deapexerTagStruct{}
+
+// RequiredFilesFromPrebuiltApex must be implemented by modules that require files to be exported
+// from a prebuilt_apex/apex_set.
+type RequiredFilesFromPrebuiltApex interface {
+ // RequiredFilesFromPrebuiltApex returns a list of the file paths (relative to the root of the
+ // APEX's contents) that the implementing module requires from within a prebuilt .apex file.
+ //
+ // For each file path this will cause the file to be extracted out of the prebuilt .apex file, and
+ // the path to the extracted file will be stored in the DeapexerInfo using the APEX relative file
+ // path as the key, The path can then be retrieved using the PrebuiltExportPath(key) method.
+ RequiredFilesFromPrebuiltApex(ctx BaseModuleContext) []string
+}
+
+// Marker interface that identifies dependencies on modules that may require files from a prebuilt
+// apex.
+type RequiresFilesFromPrebuiltApexTag interface {
+ blueprint.DependencyTag
+
+ // Method that differentiates this interface from others.
+ RequiresFilesFromPrebuiltApex()
+}
diff --git a/android/image.go b/android/image.go
index 66101be..bc6b8cd 100644
--- a/android/image.go
+++ b/android/image.go
@@ -43,10 +43,9 @@
// its variation.
ExtraImageVariations(ctx BaseModuleContext) []string
- // SetImageVariation will be passed a newly created recovery variant of the module. ModuleBase implements
- // SetImageVariation, most module types will not need to override it, and those that do must call the
- // overridden method. Implementors of SetImageVariation must be careful to modify the module argument
- // and not the receiver.
+ // SetImageVariation is called for each newly created image variant. The receiver is the original
+ // module, "variation" is the name of the newly created variant and "module" is the newly created
+ // variant itself.
SetImageVariation(ctx BaseModuleContext, variation string, module Module)
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 41b399a..19b58a7 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,6 +55,7 @@
AddNeverAllowRules(createCcSdkVariantRules()...)
AddNeverAllowRules(createUncompressDexRules()...)
AddNeverAllowRules(createMakefileGoalRules()...)
+ AddNeverAllowRules(createInitFirstStageRules()...)
}
// Add a NeverAllow rule to the set of rules to apply.
@@ -216,6 +217,15 @@
}
}
+func createInitFirstStageRules() []Rule {
+ return []Rule{
+ NeverAllow().
+ Without("name", "init_first_stage").
+ With("install_in_root", "true").
+ Because("install_in_root is only for init_first_stage."),
+ }
+}
+
func neverallowMutator(ctx BottomUpMutatorContext) {
m, ok := ctx.Module().(Module)
if !ok {
diff --git a/android/paths.go b/android/paths.go
index b192a35..128ec12 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1591,6 +1591,18 @@
// PathForModuleInstall returns a Path representing the install path for the
// module appended with paths...
func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
+ os, arch := osAndArch(ctx)
+ partition := modulePartition(ctx, os)
+ return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
+}
+
+// PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller
+func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath {
+ os, arch := osAndArch(ctx)
+ return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
+}
+
+func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) {
os := ctx.Os()
arch := ctx.Arch().ArchType
forceOS, forceArch := ctx.InstallForceOS()
@@ -1600,14 +1612,14 @@
if forceArch != nil {
arch = *forceArch
}
- partition := modulePartition(ctx, os)
+ return os, arch
+}
- ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
-
+func makePathForInstall(ctx ModuleInstallPathContext, os OsType, arch ArchType, partition string, debug bool, pathComponents ...string) InstallPath {
+ ret := pathForInstall(ctx, os, arch, partition, debug, pathComponents...)
if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
ret = ret.ToMakePath()
}
-
return ret
}
diff --git a/android/testing.go b/android/testing.go
index 191cb8d..b36f62c 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -713,9 +713,11 @@
func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
var searchedRules []string
- for _, p := range b.provider.BuildParamsForTests() {
- searchedRules = append(searchedRules, p.Rule.String())
- if strings.Contains(p.Rule.String(), rule) {
+ buildParams := b.provider.BuildParamsForTests()
+ for _, p := range buildParams {
+ ruleAsString := p.Rule.String()
+ searchedRules = append(searchedRules, ruleAsString)
+ if strings.Contains(ruleAsString, rule) {
return b.newTestingBuildParams(p), searchedRules
}
}
@@ -725,7 +727,7 @@
func (b baseTestingComponent) buildParamsFromRule(rule string) TestingBuildParams {
p, searchRules := b.maybeBuildParamsFromRule(rule)
if p.Rule == nil {
- panic(fmt.Errorf("couldn't find rule %q.\nall rules: %v", rule, searchRules))
+ panic(fmt.Errorf("couldn't find rule %q.\nall rules:\n%s", rule, strings.Join(searchRules, "\n")))
}
return p
}
diff --git a/android/variable.go b/android/variable.go
index 6d235d6..c766120 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -458,12 +458,13 @@
// with the appropriate ProductConfigVariable.
type ProductConfigProperty struct {
ProductConfigVariable string
+ FullConfig string
Property interface{}
}
// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
// all it all product variable-specific versions of a property are easily accessed together
-type ProductConfigProperties map[string][]ProductConfigProperty
+type ProductConfigProperties map[string]map[string]ProductConfigProperty
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
// have been set for the module in the given context.
@@ -512,11 +513,15 @@
// e.g. Asflags, Cflags, Enabled, etc.
propertyName := variableValue.Type().Field(j).Name
- (*productConfigProperties)[propertyName] = append((*productConfigProperties)[propertyName],
- ProductConfigProperty{
- ProductConfigVariable: productVariableName + suffix,
- Property: property.Interface(),
- })
+ if (*productConfigProperties)[propertyName] == nil {
+ (*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
+ }
+ config := productVariableName + suffix
+ (*productConfigProperties)[propertyName][config] = ProductConfigProperty{
+ ProductConfigVariable: productVariableName,
+ FullConfig: config,
+ Property: property.Interface(),
+ }
}
}
}
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 5316d7b..08616a9 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -104,6 +104,7 @@
"LOCAL_NDK_STL_VARIANT": "stl",
"LOCAL_JAR_MANIFEST": "manifest",
"LOCAL_CERTIFICATE": "certificate",
+ "LOCAL_CERTIFICATE_LINEAGE": "lineage",
"LOCAL_PACKAGE_NAME": "name",
"LOCAL_MODULE_RELATIVE_PATH": "relative_install_path",
"LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type",
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 439f45d..067dcba 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1463,6 +1463,22 @@
}
`,
},
+ {
+ desc: "LOCAL_CERTIFICATE_LINEAGE",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE_LINEAGE := lineage
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+android_test {
+ name: "foo",
+ lineage: "lineage",
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/apex/Android.bp b/apex/Android.bp
index 14c8771..6269757 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,6 +31,7 @@
testSrcs: [
"apex_test.go",
"bootclasspath_fragment_test.go",
+ "classpath_element_test.go",
"platform_bootclasspath_test.go",
"systemserver_classpath_fragment_test.go",
"vndk_test.go",
diff --git a/apex/apex.go b/apex/apex.go
index 442769a..7ffa6cc 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -170,9 +170,9 @@
// Default is false.
Ignore_system_library_special_case *bool
- // Whenever apex_payload.img of the APEX should include dm-verity hashtree. Should be only
- // used in tests.
- Test_only_no_hashtree *bool
+ // Whenever apex_payload.img of the APEX should include dm-verity hashtree.
+ // Default value is true.
+ Generate_hashtree *bool
// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
// used in tests.
@@ -1059,8 +1059,9 @@
// apexBundle itself is mutated so that it and its dependencies have the same apex variant.
// TODO(jiyong): document the reason why the VNDK APEX is an exception here.
- if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex {
- apexBundleName := mctx.ModuleName()
+ unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
+ if apexModuleTypeRequiresVariant(mctx.Module()) {
+ apexBundleName := unprefixedModuleName
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// Create an alias from the platform variant. This is done to make
@@ -1078,6 +1079,12 @@
mctx.ModuleErrorf("base property is not set")
return
}
+ // Workaround the issue reported in b/191269918 by using the unprefixed module name of this
+ // module as the default variation to use if dependencies of this module do not have the correct
+ // apex variant name. This name matches the name used to create the variations of modules for
+ // which apexModuleTypeRequiresVariant return true.
+ // TODO(b/191269918): Remove this workaround.
+ mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
mctx.CreateVariations(apexBundleName)
if strings.HasPrefix(apexBundleName, "com.android.art") {
// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1086,6 +1093,22 @@
}
}
+// apexModuleTypeRequiresVariant determines whether the module supplied requires an apex specific
+// variant.
+func apexModuleTypeRequiresVariant(module android.Module) bool {
+ if a, ok := module.(*apexBundle); ok {
+ return !a.vndkApex
+ }
+
+ // Match apex_set and prebuilt_apex. Would also match apexBundle but that is handled specially
+ // above.
+ if _, ok := module.(ApexInfoMutator); ok {
+ return true
+ }
+
+ return false
+}
+
// See android.UpdateDirectlyInAnyApex
// TODO(jiyong): move this to android/apex.go?
func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
@@ -1320,9 +1343,9 @@
return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
}
-// See the test_only_no_hashtree property
-func (a *apexBundle) testOnlyShouldSkipHashtreeGeneration() bool {
- return proptools.Bool(a.properties.Test_only_no_hashtree)
+// See the generate_hashtree property
+func (a *apexBundle) shouldGenerateHashtree() bool {
+ return proptools.BoolDefault(a.properties.Generate_hashtree, true)
}
// See the test_only_unsigned_payload property
@@ -1738,7 +1761,9 @@
ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
return false
}
- filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child))
+ if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+ filesInfo = append(filesInfo, *af)
+ }
return true
}
case javaLibTag:
@@ -2141,17 +2166,23 @@
}
// Add classpaths.proto config.
- filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module))
+ if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
+ filesToAdd = append(filesToAdd, *af)
+ }
return filesToAdd
}
-// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that
-// the module contributes to the apex.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile {
- fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
- classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput
- return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+// apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
+// the module contributes to the apex; or nil if the proto config was not generated.
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ return nil
+ }
+ classpathProtoOutput := info.ClasspathFragmentProtoOutput
+ af := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), info.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+ return &af
}
// apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
@@ -2337,16 +2368,30 @@
})
}
-// Enforce that Java deps of the apex are using stable SDKs to compile
+// checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
if a.Updatable() {
if String(a.properties.Min_sdk_version) == "" {
ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
}
a.checkJavaStableSdkVersion(ctx)
+ a.checkClasspathFragments(ctx)
}
}
+// checkClasspathFragments enforces that all classpath fragments in deps generate classpaths.proto config.
+func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
+ ctx.VisitDirectDeps(func(module android.Module) {
+ if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
+ info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+ if !info.ClasspathFragmentProtoGenerated {
+ ctx.OtherModuleErrorf(module, "is included in updatable apex %v, it must not set generate_classpaths_proto to false", ctx.ModuleName())
+ }
+ }
+ })
+}
+
+// checkJavaStableSdkVersion enforces that all Java deps are using stable SDKs to compile.
func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
// java's checkLinkType guarantees correct usage for transitive deps
@@ -2365,7 +2410,7 @@
})
}
-// Ensures that the all the dependencies are marked as available for this APEX
+// checkApexAvailability ensures that the all the dependencies are marked as available for this APEX.
func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
// Let's be practical. Availability for test, host, and the VNDK apex isn't important
if ctx.Host() || a.testApex || a.vndkApex {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 6a7c35c..484b807 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -4389,7 +4389,7 @@
}
`)
- prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+ prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
expectedInput := "myapex-arm64.apex"
if prebuilt.inputApex.String() != expectedInput {
@@ -4398,7 +4398,7 @@
}
func TestPrebuiltMissingSrc(t *testing.T) {
- testApexError(t, `module "myapex" variant "android_common".*: prebuilt_apex does not support "arm64_armv8-a"`, `
+ testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, `
prebuilt_apex {
name: "myapex",
}
@@ -4414,7 +4414,7 @@
}
`)
- p := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt)
+ p := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
expected := "notmyapex.apex"
if p.installFilename != expected {
@@ -4433,7 +4433,7 @@
}
`)
- p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt)
+ p := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt").Module().(*Prebuilt)
expected := []string{"myapex"}
actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
@@ -4503,7 +4503,7 @@
}
// Make sure that the prebuilt_apex has the correct input APEX.
- prebuiltApex := ctx.ModuleForTests("myapex", "android_common")
+ prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex")
rule = prebuiltApex.Rule("android/soong/android.Cp")
if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) {
t.Errorf("expected: %q, found: %q", expected, actual)
@@ -4769,7 +4769,7 @@
// prebuilt_apex module always depends on the prebuilt, and so it doesn't
// find the dex boot jar in it. We either need to disable the source libfoo
// or make the prebuilt libfoo preferred.
- testDexpreoptWithApexes(t, bp, "failed to find a dex jar path for module 'libfoo'", preparer)
+ testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer)
})
t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -6522,8 +6522,8 @@
android.AssertArrayString(t, "extractor input", []string{"myapex.hwasan.apks"}, extractedApex.Inputs.Strings())
// Ditto for the apex.
- m = ctx.ModuleForTests("myapex", "android_common")
- copiedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
+ m = ctx.ModuleForTests("myapex", "android_common_myapex")
+ copiedApex := m.Output("out/soong/.intermediates/myapex/android_common_myapex/foo_v2.apex")
android.AssertStringEquals(t, "myapex input", extractorOutput, copiedApex.Input.String())
}
@@ -6564,6 +6564,7 @@
"com.android.art.debug",
],
hostdex: true,
+ compile_dex: true,
}
apex {
@@ -6699,6 +6700,47 @@
`)
}
+func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
+ testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ min_sdk_version: "29",
+ updatable: true,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ min_sdk_version: "29",
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+}
+
func TestNoUpdatableJarsInBootImage(t *testing.T) {
// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
@@ -7183,7 +7225,7 @@
t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
}
- m = ctx.ModuleForTests("myapex", "android_common")
+ m = ctx.ModuleForTests("myapex", "android_common_myapex")
a := m.Module().(*ApexSet)
expectedOverrides := []string{"foo"}
actualOverrides := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 7aecff6..66bc9e0 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,6 +16,7 @@
import (
"fmt"
+ "sort"
"strings"
"testing"
@@ -82,6 +83,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
java_library {
@@ -90,6 +92,7 @@
"com.android.art",
],
srcs: ["b.java"],
+ compile_dex: true,
}
bootclasspath_fragment {
@@ -318,6 +321,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
java_import {
@@ -326,6 +330,7 @@
apex_available: [
"com.android.art",
],
+ compile_dex: true,
}
`),
)
@@ -355,6 +360,19 @@
addPrebuilt := func(prefer bool, contents ...string) android.FixturePreparer {
text := fmt.Sprintf(`
+ prebuilt_apex {
+ name: "com.android.art",
+ arch: {
+ arm64: {
+ src: "com.android.art-arm64.apex",
+ },
+ arm: {
+ src: "com.android.art-arm.apex",
+ },
+ },
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
prebuilt_bootclasspath_fragment {
name: "mybootclasspathfragment",
image_name: "art",
@@ -368,7 +386,47 @@
return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
}
- t.Run("boot image files", func(t *testing.T) {
+ t.Run("boot image files from source", func(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ commonPreparer,
+
+ // Configure some libraries in the art bootclasspath_fragment that match the source
+ // bootclasspath_fragment's contents property.
+ java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+ addSource("foo", "bar"),
+ ).RunTest(t)
+
+ ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ "etc/classpaths/bootclasspath.pb",
+ "javalib/arm/boot.art",
+ "javalib/arm/boot.oat",
+ "javalib/arm/boot.vdex",
+ "javalib/arm/boot-bar.art",
+ "javalib/arm/boot-bar.oat",
+ "javalib/arm/boot-bar.vdex",
+ "javalib/arm64/boot.art",
+ "javalib/arm64/boot.oat",
+ "javalib/arm64/boot.vdex",
+ "javalib/arm64/boot-bar.art",
+ "javalib/arm64/boot-bar.oat",
+ "javalib/arm64/boot-bar.vdex",
+ "javalib/bar.jar",
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+ `bar`,
+ `com.android.art.key`,
+ `mybootclasspathfragment`,
+ })
+
+ // Make sure that the source bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+ })
+
+ t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
result := android.GroupFixturePreparers(
commonPreparer,
@@ -403,7 +461,13 @@
`bar`,
`com.android.art.key`,
`mybootclasspathfragment`,
+ `prebuilt_com.android.art`,
})
+
+ // Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
+ // locations for the art image.
+ module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
t.Run("source with inconsistency between config and contents", func(t *testing.T) {
@@ -489,7 +553,7 @@
src: "com.android.art-arm.apex",
},
},
- exported_java_libs: ["foo", "bar"],
+ exported_bootclasspath_fragments: ["mybootclasspathfragment"],
}
java_import {
@@ -519,17 +583,42 @@
}
`)
- java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+ java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
`com.android.art.apex.selector`,
- `prebuilt_bar`,
- `prebuilt_foo`,
+ `prebuilt_mybootclasspathfragment`,
})
- java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common", []string{
+ java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+ `com.android.art.deapexer`,
`dex2oatd`,
`prebuilt_bar`,
`prebuilt_foo`,
})
+
+ module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+ checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+}
+
+// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
+// predefined locations of boot dex jars used as inputs for the ART boot image.
+func checkCopiesToPredefinedLocationForArt(t *testing.T, config android.Config, module android.TestingModule, modules ...string) {
+ t.Helper()
+ bootJarLocations := []string{}
+ for _, output := range module.AllOutputs() {
+ output = android.StringRelativeToTop(config, output)
+ if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+ bootJarLocations = append(bootJarLocations, output)
+ }
+ }
+
+ sort.Strings(bootJarLocations)
+ expected := []string{}
+ for _, m := range modules {
+ expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+ }
+ sort.Strings(expected)
+
+ android.AssertArrayString(t, "copies to predefined locations for art", expected, bootJarLocations)
}
func TestBootclasspathFragmentContentsNoName(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index 021e499..24c049b 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -630,11 +630,7 @@
optFlags = append(optFlags, "--assets_dir "+filepath.Dir(a.mergedNotices.HtmlGzOutput.String()))
}
- if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
- ctx.PropertyErrorf("test_only_no_hashtree", "not available")
- return
- }
- if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) || a.testOnlyShouldSkipHashtreeGeneration()) && !compressionEnabled {
+ if (moduleMinSdkVersion.GreaterThan(android.SdkVersion_Android10) && !a.shouldGenerateHashtree()) && !compressionEnabled {
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
// don't need hashtree for activation. Therefore, by removing hashtree from
// apex bundle (filesystem image in it, to be specific), we can save storage.
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
new file mode 100644
index 0000000..0193127
--- /dev/null
+++ b/apex/classpath_element_test.go
@@ -0,0 +1,317 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+ "reflect"
+ "testing"
+
+ "android/soong/android"
+ "android/soong/java"
+ "github.com/google/blueprint"
+)
+
+// Contains tests for java.CreateClasspathElements logic from java/classpath_element.go that
+// requires apexes.
+
+// testClasspathElementContext is a ClasspathElementContext suitable for use in tests.
+type testClasspathElementContext struct {
+ testContext *android.TestContext
+ module android.Module
+ errs []error
+}
+
+func (t *testClasspathElementContext) OtherModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool {
+ return t.testContext.ModuleHasProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) OtherModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{} {
+ return t.testContext.ModuleProvider(module, provider)
+}
+
+func (t *testClasspathElementContext) ModuleErrorf(fmt string, args ...interface{}) {
+ t.errs = append(t.errs, t.testContext.ModuleErrorf(t.module, fmt, args...))
+}
+
+var _ java.ClasspathElementContext = (*testClasspathElementContext)(nil)
+
+func TestCreateClasspathElements(t *testing.T) {
+ preparer := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithArtApex,
+ prepareForTestWithMyapex,
+ // For otherapex.
+ android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+ }),
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
+ android.FixtureWithRootAndroidBp(`
+ apex {
+ name: "com.android.art",
+ key: "com.android.art.key",
+ bootclasspath_fragments: [
+ "art-bootclasspath-fragment",
+ ],
+ java_libs: [
+ "othersdklibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "com.android.art.key",
+ public_key: "com.android.art.avbpubkey",
+ private_key: "com.android.art.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "art-bootclasspath-fragment",
+ apex_available: [
+ "com.android.art",
+ ],
+ contents: [
+ "baz",
+ "quuz",
+ ],
+ }
+
+ java_library {
+ name: "baz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ java_library {
+ name: "quuz",
+ apex_available: [
+ "com.android.art",
+ ],
+ srcs: ["b.java"],
+ installable: true,
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "mybootclasspath-fragment",
+ ],
+ java_libs: [
+ "othersdklibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "mybootclasspath-fragment",
+ apex_available: [
+ "myapex",
+ ],
+ contents: [
+ "bar",
+ ],
+ }
+
+ java_library {
+ name: "bar",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: ["myapex"],
+ permitted_packages: ["bar"],
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["b.java"],
+ }
+
+ java_sdk_library {
+ name: "othersdklibrary",
+ srcs: ["b.java"],
+ shared_library: false,
+ apex_available: [
+ "com.android.art",
+ "myapex",
+ ],
+ }
+
+ bootclasspath_fragment {
+ name: "non-apex-fragment",
+ contents: ["othersdklibrary"],
+ }
+
+ apex {
+ name: "otherapex",
+ key: "otherapex.key",
+ java_libs: [
+ "otherapexlibrary",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "otherapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "otherapexlibrary",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: ["otherapex"],
+ permitted_packages: ["otherapexlibrary"],
+ }
+
+ platform_bootclasspath {
+ name: "myplatform-bootclasspath",
+
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+ }
+ `),
+ )
+
+ result := preparer.RunTest(t)
+
+ artFragment := result.Module("art-bootclasspath-fragment", "android_common_apex10000")
+ artBaz := result.Module("baz", "android_common_apex10000")
+ artQuuz := result.Module("quuz", "android_common_apex10000")
+
+ myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
+ myBar := result.Module("bar", "android_common_apex10000")
+
+ nonApexFragment := result.Module("non-apex-fragment", "android_common")
+ other := result.Module("othersdklibrary", "android_common_apex10000")
+
+ otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
+
+ platformFoo := result.Module("quuz", "android_common")
+
+ bootclasspath := result.Module("myplatform-bootclasspath", "android_common")
+
+ // Use a custom assertion method instead of AssertDeepEquals as the latter formats the output
+ // using %#v which results in meaningless output as ClasspathElements are pointers.
+ assertElementsEquals := func(t *testing.T, message string, expected, actual java.ClasspathElements) {
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("%s: expected:\n %s\n got:\n %s", message, expected, actual)
+ }
+ }
+
+ expectFragmentElement := func(module android.Module, contents ...android.Module) java.ClasspathElement {
+ return &java.ClasspathFragmentElement{module, contents}
+ }
+ expectLibraryElement := func(module android.Module) java.ClasspathElement {
+ return &java.ClasspathLibraryElement{module}
+ }
+
+ newCtx := func() *testClasspathElementContext {
+ return &testClasspathElementContext{testContext: result.TestContext, module: bootclasspath}
+ }
+
+ // Verify that CreateClasspathElements works when given valid input.
+ t.Run("art:baz, art:quuz, my:bar, foo", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, artQuuz, myBar, platformFoo}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectFragmentElement(myFragment, myBar),
+ expectLibraryElement(platformFoo),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment does not have an associated apex.
+ t.Run("non apex fragment", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment})
+ android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when an apex has multiple fragments.
+ t.Run("multiple fragments for same apex", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{artFragment, artFragment})
+ android.FailIfNoMatchingErrors(t, "apex com.android.art has multiple fragments, art-bootclasspath-fragment{.*} and art-bootclasspath-fragment{.*}", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a library is in multiple fragments.
+ t.Run("library from multiple fragments", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{other}, []android.Module{artFragment, myFragment})
+ android.FailIfNoMatchingErrors(t, "library othersdklibrary{.*} is in two separate fragments, art-bootclasspath-fragment{.*} and mybootclasspath-fragment{.*}", ctx.errs)
+ expectedElements := java.ClasspathElements{}
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+ // are separated by a library from another fragment.
+ t.Run("discontiguous separated by fragment", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, myBar, artQuuz, platformFoo}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectFragmentElement(myFragment, myBar),
+ expectLibraryElement(platformFoo),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by libraries from fragment mybootclasspath-fragment{.*} like bar{.*}", ctx.errs)
+ })
+
+ // Verify that CreateClasspathElements detects when a fragment's contents are not contiguous and
+ // are separated by a standalone library.
+ t.Run("discontiguous separated by library", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, platformFoo, artQuuz, myBar}, []android.Module{artFragment, myFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz, artQuuz),
+ expectLibraryElement(platformFoo),
+ expectFragmentElement(myFragment, myBar),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, "libraries from the same fragment must be contiguous, however baz{.*} and quuz{os:android,arch:common,apex:apex10000} from fragment art-bootclasspath-fragment{.*} are separated by library quuz{.*}", ctx.errs)
+ })
+
+ // Verify that CreateClasspathElements detects when there a library on the classpath that
+ // indicates it is from an apex the supplied fragments list does not contain a fragment for that
+ // apex.
+ t.Run("no fragment for apex", func(t *testing.T) {
+ ctx := newCtx()
+ elements := java.CreateClasspathElements(ctx, []android.Module{artBaz, otherApexLibrary}, []android.Module{artFragment})
+ expectedElements := java.ClasspathElements{
+ expectFragmentElement(artFragment, artBaz),
+ }
+ assertElementsEquals(t, "elements", expectedElements, elements)
+ android.FailIfNoMatchingErrors(t, `library otherapexlibrary{.*} is from apexes \[otherapex\] which have no corresponding fragment in \[art-bootclasspath-fragment{.*}\]`, ctx.errs)
+ })
+}
diff --git a/apex/deapexer.go b/apex/deapexer.go
index c7cdbfa..c70da15 100644
--- a/apex/deapexer.go
+++ b/apex/deapexer.go
@@ -40,16 +40,6 @@
// This is intentionally not registered by name as it is not intended to be used from within an
// `Android.bp` file.
-// DeapexerExportedFile defines the properties needed to expose a file from the deapexer module.
-type DeapexerExportedFile struct {
- // The tag parameter which must be passed to android.OutputFileProducer OutputFiles(tag) method
- // to retrieve the path to the unpacked file.
- Tag string
-
- // The path within the APEX that needs to be exported.
- Path string `android:"path"`
-}
-
// DeapexerProperties specifies the properties supported by the deapexer module.
//
// As these are never intended to be supplied in a .bp file they use a different naming convention
@@ -62,7 +52,9 @@
CommonModules []string
// List of files exported from the .apex file by this module
- ExportedFiles []DeapexerExportedFile
+ //
+ // Each entry is a path from the apex root, e.g. javalib/core-libart.jar.
+ ExportedFiles []string
}
type SelectedApexProperties struct {
@@ -107,27 +99,23 @@
exports := make(map[string]android.Path)
- // Create mappings from name+tag to all the required exported paths.
- for _, e := range p.properties.ExportedFiles {
- tag := e.Tag
- path := e.Path
-
+ // Create mappings from apex relative path to the extracted file's path.
+ exportedPaths := make(android.Paths, 0, len(exports))
+ for _, path := range p.properties.ExportedFiles {
// Populate the exports that this makes available.
- exports[tag] = deapexerOutput.Join(ctx, path)
+ extractedPath := deapexerOutput.Join(ctx, path)
+ exports[path] = extractedPath
+ exportedPaths = append(exportedPaths, extractedPath)
}
// If the prebuilt_apex exports any files then create a build rule that unpacks the apex using
// deapexer and verifies that all the required files were created. Also, make the mapping from
- // name+tag to path available for other modules.
+ // apex relative path to extracted file path available for other modules.
if len(exports) > 0 {
// Make the information available for other modules.
ctx.SetProvider(android.DeapexerProvider, android.NewDeapexerInfo(exports))
// Create a sorted list of the files that this exports.
- exportedPaths := make(android.Paths, 0, len(exports))
- for _, p := range exports {
- exportedPaths = append(exportedPaths, p)
- }
exportedPaths = android.SortedUniquePaths(exportedPaths)
// The apex needs to export some files so create a ninja rule to unpack the apex and check that
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index ce12f46..7297926 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -15,6 +15,8 @@
package apex
import (
+ "fmt"
+ "strings"
"testing"
"android/soong/android"
@@ -31,6 +33,139 @@
PrepareForTestWithApexBuildComponents,
)
+func TestPlatformBootclasspath_Fragments(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithPlatformBootclasspath,
+ prepareForTestWithMyapex,
+ java.PrepareForTestWithJavaSdkLibraryFiles,
+ java.FixtureWithLastReleaseApis("foo"),
+ java.FixtureConfigureBootJars("myapex:bar"),
+ android.FixtureWithRootAndroidBp(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "myapex",
+ module:"bar-fragment",
+ },
+ ],
+ hidden_api: {
+ unsupported: [
+ "unsupported.txt",
+ ],
+ removed: [
+ "removed.txt",
+ ],
+ max_target_r_low_priority: [
+ "max-target-r-low-priority.txt",
+ ],
+ max_target_q: [
+ "max-target-q.txt",
+ ],
+ max_target_p: [
+ "max-target-p.txt",
+ ],
+ max_target_o_low_priority: [
+ "max-target-o-low-priority.txt",
+ ],
+ blocked: [
+ "blocked.txt",
+ ],
+ unsupported_packages: [
+ "unsupported-packages.txt",
+ ],
+ },
+ }
+
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ bootclasspath_fragments: [
+ "bar-fragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ bootclasspath_fragment {
+ name: "bar-fragment",
+ contents: ["bar"],
+ apex_available: ["myapex"],
+ api: {
+ stub_libs: ["foo"],
+ },
+ hidden_api: {
+ unsupported: [
+ "bar-unsupported.txt",
+ ],
+ removed: [
+ "bar-removed.txt",
+ ],
+ max_target_r_low_priority: [
+ "bar-max-target-r-low-priority.txt",
+ ],
+ max_target_q: [
+ "bar-max-target-q.txt",
+ ],
+ max_target_p: [
+ "bar-max-target-p.txt",
+ ],
+ max_target_o_low_priority: [
+ "bar-max-target-o-low-priority.txt",
+ ],
+ blocked: [
+ "bar-blocked.txt",
+ ],
+ unsupported_packages: [
+ "bar-unsupported-packages.txt",
+ ],
+ },
+ }
+
+ java_library {
+ name: "bar",
+ apex_available: ["myapex"],
+ srcs: ["a.java"],
+ system_modules: "none",
+ sdk_version: "none",
+ compile_dex: true,
+ permitted_packages: ["bar"],
+ }
+
+ java_sdk_library {
+ name: "foo",
+ srcs: ["a.java"],
+ public: {
+ enabled: true,
+ },
+ compile_dex: true,
+ }
+ `),
+ ).RunTest(t)
+
+ pbcp := result.Module("platform-bootclasspath", "android_common")
+ info := result.ModuleProvider(pbcp, java.MonolithicHiddenAPIInfoProvider).(java.MonolithicHiddenAPIInfo)
+
+ for _, category := range java.HiddenAPIFlagFileCategories {
+ name := category.PropertyName
+ message := fmt.Sprintf("category %s", name)
+ filename := strings.ReplaceAll(name, "_", "-")
+ expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
+ android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
+ }
+
+ android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
+ android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
+ android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
+ android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
+ android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
+}
+
func TestPlatformBootclasspathDependencies(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithPlatformBootclasspath,
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 81bfc86..ea06d45 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,7 +16,7 @@
import (
"fmt"
- "path/filepath"
+ "io"
"strconv"
"strings"
@@ -47,21 +47,51 @@
}
type prebuiltCommon struct {
+ android.ModuleBase
prebuilt android.Prebuilt
// Properties common to both prebuilt_apex and apex_set.
- prebuiltCommonProperties prebuiltCommonProperties
+ prebuiltCommonProperties *PrebuiltCommonProperties
+
+ installDir android.InstallPath
+ installFilename string
+ outputApex android.WritablePath
+
+ // A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
+ // to create make modules in prebuiltCommon.AndroidMkEntries.
+ apexFilesForAndroidMk []apexFile
+
+ // list of commands to create symlinks for backward compatibility.
+ // these commands will be attached as LOCAL_POST_INSTALL_CMD
+ compatSymlinks []string
+
+ hostRequired []string
+ postInstallCommands []string
}
type sanitizedPrebuilt interface {
hasSanitizedSource(sanitizer string) bool
}
-type prebuiltCommonProperties struct {
+type PrebuiltCommonProperties struct {
SelectedApexProperties
ForceDisable bool `blueprint:"mutated"`
+ // whether the extracted apex file is installable.
+ Installable *bool
+
+ // optional name for the installed apex. If unspecified, name of the
+ // module is used as the file name
+ Filename *string
+
+ // names of modules to be overridden. Listed modules can only be other binaries
+ // (in Make or Soong).
+ // This does not completely prevent installation of the overridden binaries, but if both
+ // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+ // from PRODUCT_PACKAGES.
+ Overrides []string
+
// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
// APEX bundle will create an APEX variant and provide dex implementation jars for use by
// dexpreopt and boot jars package check.
@@ -72,6 +102,14 @@
Exported_bootclasspath_fragments []string
}
+// initPrebuiltCommon initializes the prebuiltCommon structure and performs initialization of the
+// module that is common to Prebuilt and ApexSet.
+func (p *prebuiltCommon) initPrebuiltCommon(module android.Module, properties *PrebuiltCommonProperties) {
+ p.prebuiltCommonProperties = properties
+ android.InitSingleSourcePrebuiltModule(module.(android.PrebuiltInterface), properties, "Selected_apex")
+ android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+}
+
func (p *prebuiltCommon) Prebuilt() *android.Prebuilt {
return &p.prebuilt
}
@@ -105,6 +143,130 @@
return false
}
+func (p *prebuiltCommon) InstallFilename() string {
+ return proptools.StringDefault(p.prebuiltCommonProperties.Filename, p.BaseModuleName()+imageApexSuffix)
+}
+
+func (p *prebuiltCommon) Name() string {
+ return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltCommon) Overrides() []string {
+ return p.prebuiltCommonProperties.Overrides
+}
+
+func (p *prebuiltCommon) installable() bool {
+ return proptools.BoolDefault(p.prebuiltCommonProperties.Installable, true)
+}
+
+// initApexFilesForAndroidMk initializes the prebuiltCommon.apexFilesForAndroidMk field from the
+// modules that this depends upon.
+func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
+ // Walk the dependencies of this module looking for the java modules that it exports.
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ tag := ctx.OtherModuleDependencyTag(child)
+
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
+ if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
+ // If the exported java module provides a dex jar path then add it to the list of apexFiles.
+ path := child.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+ if path != nil {
+ p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, apexFile{
+ module: child,
+ moduleDir: ctx.OtherModuleDir(child),
+ androidMkModuleName: name,
+ builtFile: path,
+ class: javaSharedLib,
+ })
+ }
+ } else if tag == exportedBootclasspathFragmentTag {
+ // Visit the children of the bootclasspath_fragment.
+ return true
+ }
+
+ return false
+ })
+}
+
+func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := []android.AndroidMkEntries{
+ {
+ Class: "ETC",
+ OutputFile: android.OptionalPathForPath(p.outputApex),
+ Include: "$(BUILD_PREBUILT)",
+ Host_required: p.hostRequired,
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+ entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+ entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
+ postInstallCommands := append([]string{}, p.postInstallCommands...)
+ postInstallCommands = append(postInstallCommands, p.compatSymlinks...)
+ if len(postInstallCommands) > 0 {
+ entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
+ }
+ },
+ },
+ },
+ }
+
+ // Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
+ // file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
+ // apex specific variants of the exported java modules available for use from within make.
+ apexName := p.BaseModuleName()
+ for _, fi := range p.apexFilesForAndroidMk {
+ entries := p.createEntriesForApexFile(fi, apexName)
+ entriesList = append(entriesList, entries)
+ }
+
+ return entriesList
+}
+
+// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
+func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
+ moduleName := fi.androidMkModuleName + "." + apexName
+ entries := android.AndroidMkEntries{
+ Class: fi.class.nameInMake(),
+ OverrideName: moduleName,
+ OutputFile: android.OptionalPathForPath(fi.builtFile),
+ Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+
+ // soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
+ // we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
+ // we will have foo.jar.jar
+ entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
+ var classesJar android.Path
+ var headerJar android.Path
+ if javaModule, ok := fi.module.(java.ApexDependency); ok {
+ classesJar = javaModule.ImplementationAndResourcesJars()[0]
+ headerJar = javaModule.HeaderJars()[0]
+ } else {
+ classesJar = fi.builtFile
+ headerJar = fi.builtFile
+ }
+ entries.SetString("LOCAL_SOONG_CLASSES_JAR", classesJar.String())
+ entries.SetString("LOCAL_SOONG_HEADER_JAR", headerJar.String())
+ entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
+ entries.SetString("LOCAL_DEX_PREOPT", "false")
+ },
+ },
+ ExtraFooters: []android.AndroidMkExtraFootersFunc{
+ func(w io.Writer, name, prefix, moduleDir string) {
+ // m <module_name> will build <module_name>.<apex_name> as well.
+ if fi.androidMkModuleName != moduleName {
+ fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
+ fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
+ }
+ },
+ },
+ }
+ return entries
+}
+
// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
// apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
type prebuiltApexModuleCreator interface {
@@ -272,19 +434,11 @@
}
type Prebuilt struct {
- android.ModuleBase
prebuiltCommon
properties PrebuiltProperties
- inputApex android.Path
- installDir android.InstallPath
- installFilename string
- outputApex android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
+ inputApex android.Path
}
type ApexFileProperties struct {
@@ -349,27 +503,13 @@
type PrebuiltProperties struct {
ApexFileProperties
- Installable *bool
- // Optional name for the installed apex. If unspecified, name of the
- // module is used as the file name
- Filename *string
-
- // Names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
+ PrebuiltCommonProperties
}
func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
return false
}
-func (p *Prebuilt) installable() bool {
- return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
-}
-
func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
@@ -379,20 +519,11 @@
}
}
-func (p *Prebuilt) InstallFilename() string {
- return proptools.StringDefault(p.properties.Filename, p.BaseModuleName()+imageApexSuffix)
-}
-
-func (p *Prebuilt) Name() string {
- return p.prebuiltCommon.prebuilt.Name(p.ModuleBase.Name())
-}
-
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
- module.AddProperties(&module.properties, &module.prebuiltCommonProperties)
- android.InitSingleSourcePrebuiltModule(module, &module.prebuiltCommonProperties, "Selected_apex")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ module.AddProperties(&module.properties)
+ module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
return module
}
@@ -415,28 +546,32 @@
// A deapexer module is only needed when the prebuilt apex specifies one or more modules in either
// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that
// the listed modules need access to files from within the prebuilt .apex file.
-func createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string, properties *prebuiltCommonProperties) {
+func createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string, properties *PrebuiltCommonProperties) {
// Only create the deapexer module if it is needed.
if len(properties.Exported_java_libs)+len(properties.Exported_bootclasspath_fragments) == 0 {
return
}
// Compute the deapexer properties from the transitive dependencies of this module.
- javaModules := []string{}
- exportedFiles := map[string]string{}
+ commonModules := []string{}
+ exportedFiles := []string{}
ctx.WalkDeps(func(child, parent android.Module) bool {
tag := ctx.OtherModuleDependencyTag(child)
+ // If the child is not in the same apex as the parent then ignore it and all its children.
+ if !android.IsDepInSameApex(ctx, parent, child) {
+ return false
+ }
+
name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
- if java.IsBootclasspathFragmentContentDepTag(tag) || tag == exportedJavaLibTag {
- javaModules = append(javaModules, name)
+ if _, ok := tag.(android.RequiresFilesFromPrebuiltApexTag); ok {
+ commonModules = append(commonModules, name)
- // Add the dex implementation jar to the set of exported files. The path here must match the
- // path of the file in the APEX created by apexFileForJavaModule(...).
- exportedFiles[name+"{.dexjar}"] = filepath.Join("javalib", name+".jar")
+ requiredFiles := child.(android.RequiredFilesFromPrebuiltApex).RequiredFilesFromPrebuiltApex(ctx)
+ exportedFiles = append(exportedFiles, requiredFiles...)
- } else if tag == exportedBootclasspathFragmentTag {
- // Only visit the children of the bootclasspath_fragment for now.
+ // Visit the dependencies of this module just in case they also require files from the
+ // prebuilt apex.
return true
}
@@ -445,18 +580,13 @@
// Create properties for deapexer module.
deapexerProperties := &DeapexerProperties{
- // Remove any duplicates from the java modules lists as a module may be included via a direct
+ // Remove any duplicates from the common modules lists as a module may be included via a direct
// dependency as well as transitive ones.
- CommonModules: android.SortedUniqueStrings(javaModules),
+ CommonModules: android.SortedUniqueStrings(commonModules),
}
// Populate the exported files property in a fixed order.
- for _, tag := range android.SortedStringKeys(exportedFiles) {
- deapexerProperties.ExportedFiles = append(deapexerProperties.ExportedFiles, DeapexerExportedFile{
- Tag: tag,
- Path: exportedFiles[tag],
- })
- }
+ deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
props := struct {
Name *string
@@ -513,6 +643,10 @@
// incorrectly.
func (t exportedDependencyTag) ExcludeFromVisibilityEnforcement() {}
+func (t exportedDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
+var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
+
var (
exportedJavaLibTag = exportedDependencyTag{name: "exported_java_libs"}
exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
@@ -556,7 +690,7 @@
createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
apexFileSource := ":" + apexSelectorModuleName
- createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, &p.prebuiltCommonProperties)
+ createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, p.prebuiltCommonProperties)
// Add a source reference to retrieve the selected apex from the selector module.
p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -592,6 +726,9 @@
return
}
+ // Save the files that need to be made available to Make.
+ p.initApexFilesForAndroidMk(ctx)
+
if p.installable() {
ctx.InstallFile(p.installDir, p.installFilename, p.inputApex)
}
@@ -599,30 +736,11 @@
// in case that prebuilt_apex replaces source apex (using prefer: prop)
p.compatSymlinks = makeCompatSymlinks(p.BaseModuleName(), ctx)
// or that prebuilt_apex overrides other apexes (using overrides: prop)
- for _, overridden := range p.properties.Overrides {
+ for _, overridden := range p.prebuiltCommonProperties.Overrides {
p.compatSymlinks = append(p.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
}
}
-func (p *Prebuilt) AndroidMkEntries() []android.AndroidMkEntries {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(p.inputApex),
- Include: "$(BUILD_PREBUILT)",
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
- entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
- entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
- entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.properties.Overrides...)
- if len(p.compatSymlinks) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(p.compatSymlinks, " && "))
- }
- },
- },
- }}
-}
-
// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
// module. It extracts the correct apex to use and makes it available for use by apex_set.
type prebuiltApexExtractorModule struct {
@@ -667,21 +785,9 @@
}
type ApexSet struct {
- android.ModuleBase
prebuiltCommon
properties ApexSetProperties
-
- installDir android.InstallPath
- installFilename string
- outputApex android.WritablePath
-
- // list of commands to create symlinks for backward compatibility.
- // these commands will be attached as LOCAL_POST_INSTALL_CMD
- compatSymlinks []string
-
- hostRequired []string
- postInstallCommands []string
}
type ApexExtractorProperties struct {
@@ -731,19 +837,7 @@
type ApexSetProperties struct {
ApexExtractorProperties
- // whether the extracted apex file installable.
- Installable *bool
-
- // optional name for the installed apex. If unspecified, name of the
- // module is used as the file name
- Filename *string
-
- // names of modules to be overridden. Listed modules can only be other binaries
- // (in Make or Soong).
- // This does not completely prevent installation of the overridden binaries, but if both
- // binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
- // from PRODUCT_PACKAGES.
- Overrides []string
+ PrebuiltCommonProperties
}
func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
@@ -757,29 +851,11 @@
return false
}
-func (a *ApexSet) installable() bool {
- return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
-}
-
-func (a *ApexSet) InstallFilename() string {
- return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
-}
-
-func (a *ApexSet) Name() string {
- return a.prebuiltCommon.prebuilt.Name(a.ModuleBase.Name())
-}
-
-func (a *ApexSet) Overrides() []string {
- return a.properties.Overrides
-}
-
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
- module.AddProperties(&module.properties, &module.prebuiltCommonProperties)
-
- android.InitSingleSourcePrebuiltModule(module, &module.prebuiltCommonProperties, "Selected_apex")
- android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ module.AddProperties(&module.properties)
+ module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
return module
}
@@ -817,7 +893,7 @@
createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
apexFileSource := ":" + apexExtractorModuleName
- createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, &a.prebuiltCommonProperties)
+ createDeapexerModuleIfNeeded(ctx, deapexerModuleName(baseModuleName), apexFileSource, a.prebuiltCommonProperties)
// After passing the arch specific src properties to the creating the apex selector module
a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
@@ -852,6 +928,9 @@
return
}
+ // Save the files that need to be made available to Make.
+ a.initApexFilesForAndroidMk(ctx)
+
a.installDir = android.PathForModuleInstall(ctx, "apex")
if a.installable() {
ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
@@ -860,7 +939,7 @@
// in case that apex_set replaces source apex (using prefer: prop)
a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
// or that apex_set overrides other apexes (using overrides: prop)
- for _, overridden := range a.properties.Overrides {
+ for _, overridden := range a.prebuiltCommonProperties.Overrides {
a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
}
@@ -883,25 +962,3 @@
func (*systemExtContext) SystemExtSpecific() bool {
return true
}
-
-func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries {
- return []android.AndroidMkEntries{android.AndroidMkEntries{
- Class: "ETC",
- OutputFile: android.OptionalPathForPath(a.outputApex),
- Include: "$(BUILD_PREBUILT)",
- Host_required: a.hostRequired,
- ExtraEntries: []android.AndroidMkExtraEntriesFunc{
- func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
- entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
- entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
- entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...)
- postInstallCommands := append([]string{}, a.postInstallCommands...)
- postInstallCommands = append(postInstallCommands, a.compatSymlinks...)
- if len(postInstallCommands) > 0 {
- entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(postInstallCommands, " && "))
- }
- },
- },
- }}
-}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 95b6e23..537f51d 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -76,3 +76,54 @@
`mysystemserverclasspathfragment`,
})
}
+
+func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForTestWithSystemserverclasspathFragment,
+ prepareForTestWithMyapex,
+ ).RunTestWithBp(t, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ systemserverclasspath_fragments: [
+ "mysystemserverclasspathfragment",
+ ],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "foo",
+ srcs: ["b.java"],
+ installable: true,
+ apex_available: [
+ "myapex",
+ ],
+ }
+
+ systemserverclasspath_fragment {
+ name: "mysystemserverclasspathfragment",
+ generate_classpaths_proto: false,
+ contents: [
+ "foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ `)
+
+ ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ "javalib/foo.jar",
+ })
+
+ java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+ `myapex.key`,
+ `mysystemserverclasspathfragment`,
+ })
+}
diff --git a/bazel/aquery.go b/bazel/aquery.go
index d1119cb..8741afb 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -73,12 +73,13 @@
// BuildStatement contains information to register a build statement corresponding (one to one)
// with a Bazel action from Bazel's action graph.
type BuildStatement struct {
- Command string
- Depfile *string
- OutputPaths []string
- InputPaths []string
- Env []KeyValuePair
- Mnemonic string
+ Command string
+ Depfile *string
+ OutputPaths []string
+ InputPaths []string
+ SymlinkPaths []string
+ Env []KeyValuePair
+ Mnemonic string
}
// A helper type for aquery processing which facilitates retrieval of path IDs from their
@@ -234,10 +235,21 @@
OutputPaths: outputPaths,
InputPaths: inputPaths,
Env: actionEntry.EnvironmentVariables,
- Mnemonic: actionEntry.Mnemonic}
- if len(actionEntry.Arguments) < 1 {
+ Mnemonic: actionEntry.Mnemonic,
+ }
+
+ if isSymlinkAction(actionEntry) {
+ if len(inputPaths) != 1 || len(outputPaths) != 1 {
+ return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+ }
+ out := outputPaths[0]
+ outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
+ out = proptools.ShellEscapeIncludingSpaces(out)
+ in := proptools.ShellEscapeIncludingSpaces(inputPaths[0])
+ buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -rsf %[3]s %[2]s", outDir, out, in)
+ buildStatement.SymlinkPaths = outputPaths[:]
+ } else if len(actionEntry.Arguments) < 1 {
return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
- continue
}
buildStatements = append(buildStatements, buildStatement)
}
@@ -245,9 +257,13 @@
return buildStatements, nil
}
+func isSymlinkAction(a action) bool {
+ return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
+}
+
func shouldSkipAction(a action) bool {
- // TODO(b/180945121): Handle symlink actions.
- if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SolibSymlink" {
+ // TODO(b/180945121): Handle complex symlink actions.
+ if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
return true
}
// Middleman actions are not handled like other actions; they are handled separately as a
@@ -278,6 +294,9 @@
return "", fmt.Errorf("undefined path fragment id %d", currId)
}
labels = append([]string{currFragment.Label}, labels...)
+ if currId == currFragment.ParentId {
+ return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+ }
currId = currFragment.ParentId
}
return filepath.Join(labels...), nil
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 7b40dcd..43e4155 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -805,17 +805,229 @@
}
}
+func TestSimpleSymlink(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file_subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink_subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p one/symlink_subdir && " +
+ "rm -f one/symlink_subdir/symlink && " +
+ "ln -rsf one/file_subdir/file one/symlink_subdir/symlink",
+ InputPaths: []string{"one/file_subdir/file"},
+ OutputPaths: []string{"one/symlink_subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink_subdir/symlink"},
+ Mnemonic: "Symlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkQuotesPaths(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 3
+ }, {
+ "id": 2,
+ "pathFragmentId": 5
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "SolibSymlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "one"
+ }, {
+ "id": 2,
+ "label": "file subdir",
+ "parentId": 1
+ }, {
+ "id": 3,
+ "label": "file",
+ "parentId": 2
+ }, {
+ "id": 4,
+ "label": "symlink subdir",
+ "parentId": 1
+ }, {
+ "id": 5,
+ "label": "symlink",
+ "parentId": 4
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "mkdir -p 'one/symlink subdir' && " +
+ "rm -f 'one/symlink subdir/symlink' && " +
+ "ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'",
+ InputPaths: []string{"one/file subdir/file"},
+ OutputPaths: []string{"one/symlink subdir/symlink"},
+ SymlinkPaths: []string{"one/symlink subdir/symlink"},
+ Mnemonic: "SolibSymlink",
+ },
+ }
+ assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkMultipleInputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [3],
+ "primaryOutputId": 3
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1,2]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "other_file"
+ }, {
+ "id": 3,
+ "label": "symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
+}
+
+func TestSymlinkMultipleOutputs(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }, {
+ "id": 2,
+ "pathFragmentId": 2
+ }, {
+ "id": 3,
+ "pathFragmentId": 3
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "Symlink",
+ "inputDepSetIds": [1],
+ "outputIds": [2,3],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [1]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "file"
+ }, {
+ "id": 2,
+ "label": "symlink"
+ }, {
+ "id": 3,
+ "label": "other_symlink"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
+}
+
func assertError(t *testing.T, err error, expected string) {
+ t.Helper()
if err == nil {
t.Errorf("expected error '%s', but got no error", expected)
} else if err.Error() != expected {
- t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+ t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
}
}
// Asserts that the given actual build statements match the given expected build statements.
// Build statement equivalence is determined using buildStatementEquals.
func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+ t.Helper()
if len(expected) != len(actual) {
t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
len(expected), len(actual), expected, actual)
@@ -852,6 +1064,12 @@
if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
return false
}
+ if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
+ return false
+ }
+ if first.Depfile != second.Depfile {
+ return false
+ }
return true
}
diff --git a/bazel/configurability.go b/bazel/configurability.go
index df9c9bf..282c606 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -56,7 +56,7 @@
// This is consistently named "conditions_default" to mirror the Soong
// config variable default key in an Android.bp file, although there's no
// integration with Soong config variables (yet).
- ConditionsDefault = "conditions_default"
+ conditionsDefault = "conditions_default"
ConditionsDefaultSelectKey = "//conditions:default"
@@ -76,7 +76,7 @@
archArm64: "//build/bazel/platforms/arch:arm64",
archX86: "//build/bazel/platforms/arch:x86",
archX86_64: "//build/bazel/platforms/arch:x86_64",
- ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
+ conditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
}
// A map of target operating systems to the Bazel label of the
@@ -88,7 +88,7 @@
osLinux: "//build/bazel/platforms/os:linux",
osLinuxBionic: "//build/bazel/platforms/os:linux_bionic",
osWindows: "//build/bazel/platforms/os:windows",
- ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+ conditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
}
platformOsArchMap = map[string]string{
@@ -105,7 +105,7 @@
osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
osArchWindowsX86: "//build/bazel/platforms/os_arch:windows_x86",
osArchWindowsX86_64: "//build/bazel/platforms/os_arch:windows_x86_64",
- ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+ conditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
}
)
@@ -168,7 +168,7 @@
case osArch:
return platformOsArchMap[config]
case productVariables:
- if config == ConditionsDefault {
+ if config == conditionsDefault {
return ConditionsDefaultSelectKey
}
return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 92135c6..52c6c2f 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -20,6 +20,10 @@
// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
// but general cc_library will also have dynamic libraries in output files).
RootStaticArchives []string
+ // Dynamic libraries (.so files) created by the current target. These will
+ // be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
+ // but general cc_library will also have dynamic libraries in output files).
+ RootDynamicLibraries []string
}
type getOutputFilesRequestType struct{}
@@ -86,13 +90,21 @@
if linker_input.owner == target.label:
rootStaticArchives.append(library.static_library.path)
+rootDynamicLibraries = []
+
+if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
+ shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+ for lib in shared_info.linker_input.libraries:
+ rootDynamicLibraries += [lib.dynamic_library.path]
+
returns = [
outputFiles,
staticLibraries,
ccObjectFiles,
includes,
system_includes,
- rootStaticArchives
+ rootStaticArchives,
+ rootDynamicLibraries
]
return "|".join([", ".join(r) for r in returns])`
@@ -106,7 +118,7 @@
var ccObjects []string
splitString := strings.Split(rawString, "|")
- if expectedLen := 6; len(splitString) != expectedLen {
+ if expectedLen := 7; len(splitString) != expectedLen {
return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
}
outputFilesString := splitString[0]
@@ -118,6 +130,7 @@
includes := splitOrEmpty(splitString[3], ", ")
systemIncludes := splitOrEmpty(splitString[4], ", ")
rootStaticArchives := splitOrEmpty(splitString[5], ", ")
+ rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
return CcInfo{
OutputFiles: outputFiles,
CcObjectFiles: ccObjects,
@@ -125,6 +138,7 @@
Includes: includes,
SystemIncludes: systemIncludes,
RootStaticArchives: rootStaticArchives,
+ RootDynamicLibraries: rootDynamicLibraries,
}, nil
}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 602849e..035544e 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -46,7 +46,7 @@
}{
{
description: "no result",
- input: "|||||",
+ input: "||||||",
expectedOutput: CcInfo{
OutputFiles: []string{},
CcObjectFiles: []string{},
@@ -54,11 +54,12 @@
Includes: []string{},
SystemIncludes: []string{},
RootStaticArchives: []string{},
+ RootDynamicLibraries: []string{},
},
},
{
description: "only output",
- input: "test|||||",
+ input: "test||||||",
expectedOutput: CcInfo{
OutputFiles: []string{"test"},
CcObjectFiles: []string{},
@@ -66,11 +67,12 @@
Includes: []string{},
SystemIncludes: []string{},
RootStaticArchives: []string{},
+ RootDynamicLibraries: []string{},
},
},
{
description: "all items set",
- input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1",
+ input: "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
expectedOutput: CcInfo{
OutputFiles: []string{"out1", "out2"},
CcObjectFiles: []string{"object1", "object2"},
@@ -78,19 +80,20 @@
Includes: []string{".", "dir/subdir"},
SystemIncludes: []string{"system/dir", "system/other/dir"},
RootStaticArchives: []string{"rootstaticarchive1"},
+ RootDynamicLibraries: []string{"rootdynamiclibrary1"},
},
},
{
description: "too few result splits",
input: "|",
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, []string{"", ""}),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
},
{
description: "too many result splits",
input: strings.Repeat("|", 8),
expectedOutput: CcInfo{},
- expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 6, make([]string, 9)),
+ expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
},
}
for _, tc := range testCases {
diff --git a/bazel/properties.go b/bazel/properties.go
index c55de95..7093d6c 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -68,6 +68,13 @@
return ll.Includes == nil && ll.Excludes == nil
}
+func (ll *LabelList) deepCopy() LabelList {
+ return LabelList{
+ Includes: ll.Includes[:],
+ Excludes: ll.Excludes[:],
+ }
+}
+
// uniqueParentDirectories returns a list of the unique parent directories for
// all files in ll.Includes.
func (ll *LabelList) uniqueParentDirectories() []string {
@@ -343,6 +350,83 @@
return keys
}
+type configToBools map[string]bool
+
+func (ctb configToBools) setValue(config string, value *bool) {
+ if value == nil {
+ if _, ok := ctb[config]; ok {
+ delete(ctb, config)
+ }
+ return
+ }
+ ctb[config] = *value
+}
+
+type configurableBools map[ConfigurationAxis]configToBools
+
+func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
+ if cb[axis] == nil {
+ cb[axis] = make(configToBools)
+ }
+ cb[axis].setValue(config, value)
+}
+
+// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
+type BoolAttribute struct {
+ Value *bool
+
+ ConfigurableValues configurableBools
+}
+
+// HasConfigurableValues returns whether there are configurable values for this attribute.
+func (ba BoolAttribute) HasConfigurableValues() bool {
+ return len(ba.ConfigurableValues) > 0
+}
+
+// SetSelectValue sets value for the given axis/config.
+func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
+ axis.validateConfig(config)
+ switch axis.configurationType {
+ case noConfig:
+ ba.Value = value
+ case arch, os, osArch, productVariables:
+ if ba.ConfigurableValues == nil {
+ ba.ConfigurableValues = make(configurableBools)
+ }
+ ba.ConfigurableValues.setValueForAxis(axis, config, value)
+ default:
+ panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+ }
+}
+
+// SelectValue gets the value for the given axis/config.
+func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
+ axis.validateConfig(config)
+ switch axis.configurationType {
+ case noConfig:
+ return ba.Value
+ case arch, os, osArch, productVariables:
+ if v, ok := ba.ConfigurableValues[axis][config]; ok {
+ return &v
+ } else {
+ return nil
+ }
+ default:
+ panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+ }
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+ keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
+ for k := range ba.ConfigurableValues {
+ keys = append(keys, k)
+ }
+
+ sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+ return keys
+}
+
// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
type labelListSelectValues map[string]LabelList
@@ -469,6 +553,39 @@
return len(lla.ConfigurableValues) > 0
}
+// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
+// the base value and included in default values as appropriate.
+func (lla *LabelListAttribute) ResolveExcludes() {
+ for axis, configToLabels := range lla.ConfigurableValues {
+ baseLabels := lla.Value.deepCopy()
+ for config, val := range configToLabels {
+ // Exclude config-specific excludes from base value
+ lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
+
+ // add base values to config specific to add labels excluded by others in this axis
+ // then remove all config-specific excludes
+ allLabels := baseLabels.deepCopy()
+ allLabels.Append(val)
+ lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+ }
+
+ // After going through all configs, delete the duplicates in the config
+ // values that are already in the base Value.
+ for config, val := range configToLabels {
+ lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
+ }
+
+ // Now that the Value list is finalized for this axis, compare it with the original
+ // list, and put the difference into the default condition for the axis.
+ lla.ConfigurableValues[axis][conditionsDefault] = SubtractBazelLabelList(baseLabels, lla.Value)
+
+ // if everything ends up without includes, just delete the axis
+ if !lla.ConfigurableValues[axis].HasConfigurableValues() {
+ delete(lla.ConfigurableValues, axis)
+ }
+ }
+}
+
// StringListAttribute corresponds to the string_list Bazel attribute type with
// support for additional metadata, like configurations.
type StringListAttribute struct {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index bc556bf..9464245 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -205,3 +205,91 @@
}
}
}
+
+func makeLabels(labels ...string) []Label {
+ var ret []Label
+ for _, l := range labels {
+ ret = append(ret, Label{Label: l})
+ }
+ return ret
+}
+
+func makeLabelList(includes, excludes []string) LabelList {
+ return LabelList{
+ Includes: makeLabels(includes...),
+ Excludes: makeLabels(excludes...),
+ }
+}
+
+func TestResolveExcludes(t *testing.T) {
+ attr := LabelListAttribute{
+ Value: makeLabelList(
+ []string{
+ "all_include",
+ "arm_exclude",
+ "android_exclude",
+ },
+ []string{"all_exclude"},
+ ),
+ ConfigurableValues: configurableLabelLists{
+ ArchConfigurationAxis: labelListSelectValues{
+ "arm": makeLabelList([]string{}, []string{"arm_exclude"}),
+ "x86": makeLabelList([]string{"x86_include"}, []string{}),
+ },
+ OsConfigurationAxis: labelListSelectValues{
+ "android": makeLabelList([]string{}, []string{"android_exclude"}),
+ "linux": makeLabelList([]string{"linux_include"}, []string{}),
+ },
+ OsArchConfigurationAxis: labelListSelectValues{
+ "linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
+ },
+ ProductVariableConfigurationAxis("a"): labelListSelectValues{
+ "a": makeLabelList([]string{}, []string{"not_in_value"}),
+ },
+ },
+ }
+
+ attr.ResolveExcludes()
+
+ expectedBaseIncludes := []Label{Label{Label: "all_include"}}
+ if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
+ t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
+ }
+ var nilLabels []Label
+ expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
+ ArchConfigurationAxis: map[string][]Label{
+ "arm": nilLabels,
+ "x86": makeLabels("arm_exclude", "x86_include"),
+ "conditions_default": makeLabels("arm_exclude"),
+ },
+ OsConfigurationAxis: map[string][]Label{
+ "android": nilLabels,
+ "linux": makeLabels("android_exclude", "linux_include"),
+ "conditions_default": makeLabels("android_exclude"),
+ },
+ OsArchConfigurationAxis: map[string][]Label{
+ "linux_x86": makeLabels("linux_x86_include"),
+ "conditions_default": nilLabels,
+ },
+ }
+ for _, axis := range attr.SortedConfigurationAxes() {
+ if _, ok := expectedConfiguredIncludes[axis]; !ok {
+ t.Errorf("Found unexpected axis %s", axis)
+ continue
+ }
+ expectedForAxis := expectedConfiguredIncludes[axis]
+ gotForAxis := attr.ConfigurableValues[axis]
+ if len(expectedForAxis) != len(gotForAxis) {
+ t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
+ }
+ for config, value := range gotForAxis {
+ if expected, ok := expectedForAxis[config]; ok {
+ if !reflect.DeepEqual(expected, value.Includes) {
+ t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value.Includes)
+ }
+ } else {
+ t.Errorf("Got unexpected config %q for %s", config, axis)
+ }
+ }
+ }
+}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 6bdfc0e..a1e0424 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -488,6 +488,9 @@
ret = "{\n"
// Sort and print the struct props by the key.
structProps := extractStructProperties(propertyValue, indent)
+ if len(structProps) == 0 {
+ return "", nil
+ }
for _, k := range android.SortedStringKeys(structProps) {
ret += makeIndent(indent + 1)
ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
@@ -567,6 +570,10 @@
} else {
return true
}
+ // Always print bools, if you want a bool attribute to be able to take the default value, use a
+ // bool pointer instead
+ case reflect.Bool:
+ return false
default:
if !value.IsValid() {
return true
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 6d0a9b2..33609af 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -23,12 +23,14 @@
func TestGenerateSoongModuleTargets(t *testing.T) {
testCases := []struct {
+ description string
bp string
expectedBazelTarget string
}{
{
+ description: "only name",
bp: `custom { name: "foo" }
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -36,14 +38,16 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = False,
)`,
},
{
+ description: "handles bool",
bp: `custom {
- name: "foo",
- ramdisk: true,
+ name: "foo",
+ bool_prop: true,
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -51,15 +55,16 @@
soong_module_variant = "",
soong_module_deps = [
],
- ramdisk = True,
+ bool_prop = True,
)`,
},
{
+ description: "string escaping",
bp: `custom {
- name: "foo",
- owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+ name: "foo",
+ owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -67,15 +72,17 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = False,
owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
)`,
},
{
+ description: "single item string list",
bp: `custom {
- name: "foo",
- required: ["bar"],
+ name: "foo",
+ required: ["bar"],
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -83,15 +90,17 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = False,
required = ["bar"],
)`,
},
{
+ description: "list of strings",
bp: `custom {
- name: "foo",
- target_required: ["qux", "bazqux"],
+ name: "foo",
+ target_required: ["qux", "bazqux"],
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -99,6 +108,7 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = False,
target_required = [
"qux",
"bazqux",
@@ -106,18 +116,19 @@
)`,
},
{
+ description: "dist/dists",
bp: `custom {
- name: "foo",
- dist: {
- targets: ["goal_foo"],
- tag: ".foo",
- },
- dists: [{
- targets: ["goal_bar"],
- tag: ".bar",
- }],
+ name: "foo",
+ dist: {
+ targets: ["goal_foo"],
+ tag: ".foo",
+ },
+ dists: [{
+ targets: ["goal_bar"],
+ tag: ".bar",
+ }],
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -125,6 +136,7 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = False,
dist = {
"tag": ".foo",
"targets": ["goal_foo"],
@@ -136,20 +148,21 @@
)`,
},
{
+ description: "put it together",
bp: `custom {
- name: "foo",
- required: ["bar"],
- target_required: ["qux", "bazqux"],
- ramdisk: true,
- owner: "custom_owner",
- dists: [
- {
- tag: ".tag",
- targets: ["my_goal"],
- },
- ],
+ name: "foo",
+ required: ["bar"],
+ target_required: ["qux", "bazqux"],
+ bool_prop: true,
+ owner: "custom_owner",
+ dists: [
+ {
+ tag: ".tag",
+ targets: ["my_goal"],
+ },
+ ],
}
- `,
+ `,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
@@ -157,12 +170,12 @@
soong_module_variant = "",
soong_module_deps = [
],
+ bool_prop = True,
dists = [{
"tag": ".tag",
"targets": ["my_goal"],
}],
owner = "custom_owner",
- ramdisk = True,
required = ["bar"],
target_required = [
"qux",
@@ -174,31 +187,33 @@
dir := "."
for _, testCase := range testCases {
- config := android.TestConfig(buildDir, nil, testCase.bp, nil)
- ctx := android.NewTestContext(config)
+ t.Run(testCase.description, func(t *testing.T) {
+ config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+ ctx := android.NewTestContext(config)
- ctx.RegisterModuleType("custom", customModuleFactory)
- ctx.Register()
+ ctx.RegisterModuleType("custom", customModuleFactory)
+ ctx.Register()
- _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
- android.FailIfErrored(t, errs)
- _, errs = ctx.PrepareBuildActions(config)
- android.FailIfErrored(t, errs)
+ _, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
- codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
- bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
- if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
- t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
- }
+ codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
+ bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+ if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+ t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+ }
- actualBazelTarget := bazelTargets[0]
- if actualBazelTarget.content != testCase.expectedBazelTarget {
- t.Errorf(
- "Expected generated Bazel target to be '%s', got '%s'",
- testCase.expectedBazelTarget,
- actualBazelTarget.content,
- )
- }
+ actualBazelTarget := bazelTargets[0]
+ if actualBazelTarget.content != testCase.expectedBazelTarget {
+ t.Errorf(
+ "Expected generated Bazel target to be '%s', got '%s'",
+ testCase.expectedBazelTarget,
+ actualBazelTarget.content,
+ )
+ }
+ })
}
}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index c8ae031..285677a 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -25,18 +25,18 @@
// See cc/testing.go for more context
soongCcLibraryPreamble = `
cc_defaults {
- name: "linux_bionic_supported",
+ name: "linux_bionic_supported",
}
toolchain_library {
- name: "libclang_rt.builtins-x86_64-android",
- defaults: ["linux_bionic_supported"],
- vendor_available: true,
- vendor_ramdisk_available: true,
- product_available: true,
- recovery_available: true,
- native_bridge_supported: true,
- src: "",
+ name: "libclang_rt.builtins-x86_64-android",
+ defaults: ["linux_bionic_supported"],
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ product_available: true,
+ recovery_available: true,
+ native_bridge_supported: true,
+ src: "",
}`
)
@@ -49,6 +49,7 @@
cc.RegisterCCBuildComponents(ctx)
ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+ ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
}
@@ -384,19 +385,69 @@
"-I$(BINDIR)/foo/bar",
],
dynamic_deps = [":shared_dep_for_both"],
- dynamic_deps_for_shared = [":shared_dep_for_shared"],
- dynamic_deps_for_static = [":shared_dep_for_static"],
implementation_deps = [":static_dep_for_both"],
- shared_copts = ["sharedflag"],
- shared_srcs = ["sharedonly.cpp"],
+ shared = {
+ "copts": ["sharedflag"],
+ "dynamic_deps": [":shared_dep_for_shared"],
+ "srcs": ["sharedonly.cpp"],
+ "static_deps": [":static_dep_for_shared"],
+ "whole_archive_deps": [":whole_static_lib_for_shared"],
+ },
srcs = ["both.cpp"],
- static_copts = ["staticflag"],
- static_deps_for_shared = [":static_dep_for_shared"],
- static_deps_for_static = [":static_dep_for_static"],
- static_srcs = ["staticonly.cpp"],
+ static = {
+ "copts": ["staticflag"],
+ "dynamic_deps": [":shared_dep_for_static"],
+ "srcs": ["staticonly.cpp"],
+ "static_deps": [":static_dep_for_static"],
+ "whole_archive_deps": [":whole_static_lib_for_static"],
+ },
whole_archive_deps = [":whole_static_lib_for_both"],
- whole_archive_deps_for_shared = [":whole_static_lib_for_shared"],
- whole_archive_deps_for_static = [":whole_static_lib_for_static"],
+)`},
+ })
+}
+
+func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "a",
+ whole_static_libs: ["whole_static_lib_for_both"],
+ static: {
+ whole_static_libs: ["whole_static_lib_for_static"],
+ },
+ shared: {
+ whole_static_libs: ["whole_static_lib_for_shared"],
+ },
+ bazel_module: { bp2build_available: true },
+}
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_shared" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_static" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_both" }
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "a",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ shared = {
+ "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
+ },
+ static = {
+ "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"],
+ },
+ whole_archive_deps = [":whole_static_lib_for_both_alwayslink"],
)`},
})
}
@@ -486,52 +537,56 @@
"-Ifoo/bar",
"-I$(BINDIR)/foo/bar",
],
- dynamic_deps_for_shared = select({
- "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
- "//conditions:default": [],
- }),
implementation_deps = [":static_dep_for_both"],
- shared_copts = ["sharedflag"] + select({
- "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
- "//conditions:default": [],
- }),
- shared_srcs = ["sharedonly.cpp"] + select({
- "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": ["android_shared.cpp"],
- "//conditions:default": [],
- }),
+ shared = {
+ "copts": ["sharedflag"] + select({
+ "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
+ "//conditions:default": [],
+ }),
+ "dynamic_deps": select({
+ "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ "srcs": ["sharedonly.cpp"] + select({
+ "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+ "//conditions:default": [],
+ }),
+ "static_deps": [":static_dep_for_shared"] + select({
+ "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+ "//conditions:default": [],
+ }) + select({
+ "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ "whole_archive_deps": select({
+ "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+ "//conditions:default": [],
+ }),
+ },
srcs = ["both.cpp"],
- static_copts = ["staticflag"] + select({
- "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
- "//conditions:default": [],
- }),
- static_deps_for_shared = [":static_dep_for_shared"] + select({
- "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
- "//conditions:default": [],
- }) + select({
- "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
- "//conditions:default": [],
- }),
- static_deps_for_static = [":static_dep_for_static"] + select({
- "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
- "//conditions:default": [],
- }),
- static_srcs = ["staticonly.cpp"] + select({
- "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
- "//conditions:default": [],
- }),
- whole_archive_deps_for_shared = select({
- "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
- "//conditions:default": [],
- }),
+ static = {
+ "copts": ["staticflag"] + select({
+ "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+ "//conditions:default": [],
+ }),
+ "srcs": ["staticonly.cpp"] + select({
+ "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+ "//conditions:default": [],
+ }),
+ "static_deps": [":static_dep_for_static"] + select({
+ "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+ "//conditions:default": [],
+ }),
+ },
)`},
})
}
@@ -564,32 +619,32 @@
cc_library {
name: "a",
srcs: [
- "both_source.cpp",
- "both_source.cc",
- "both_source.c",
- "both_source.s",
- "both_source.S",
+ "both_source.cpp",
+ "both_source.cc",
+ "both_source.c",
+ "both_source.s",
+ "both_source.S",
":both_filegroup",
- ],
+ ],
static: {
- srcs: [
- "static_source.cpp",
- "static_source.cc",
- "static_source.c",
- "static_source.s",
- "static_source.S",
- ":static_filegroup",
- ],
+ srcs: [
+ "static_source.cpp",
+ "static_source.cc",
+ "static_source.c",
+ "static_source.s",
+ "static_source.S",
+ ":static_filegroup",
+ ],
},
shared: {
- srcs: [
- "shared_source.cpp",
- "shared_source.cc",
- "shared_source.c",
- "shared_source.s",
- "shared_source.S",
- ":shared_filegroup",
- ],
+ srcs: [
+ "shared_source.cpp",
+ "shared_source.cc",
+ "shared_source.c",
+ "shared_source.s",
+ "shared_source.S",
+ ":shared_filegroup",
+ ],
},
bazel_module: { bp2build_available: true },
}
@@ -598,21 +653,21 @@
name: "both_filegroup",
srcs: [
// Not relevant, handled by filegroup macro
- ],
+ ],
}
filegroup {
name: "shared_filegroup",
srcs: [
// Not relevant, handled by filegroup macro
- ],
+ ],
}
filegroup {
name: "static_filegroup",
srcs: [
// Not relevant, handled by filegroup macro
- ],
+ ],
}
`,
},
@@ -623,20 +678,22 @@
"-Ifoo/bar",
"-I$(BINDIR)/foo/bar",
],
- shared_srcs = [
- ":shared_filegroup_cpp_srcs",
- "shared_source.cc",
- "shared_source.cpp",
- ],
- shared_srcs_as = [
- "shared_source.s",
- "shared_source.S",
- ":shared_filegroup_as_srcs",
- ],
- shared_srcs_c = [
- "shared_source.c",
- ":shared_filegroup_c_srcs",
- ],
+ shared = {
+ "srcs": [
+ ":shared_filegroup_cpp_srcs",
+ "shared_source.cc",
+ "shared_source.cpp",
+ ],
+ "srcs_as": [
+ "shared_source.s",
+ "shared_source.S",
+ ":shared_filegroup_as_srcs",
+ ],
+ "srcs_c": [
+ "shared_source.c",
+ ":shared_filegroup_c_srcs",
+ ],
+ },
srcs = [
":both_filegroup_cpp_srcs",
"both_source.cc",
@@ -651,20 +708,22 @@
"both_source.c",
":both_filegroup_c_srcs",
],
- static_srcs = [
- ":static_filegroup_cpp_srcs",
- "static_source.cc",
- "static_source.cpp",
- ],
- static_srcs_as = [
- "static_source.s",
- "static_source.S",
- ":static_filegroup_as_srcs",
- ],
- static_srcs_c = [
- "static_source.c",
- ":static_filegroup_c_srcs",
- ],
+ static = {
+ "srcs": [
+ ":static_filegroup_cpp_srcs",
+ "static_source.cc",
+ "static_source.cpp",
+ ],
+ "srcs_as": [
+ "static_source.s",
+ "static_source.S",
+ ":static_filegroup_as_srcs",
+ ],
+ "srcs_c": [
+ "static_source.c",
+ ":static_filegroup_c_srcs",
+ ],
+ },
)`},
})
}
@@ -710,21 +769,21 @@
dir: "foo/bar",
filesystem: map[string]string{
"foo/bar/Android.bp": `
- cc_library {
- name: "a",
- srcs: ["a.cpp"],
- arch: {
- arm: {
- version_script: "arm.map",
- },
- arm64: {
- version_script: "arm64.map",
- },
- },
+ cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ arch: {
+ arm: {
+ version_script: "arm.map",
+ },
+ arm64: {
+ version_script: "arm64.map",
+ },
+ },
- bazel_module: { bp2build_available: true },
- }
- `,
+ bazel_module: { bp2build_available: true },
+ }
+ `,
},
blueprint: soongCcLibraryPreamble,
expectedBazelTargets: []string{`cc_library(
@@ -805,8 +864,8 @@
srcs: ["b.cpp"],
arch: {
x86_64: {
- pack_relocations: false,
- },
+ pack_relocations: false,
+ },
},
bazel_module: { bp2build_available: true },
}
@@ -816,8 +875,8 @@
srcs: ["c.cpp"],
target: {
darwin: {
- pack_relocations: false,
- },
+ pack_relocations: false,
+ },
},
bazel_module: { bp2build_available: true },
}`,
@@ -900,22 +959,22 @@
name: "a",
srcs: ["a.cpp"],
cflags: [
- "-Wall",
- ],
+ "-Wall",
+ ],
cppflags: [
"-fsigned-char",
"-pedantic",
- ],
+ ],
arch: {
arm64: {
cppflags: ["-DARM64=1"],
- },
- },
+ },
+ },
target: {
android: {
cppflags: ["-DANDROID=1"],
- },
- },
+ },
+ },
bazel_module: { bp2build_available: true },
}
`,
@@ -953,21 +1012,21 @@
dir: "foo/bar",
filesystem: map[string]string{
"foo/bar/Android.bp": `
- cc_library {
- name: "a",
- srcs: ["a.cpp"],
- target: {
- android_arm: {
- version_script: "android_arm.map",
- },
- linux_bionic_arm64: {
- version_script: "linux_bionic_arm64.map",
- },
- },
+ cc_library {
+ name: "a",
+ srcs: ["a.cpp"],
+ target: {
+ android_arm: {
+ version_script: "android_arm.map",
+ },
+ linux_bionic_arm64: {
+ version_script: "linux_bionic_arm64.map",
+ },
+ },
- bazel_module: { bp2build_available: true },
- }
- `,
+ bazel_module: { bp2build_available: true },
+ }
+ `,
},
blueprint: soongCcLibraryPreamble,
expectedBazelTargets: []string{`cc_library(
@@ -985,3 +1044,419 @@
)`},
})
}
+
+func TestCcLibraryExcludeLibs(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ filesystem: map[string]string{},
+ blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+ name: "foo_static",
+ srcs: ["common.c"],
+ whole_static_libs: [
+ "arm_whole_static_lib_excludes",
+ "malloc_not_svelte_whole_static_lib_excludes"
+ ],
+ static_libs: [
+ "arm_static_lib_excludes",
+ "malloc_not_svelte_static_lib_excludes"
+ ],
+ shared_libs: [
+ "arm_shared_lib_excludes",
+ ],
+ arch: {
+ arm: {
+ exclude_shared_libs: [
+ "arm_shared_lib_excludes",
+ ],
+ exclude_static_libs: [
+ "arm_static_lib_excludes",
+ "arm_whole_static_lib_excludes",
+ ],
+ },
+ },
+ product_variables: {
+ malloc_not_svelte: {
+ shared_libs: ["malloc_not_svelte_shared_lib"],
+ whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
+ exclude_static_libs: [
+ "malloc_not_svelte_static_lib_excludes",
+ "malloc_not_svelte_whole_static_lib_excludes",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "arm_whole_static_lib_excludes",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "malloc_not_svelte_whole_static_lib",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "malloc_not_svelte_whole_static_lib_excludes",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "arm_static_lib_excludes",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "malloc_not_svelte_static_lib_excludes",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "arm_shared_lib_excludes",
+ bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+ name: "malloc_not_svelte_shared_lib",
+ bazel_module: { bp2build_available: false },
+}
+`,
+ expectedBazelTargets: []string{
+ `cc_library(
+ name = "foo_static",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ dynamic_deps = select({
+ "//build/bazel/platforms/arch:arm": [],
+ "//conditions:default": [":arm_shared_lib_excludes"],
+ }) + select({
+ "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
+ "//conditions:default": [],
+ }),
+ implementation_deps = select({
+ "//build/bazel/platforms/arch:arm": [],
+ "//conditions:default": [":arm_static_lib_excludes"],
+ }) + select({
+ "//build/bazel/product_variables:malloc_not_svelte": [],
+ "//conditions:default": [":malloc_not_svelte_static_lib_excludes"],
+ }),
+ srcs_c = ["common.c"],
+ whole_archive_deps = select({
+ "//build/bazel/platforms/arch:arm": [],
+ "//conditions:default": [":arm_whole_static_lib_excludes"],
+ }) + select({
+ "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"],
+ "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"],
+ }),
+)`,
+ },
+ })
+}
+
+func TestCCLibraryNoCrtTrue(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library - simple example",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ no_libcrt: true,
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "foo-lib",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ srcs = ["impl.cpp"],
+ use_libcrt = False,
+)`}})
+}
+
+func TestCCLibraryNoCrtFalse(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ no_libcrt: false,
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "foo-lib",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ srcs = ["impl.cpp"],
+ use_libcrt = True,
+)`}})
+}
+
+func TestCCLibraryNoCrtArchVariant(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ arch: {
+ arm: {
+ no_libcrt: true,
+ },
+ x86: {
+ no_libcrt: true,
+ },
+ },
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "foo-lib",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ srcs = ["impl.cpp"],
+ use_libcrt = select({
+ "//build/bazel/platforms/arch:arm": False,
+ "//build/bazel/platforms/arch:x86": False,
+ "//conditions:default": None,
+ }),
+)`}})
+}
+
+func TestCCLibraryNoCrtArchVariantWithDefault(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ filesystem: map[string]string{
+ "impl.cpp": "",
+ },
+ blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+ name: "foo-lib",
+ srcs: ["impl.cpp"],
+ no_libcrt: false,
+ arch: {
+ arm: {
+ no_libcrt: true,
+ },
+ x86: {
+ no_libcrt: true,
+ },
+ },
+}
+`,
+ expectedBazelTargets: []string{`cc_library(
+ name = "foo-lib",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ srcs = ["impl.cpp"],
+ use_libcrt = select({
+ "//build/bazel/platforms/arch:arm": False,
+ "//build/bazel/platforms/arch:x86": False,
+ "//conditions:default": True,
+ }),
+)`}})
+}
+
+func TestCcLibraryStrip(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "nothing",
+ bazel_module: { bp2build_available: true },
+}
+cc_library {
+ name: "keep_symbols",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols: true,
+ }
+}
+cc_library {
+ name: "keep_symbols_and_debug_frame",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols_and_debug_frame: true,
+ }
+}
+cc_library {
+ name: "none",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ none: true,
+ }
+}
+cc_library {
+ name: "keep_symbols_list",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ keep_symbols_list: ["symbol"],
+ }
+}
+cc_library {
+ name: "all",
+ bazel_module: { bp2build_available: true },
+ strip: {
+ all: true,
+ }
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "all",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "all": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols_and_debug_frame",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols_and_debug_frame": True,
+ },
+)`, `cc_library(
+ name = "keep_symbols_list",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols_list": ["symbol"],
+ },
+)`, `cc_library(
+ name = "none",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "none": True,
+ },
+)`, `cc_library(
+ name = "nothing",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+)`},
+ })
+}
+
+func TestCcLibraryStripWithArch(t *testing.T) {
+ runCcLibraryTestCase(t, bp2buildTestCase{
+ description: "cc_library strip args",
+ moduleTypeUnderTest: "cc_library",
+ moduleTypeUnderTestFactory: cc.LibraryFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+ depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+ dir: "foo/bar",
+ filesystem: map[string]string{
+ "foo/bar/Android.bp": `
+cc_library {
+ name: "multi-arch",
+ bazel_module: { bp2build_available: true },
+ target: {
+ darwin: {
+ strip: {
+ keep_symbols_list: ["foo", "bar"]
+ }
+ },
+ },
+ arch: {
+ arm: {
+ strip: {
+ keep_symbols_and_debug_frame: true,
+ },
+ },
+ arm64: {
+ strip: {
+ keep_symbols: true,
+ },
+ },
+ }
+}
+`,
+ },
+ blueprint: soongCcLibraryPreamble,
+ expectedBazelTargets: []string{`cc_library(
+ name = "multi-arch",
+ copts = [
+ "-Ifoo/bar",
+ "-I$(BINDIR)/foo/bar",
+ ],
+ strip = {
+ "keep_symbols": select({
+ "//build/bazel/platforms/arch:arm64": True,
+ "//conditions:default": None,
+ }),
+ "keep_symbols_and_debug_frame": select({
+ "//build/bazel/platforms/arch:arm": True,
+ "//conditions:default": None,
+ }),
+ "keep_symbols_list": select({
+ "//build/bazel/platforms/os:darwin": [
+ "foo",
+ "bar",
+ ],
+ "//conditions:default": [],
+ }),
+ },
+)`},
+ })
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 264ba6b..db344de 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -374,3 +374,39 @@
)`},
})
}
+
+func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) {
+ runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+ description: "cc_library_headers test",
+ moduleTypeUnderTest: "cc_library_headers",
+ moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+ moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+ filesystem: map[string]string{
+ "lib-1/lib1a.h": "",
+ "lib-1/lib1b.h": "",
+ "lib-2/lib2a.h": "",
+ "lib-2/lib2b.h": "",
+ "dir-1/dir1a.h": "",
+ "dir-1/dir1b.h": "",
+ "dir-2/dir2a.h": "",
+ "dir-2/dir2b.h": "",
+ "arch_arm64_exported_include_dir/a.h": "",
+ "arch_x86_exported_include_dir/b.h": "",
+ "arch_x86_64_exported_include_dir/c.h": "",
+ },
+ blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+ name: "lib-1",
+ export_include_dirs: ["lib-1"],
+ no_libcrt: true,
+}`,
+ expectedBazelTargets: []string{`cc_library_headers(
+ name = "lib-1",
+ copts = [
+ "-I.",
+ "-I$(BINDIR)/.",
+ ],
+ includes = ["lib-1"],
+)`},
+ })
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 60f6330..c8105eb 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -51,6 +51,28 @@
return value, []selects{ret}
}
+func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
+ value := reflect.ValueOf(boolAttr.Value)
+ if !boolAttr.HasConfigurableValues() {
+ return value, []selects{}
+ }
+
+ ret := selects{}
+ for _, axis := range boolAttr.SortedConfigurationAxes() {
+ configToBools := boolAttr.ConfigurableValues[axis]
+ for config, bools := range configToBools {
+ selectKey := axis.SelectKey(config)
+ ret[selectKey] = reflect.ValueOf(bools)
+ }
+ }
+ // if there is a select, use the base value as the conditions default value
+ if len(ret) > 0 {
+ ret[bazel.ConditionsDefaultSelectKey] = value
+ value = reflect.Zero(value.Type())
+ }
+
+ return value, []selects{ret}
+}
func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
value := reflect.ValueOf(list.Value.Includes)
var ret []selects
@@ -85,22 +107,30 @@
return false, reflect.Zero(reflect.TypeOf([]string{}))
}
+var (
+ emptyBazelList = "[]"
+ bazelNone = "None"
+)
+
// prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
// select statements.
func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
var value reflect.Value
var configurableAttrs []selects
- var defaultSelectValue string
+ var defaultSelectValue *string
switch list := v.(type) {
case bazel.StringListAttribute:
value, configurableAttrs = getStringListValues(list)
- defaultSelectValue = "[]"
+ defaultSelectValue = &emptyBazelList
case bazel.LabelListAttribute:
value, configurableAttrs = getLabelListValues(list)
- defaultSelectValue = "[]"
+ defaultSelectValue = &emptyBazelList
case bazel.LabelAttribute:
value, configurableAttrs = getLabelValue(list)
- defaultSelectValue = "None"
+ defaultSelectValue = &bazelNone
+ case bazel.BoolAttribute:
+ value, configurableAttrs = getBoolValue(list)
+ defaultSelectValue = &bazelNone
default:
return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
}
@@ -116,7 +146,7 @@
ret += s
}
// Convenience function to append selects components to an attribute value.
- appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
+ appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
if err != nil {
return "", err
@@ -141,13 +171,11 @@
// prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
// to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue string, indent int) (string, error) {
+func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int) (string, error) {
if selectMap == nil {
return "", nil
}
- // addConditionsDefault := false
-
var selects string
for _, selectKey := range android.SortedStringKeys(selectMap) {
if selectKey == bazel.ConditionsDefaultSelectKey {
@@ -184,14 +212,14 @@
if err != nil {
return "", err
}
- if s == "" {
- // Print an explicit empty list (the default value) even if the value is
- // empty, to avoid errors about not finding a configuration that matches.
- ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, defaultValue)
- } else {
+ if s != "" {
// Print the custom default value.
ret += s
ret += ",\n"
+ } else if defaultValue != nil {
+ // Print an explicit empty list (the default value) even if the value is
+ // empty, to avoid errors about not finding a configuration that matches.
+ ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
}
ret += makeIndent(indent)
diff --git a/cc/binary.go b/cc/binary.go
index 999b82c..3aa3fdf 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -149,11 +149,11 @@
if ctx.toolchain().Bionic() {
if !Bool(binary.baseLinker.Properties.Nocrt) {
if binary.static() {
- deps.CrtBegin = "crtbegin_static"
+ deps.CrtBegin = []string{"crtbegin_static"}
} else {
- deps.CrtBegin = "crtbegin_dynamic"
+ deps.CrtBegin = []string{"crtbegin_dynamic"}
}
- deps.CrtEnd = "crtend_android"
+ deps.CrtEnd = []string{"crtend_android"}
}
if binary.static() {
@@ -178,7 +178,7 @@
// the kernel before jumping to the embedded linker.
if ctx.Os() == android.LinuxBionic && !binary.static() {
deps.DynamicLinker = "linker"
- deps.LinkerFlagsFile = "host_bionic_linker_flags"
+ deps.CrtBegin = append(deps.CrtBegin, "host_bionic_linker_script")
}
}
@@ -345,12 +345,6 @@
var linkerDeps android.Paths
- // Add flags from linker flags file.
- if deps.LinkerFlagsFile.Valid() {
- flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
- linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
- }
-
if flags.DynamicLinker != "" {
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
} else if ctx.toolchain().Bionic() && !binary.static() {
@@ -401,16 +395,18 @@
}
}
+ var validations android.WritablePaths
+
// Handle host bionic linker symbols.
if ctx.Os() == android.LinuxBionic && !binary.static() {
- injectedOutputFile := outputFile
- outputFile = android.PathForModuleOut(ctx, "prelinker", fileName)
+ verifyFile := android.PathForModuleOut(ctx, "host_bionic_verify.stamp")
if !deps.DynamicLinker.Valid() {
panic("Non-static host bionic modules must have a dynamic linker")
}
- binary.injectHostBionicLinkerSymbols(ctx, outputFile, deps.DynamicLinker.Path(), injectedOutputFile)
+ binary.verifyHostBionicLinker(ctx, outputFile, deps.DynamicLinker.Path(), verifyFile)
+ validations = append(validations, verifyFile)
}
var sharedLibs android.Paths
@@ -430,7 +426,7 @@
// Register link action.
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
- builderFlags, outputFile, nil)
+ builderFlags, outputFile, nil, validations)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -532,19 +528,19 @@
}
func init() {
- pctx.HostBinToolVariable("hostBionicSymbolsInjectCmd", "host_bionic_inject")
+ pctx.HostBinToolVariable("verifyHostBionicCmd", "host_bionic_verify")
}
-var injectHostBionicSymbols = pctx.AndroidStaticRule("injectHostBionicSymbols",
+var verifyHostBionic = pctx.AndroidStaticRule("verifyHostBionic",
blueprint.RuleParams{
- Command: "$hostBionicSymbolsInjectCmd -i $in -l $linker -o $out",
- CommandDeps: []string{"$hostBionicSymbolsInjectCmd"},
+ Command: "$verifyHostBionicCmd -i $in -l $linker && touch $out",
+ CommandDeps: []string{"$verifyHostBionicCmd"},
}, "linker")
-func (binary *binaryDecorator) injectHostBionicLinkerSymbols(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
+func (binary *binaryDecorator) verifyHostBionicLinker(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
ctx.Build(pctx, android.BuildParams{
- Rule: injectHostBionicSymbols,
- Description: "inject host bionic symbols",
+ Rule: verifyHostBionic,
+ Description: "verify host bionic",
Input: in,
Implicit: linker,
Output: out,
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 31e69d8..76c5f3b 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -112,21 +112,44 @@
}
}
+ // product variables only support a limited set of fields, this is the full list of field names
+ // related to cc module dependency management that are supported.
+ productVariableDepFields := [4]string{
+ "Shared_libs",
+ "Static_libs",
+ "Exclude_static_libs",
+ "Whole_static_libs",
+ }
+
+ productVariableProps := android.ProductVariableProperties(ctx)
+ for _, name := range productVariableDepFields {
+ props, exists := productVariableProps[name]
+ if !exists {
+ continue
+ }
+ for _, prop := range props {
+ if p, ok := prop.Property.([]string); !ok {
+ ctx.ModuleErrorf("Could not convert product variable %s property", name)
+ } else {
+ allDeps = append(allDeps, p...)
+ }
+ }
+ }
+
ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
}
// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
// properties which apply to either the shared or static version of a cc_library module.
type staticOrSharedAttributes struct {
- srcs bazel.LabelListAttribute
- srcs_c bazel.LabelListAttribute
- srcs_as bazel.LabelListAttribute
+ Srcs bazel.LabelListAttribute
+ Srcs_c bazel.LabelListAttribute
+ Srcs_as bazel.LabelListAttribute
+ Copts bazel.StringListAttribute
- copts bazel.StringListAttribute
-
- staticDeps bazel.LabelListAttribute
- dynamicDeps bazel.LabelListAttribute
- wholeArchiveDeps bazel.LabelListAttribute
+ Static_deps bazel.LabelListAttribute
+ Dynamic_deps bazel.LabelListAttribute
+ Whole_archive_deps bazel.LabelListAttribute
}
func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
@@ -227,19 +250,19 @@
}
attrs := staticOrSharedAttributes{
- copts: bazel.StringListAttribute{Value: props.Cflags},
- srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
- staticDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
- dynamicDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
- wholeArchiveDeps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)),
+ Copts: bazel.StringListAttribute{Value: props.Cflags},
+ Srcs: bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
+ Static_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
+ Dynamic_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+ Whole_archive_deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs)),
}
setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
- attrs.copts.SetSelectValue(axis, config, props.Cflags)
- attrs.srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
- attrs.staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
- attrs.dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
- attrs.wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
+ attrs.Copts.SetSelectValue(axis, config, props.Cflags)
+ attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+ attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+ attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+ attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
}
if isStatic {
@@ -260,10 +283,10 @@
}
}
- cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.srcs)
- attrs.srcs = cppSrcs
- attrs.srcs_c = cSrcs
- attrs.srcs_as = asSrcs
+ cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
+ attrs.Srcs = cppSrcs
+ attrs.Srcs_c = cSrcs
+ attrs.Srcs_as = asSrcs
return attrs
}
@@ -372,29 +395,15 @@
return copts
}
- // baseSrcs contain the list of src files that are used for every configuration.
- var baseSrcs []string
- // baseExcludeSrcs contain the list of src files that are excluded for every configuration.
- var baseExcludeSrcs []string
- // baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the
- // arch or os specific srcs later.
- var baseSrcsLabelList bazel.LabelList
-
- // Parse srcs from an arch or OS's props value, taking the base srcs and
- // exclude srcs into account.
+ // Parse srcs from an arch or OS's props value.
parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
- // Combine the base srcs and arch-specific srcs
- allSrcs := append(baseSrcs, baseCompilerProps.Srcs...)
// Add srcs-like dependencies such as generated files.
// First create a LabelList containing these dependencies, then merge the values with srcs.
generatedHdrsAndSrcs := baseCompilerProps.Generated_headers
generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...)
-
generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs)
- // Combine the base exclude_srcs and configuration-specific exclude_srcs
- allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...)
- allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs)
+ allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList)
}
@@ -406,10 +415,6 @@
conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
- // Used for arch-specific srcs later.
- baseSrcs = baseCompilerProps.Srcs
- baseSrcsLabelList = parseSrcs(baseCompilerProps)
- baseExcludeSrcs = baseCompilerProps.Exclude_srcs
break
}
}
@@ -433,8 +438,6 @@
if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
srcsList := parseSrcs(baseCompilerProps)
srcs.SetSelectValue(axis, config, srcsList)
- // The base srcs value should not contain any arch-specific excludes.
- srcs.SetValue(bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes}))
}
copts.SetSelectValue(axis, config, parseCopts(baseCompilerProps))
@@ -445,24 +448,7 @@
}
}
- // After going through all archs, delete the duplicate files in the arch
- // values that are already in the base srcs.Value.
- for axis, configToProps := range archVariantCompilerProps {
- for config, props := range configToProps {
- if _, ok := props.(*BaseCompilerProperties); ok {
- // TODO: handle non-arch
- srcs.SetSelectValue(axis, config, bazel.SubtractBazelLabelList(srcs.SelectValue(axis, config), srcs.Value))
- }
- }
- }
-
- // Now that the srcs.Value list is finalized, compare it with the original
- // list, and put the difference into the default condition for the arch
- // select.
- for axis := range archVariantCompilerProps {
- defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
- srcs.SetSelectValue(axis, bazel.ConditionsDefault, defaultsSrcs)
- }
+ srcs.ResolveExcludes()
productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
"Cflags": &copts,
@@ -478,7 +464,7 @@
ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
}
newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
- attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.ProductConfigVariable), prop.ProductConfigVariable, newFlags)
+ attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags)
}
}
}
@@ -498,12 +484,18 @@
// Convenience struct to hold all attributes parsed from linker properties.
type linkerAttributes struct {
- deps bazel.LabelListAttribute
- dynamicDeps bazel.LabelListAttribute
- wholeArchiveDeps bazel.LabelListAttribute
- exportedDeps bazel.LabelListAttribute
- linkopts bazel.StringListAttribute
- versionScript bazel.LabelAttribute
+ deps bazel.LabelListAttribute
+ dynamicDeps bazel.LabelListAttribute
+ wholeArchiveDeps bazel.LabelListAttribute
+ exportedDeps bazel.LabelListAttribute
+ useLibcrt bazel.BoolAttribute
+ linkopts bazel.StringListAttribute
+ versionScript bazel.LabelAttribute
+ stripKeepSymbols bazel.BoolAttribute
+ stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+ stripKeepSymbolsList bazel.StringListAttribute
+ stripAll bazel.BoolAttribute
+ stripNone bazel.BoolAttribute
}
// FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here
@@ -518,36 +510,65 @@
// bp2BuildParseLinkerProps parses the linker properties of a module, including
// configurable attribute values.
func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
- var deps bazel.LabelListAttribute
+ var headerDeps bazel.LabelListAttribute
+ var staticDeps bazel.LabelListAttribute
var exportedDeps bazel.LabelListAttribute
var dynamicDeps bazel.LabelListAttribute
var wholeArchiveDeps bazel.LabelListAttribute
var linkopts bazel.StringListAttribute
var versionScript bazel.LabelAttribute
+ var useLibcrt bazel.BoolAttribute
- getLibs := func(baseLinkerProps *BaseLinkerProperties) []string {
- libs := baseLinkerProps.Header_libs
- libs = append(libs, baseLinkerProps.Static_libs...)
- libs = android.SortedUniqueStrings(libs)
- return libs
+ var stripKeepSymbols bazel.BoolAttribute
+ var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+ var stripKeepSymbolsList bazel.StringListAttribute
+ var stripAll bazel.BoolAttribute
+ var stripNone bazel.BoolAttribute
+
+ if libraryDecorator, ok := module.linker.(*libraryDecorator); ok {
+ stripProperties := libraryDecorator.stripper.StripProperties
+ stripKeepSymbols.Value = stripProperties.Strip.Keep_symbols
+ stripKeepSymbolsList.Value = stripProperties.Strip.Keep_symbols_list
+ stripKeepSymbolsAndDebugFrame.Value = stripProperties.Strip.Keep_symbols_and_debug_frame
+ stripAll.Value = stripProperties.Strip.All
+ stripNone.Value = stripProperties.Strip.None
+ }
+
+ for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) {
+ for config, props := range configToProps {
+ if stripProperties, ok := props.(*StripProperties); ok {
+ stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
+ stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
+ stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
+ stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
+ stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
+ }
+ }
}
for _, linkerProps := range module.linker.linkerProps() {
if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
- libs := getLibs(baseLinkerProps)
- exportedLibs := baseLinkerProps.Export_header_lib_headers
- wholeArchiveLibs := baseLinkerProps.Whole_static_libs
- deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
- exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs))
- linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
- wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+ // Excludes to parallel Soong:
+ // https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
+ staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
+ staticDeps.Value = android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs)
+ wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
+ wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+ sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+ dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+ headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
+ headerDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs))
+ // TODO(b/188796939): also handle export_static_lib_headers, export_shared_lib_headers,
+ // export_generated_headers
+ exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
+ exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs))
+
+ linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
if baseLinkerProps.Version_script != nil {
versionScript.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
}
-
- sharedLibs := baseLinkerProps.Shared_libs
- dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs))
+ useLibcrt.Value = baseLinkerProps.libCrt()
break
}
@@ -556,31 +577,100 @@
for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
for config, props := range configToProps {
if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
- libs := getLibs(baseLinkerProps)
- exportedLibs := baseLinkerProps.Export_header_lib_headers
- wholeArchiveLibs := baseLinkerProps.Whole_static_libs
- deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, libs))
- exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
- linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
- wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+ staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
+ staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
+ wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
+ wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+ sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+ dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+ headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
+ headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs))
+ exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
+ exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+
+ linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
if baseLinkerProps.Version_script != nil {
versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
}
-
- sharedLibs := baseLinkerProps.Shared_libs
- dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, sharedLibs))
+ useLibcrt.SetSelectValue(axis, config, baseLinkerProps.libCrt())
}
}
}
+ type productVarDep struct {
+ // the name of the corresponding excludes field, if one exists
+ excludesField string
+ // reference to the bazel attribute that should be set for the given product variable config
+ attribute *bazel.LabelListAttribute
+
+ depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
+ }
+
+ productVarToDepFields := map[string]productVarDep{
+ // product variables do not support exclude_shared_libs
+ "Shared_libs": productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes},
+ "Static_libs": productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes},
+ "Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes},
+ }
+
+ productVariableProps := android.ProductVariableProperties(ctx)
+ for name, dep := range productVarToDepFields {
+ props, exists := productVariableProps[name]
+ excludeProps, excludesExists := productVariableProps[dep.excludesField]
+ // if neither an include or excludes property exists, then skip it
+ if !exists && !excludesExists {
+ continue
+ }
+ // collect all the configurations that an include or exclude property exists for.
+ // we want to iterate all configurations rather than either the include or exclude because for a
+ // particular configuration we may have only and include or only an exclude to handle
+ configs := make(map[string]bool, len(props)+len(excludeProps))
+ for config := range props {
+ configs[config] = true
+ }
+ for config := range excludeProps {
+ configs[config] = true
+ }
+
+ for config := range configs {
+ prop, includesExists := props[config]
+ excludesProp, excludesExists := excludeProps[config]
+ var includes, excludes []string
+ var ok bool
+ // if there was no includes/excludes property, casting fails and that's expected
+ if includes, ok = prop.Property.([]string); includesExists && !ok {
+ ctx.ModuleErrorf("Could not convert product variable %s property", name)
+ }
+ if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok {
+ ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
+ }
+
+ dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
+ }
+ }
+
+ staticDeps.ResolveExcludes()
+ dynamicDeps.ResolveExcludes()
+ wholeArchiveDeps.ResolveExcludes()
+
+ headerDeps.Append(staticDeps)
+
return linkerAttributes{
- deps: deps,
+ deps: headerDeps,
exportedDeps: exportedDeps,
dynamicDeps: dynamicDeps,
wholeArchiveDeps: wholeArchiveDeps,
linkopts: linkopts,
+ useLibcrt: useLibcrt,
versionScript: versionScript,
+
+ // Strip properties
+ stripKeepSymbols: stripKeepSymbols,
+ stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame,
+ stripKeepSymbolsList: stripKeepSymbolsList,
+ stripAll: stripAll,
+ stripNone: stripNone,
}
}
diff --git a/cc/builder.go b/cc/builder.go
index fae9522..bde8c96 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -730,8 +730,9 @@
// 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,
- objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
- crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
+ objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd android.Paths,
+ groupLate bool, flags builderFlags, outputFile android.WritablePath,
+ implicitOutputs android.WritablePaths, validations android.WritablePaths) {
ldCmd := "${config.ClangBin}/clang++"
@@ -778,18 +779,17 @@
deps = append(deps, staticLibs...)
deps = append(deps, lateStaticLibs...)
deps = append(deps, wholeStaticLibs...)
- if crtBegin.Valid() {
- deps = append(deps, crtBegin.Path(), crtEnd.Path())
- }
+ deps = append(deps, crtBegin...)
+ deps = append(deps, crtEnd...)
rule := ld
args := map[string]string{
"ldCmd": ldCmd,
- "crtBegin": crtBegin.String(),
+ "crtBegin": strings.Join(crtBegin.Strings(), " "),
"libFlags": strings.Join(libFlagsList, " "),
"extraLibFlags": flags.extraLibFlags,
"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
- "crtEnd": crtEnd.String(),
+ "crtEnd": strings.Join(crtEnd.Strings(), " "),
}
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
rule = ldRE
@@ -805,6 +805,7 @@
Inputs: objFiles,
Implicits: deps,
OrderOnly: sharedLibs,
+ Validations: validations.Paths(),
Args: args,
})
}
diff --git a/cc/cc.go b/cc/cc.go
index 7c4ba44..0c9f945 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -126,11 +126,10 @@
ReexportGeneratedHeaders []string
- CrtBegin, CrtEnd string
+ CrtBegin, CrtEnd []string
// Used for host bionic
- LinkerFlagsFile string
- DynamicLinker string
+ DynamicLinker string
// List of libs that need to be excluded for APEX variant
ExcludeLibsForApex []string
@@ -177,10 +176,7 @@
ReexportedDeps android.Paths
// Paths to crt*.o files
- CrtBegin, CrtEnd android.OptionalPath
-
- // Path to the file container flags to use with the linker
- LinkerFlagsFile android.OptionalPath
+ CrtBegin, CrtEnd android.Paths
// Path to the dynamic linker binary
DynamicLinker android.OptionalPath
@@ -290,9 +286,15 @@
// Set by DepsMutator.
AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
+ // The name of the image this module is built for, suffixed with a '.'
ImageVariationPrefix string `blueprint:"mutated"`
- VndkVersion string `blueprint:"mutated"`
- SubName string `blueprint:"mutated"`
+
+ // The VNDK version this module is built against. If empty, the module is not
+ // build against the VNDK.
+ VndkVersion string `blueprint:"mutated"`
+
+ // Suffix for the name of Android.mk entries generated by this module
+ SubName string `blueprint:"mutated"`
// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
// file
@@ -315,12 +317,15 @@
// Make this module available when building for recovery
Recovery_available *bool
- // Set by imageMutator
- CoreVariantNeeded bool `blueprint:"mutated"`
- RamdiskVariantNeeded bool `blueprint:"mutated"`
- VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
- RecoveryVariantNeeded bool `blueprint:"mutated"`
- ExtraVariants []string `blueprint:"mutated"`
+ // Used by imageMutator, set by ImageMutatorBegin()
+ CoreVariantNeeded bool `blueprint:"mutated"`
+ RamdiskVariantNeeded bool `blueprint:"mutated"`
+ VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
+ RecoveryVariantNeeded bool `blueprint:"mutated"`
+
+ // A list of variations for the "image" mutator of the form
+ //<image name> '.' <version char>, for example, 'vendor.S'
+ ExtraVersionedImageVariations []string `blueprint:"mutated"`
// Allows this module to use non-APEX version of libraries. Useful
// for building binaries that are started before APEXes are activated.
@@ -580,6 +585,7 @@
hostToolPath() android.OptionalPath
relativeInstallPath() string
makeUninstallable(mod *Module)
+ installInRoot() bool
}
// bazelHandler is the interface for a helper object related to deferring to Bazel for
@@ -717,7 +723,6 @@
genHeaderDepTag = dependencyTag{name: "gen header"}
genHeaderExportDepTag = dependencyTag{name: "gen header export"}
objDepTag = dependencyTag{name: "obj"}
- linkerFlagsDepTag = dependencyTag{name: "linker flags file"}
dynamicLinkerDepTag = installDependencyTag{name: "dynamic linker"}
reuseObjTag = dependencyTag{name: "reuse objects"}
staticVariantTag = dependencyTag{name: "static variant"}
@@ -1305,6 +1310,10 @@
Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
}
+func (c *Module) InstallInRoot() bool {
+ return c.installer != nil && c.installer.installInRoot()
+}
+
type baseModuleContext struct {
android.BaseModuleContext
moduleContextImpl
@@ -1673,10 +1682,6 @@
c.makeLinkType = GetMakeLinkType(actx, c)
- if c.maybeGenerateBazelActions(actx) {
- return
- }
-
ctx := &moduleContext{
ModuleContext: actx,
moduleContextImpl: moduleContextImpl{
@@ -1685,6 +1690,11 @@
}
ctx.ctx = ctx
+ if c.maybeGenerateBazelActions(actx) {
+ c.maybeInstall(ctx, apexInfo)
+ return
+ }
+
deps := c.depsToPaths(ctx)
if ctx.Failed() {
return
@@ -1772,19 +1782,7 @@
}
c.outputFile = android.OptionalPathForPath(outputFile)
- // If a lib is directly included in any of the APEXes or is not available to the
- // platform (which is often the case when the stub is provided as a prebuilt),
- // unhide the stubs variant having the latest version gets visible to make. In
- // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
- // force anything in the make world to link against the stubs library. (unless it
- // is explicitly referenced via .bootstrap suffix or the module is marked with
- // 'bootstrap: true').
- if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
- !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
- c.IsStubs() && !c.InVendorRamdisk() {
- c.Properties.HideFromMake = false // unhide
- // Note: this is still non-installable
- }
+ c.maybeUnhideFromMake()
// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
// RECOVERY_SNAPSHOT_VERSION is current.
@@ -1795,6 +1793,26 @@
}
}
+ c.maybeInstall(ctx, apexInfo)
+}
+
+func (c *Module) maybeUnhideFromMake() {
+ // If a lib is directly included in any of the APEXes or is not available to the
+ // platform (which is often the case when the stub is provided as a prebuilt),
+ // unhide the stubs variant having the latest version gets visible to make. In
+ // addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
+ // force anything in the make world to link against the stubs library. (unless it
+ // is explicitly referenced via .bootstrap suffix or the module is marked with
+ // 'bootstrap: true').
+ if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
+ !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
+ c.IsStubs() && !c.InVendorRamdisk() {
+ c.Properties.HideFromMake = false // unhide
+ // Note: this is still non-installable
+ }
+}
+
+func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
if !proptools.BoolDefault(c.Properties.Installable, true) {
// If the module has been specifically configure to not be installed then
// hide from make as otherwise it will break when running inside make
@@ -1970,9 +1988,13 @@
if minSdkVersion == "" || minSdkVersion == "apex_inherit" {
minSdkVersion = m.SdkVersion()
}
+ apiLevel, err := android.ApiLevelFromUser(ctx, minSdkVersion)
+ if err != nil {
+ ctx.PropertyErrorf("min_sdk_version", err.Error())
+ }
return []blueprint.Variation{
{Mutator: "sdk", Variation: "sdk"},
- {Mutator: "version", Variation: minSdkVersion},
+ {Mutator: "version", Variation: apiLevel.String()},
}
}
return []blueprint.Variation{
@@ -2176,13 +2198,6 @@
}, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
}
- for _, lib := range deps.LateStaticLibs {
- depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
- actx.AddVariationDependencies([]blueprint.Variation{
- {Mutator: "link", Variation: "static"},
- }, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
- }
-
// shared lib names without the #version suffix
var sharedLibNames []string
@@ -2208,6 +2223,13 @@
AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
}
+ for _, lib := range deps.LateStaticLibs {
+ depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "static"},
+ }, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
+ }
+
for _, lib := range deps.LateSharedLibs {
if inList(lib, sharedLibNames) {
// This is to handle the case that some of the late shared libs (libc, libdl, libm, ...)
@@ -2242,16 +2264,13 @@
crtVariations := GetCrtVariations(ctx, c)
actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
- if deps.CrtBegin != "" {
+ for _, crt := range deps.CrtBegin {
actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
- RewriteSnapshotLib(deps.CrtBegin, GetSnapshot(c, &snapshotInfo, actx).Objects))
+ RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
- if deps.CrtEnd != "" {
+ for _, crt := range deps.CrtEnd {
actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
- RewriteSnapshotLib(deps.CrtEnd, GetSnapshot(c, &snapshotInfo, actx).Objects))
- }
- if deps.LinkerFlagsFile != "" {
- actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
+ RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
}
if deps.DynamicLinker != "" {
actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2551,17 +2570,10 @@
} else {
ctx.ModuleErrorf("module %q is not a genrule", depName)
}
- case linkerFlagsDepTag:
- if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
- files := genRule.GeneratedSourceFiles()
- if len(files) == 1 {
- depPaths.LinkerFlagsFile = android.OptionalPathForPath(files[0])
- } else if len(files) > 1 {
- ctx.ModuleErrorf("module %q can only generate a single file if used for a linker flag file", depName)
- }
- } else {
- ctx.ModuleErrorf("module %q is not a genrule", depName)
- }
+ case CrtBeginDepTag:
+ depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
+ case CrtEndDepTag:
+ depPaths.CrtEnd = append(depPaths.CrtEnd, android.OutputFileForModule(ctx, dep, ""))
}
return
}
@@ -2874,9 +2886,9 @@
case objDepTag:
depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
case CrtBeginDepTag:
- depPaths.CrtBegin = linkFile
+ depPaths.CrtBegin = append(depPaths.CrtBegin, linkFile.Path())
case CrtEndDepTag:
- depPaths.CrtEnd = linkFile
+ depPaths.CrtEnd = append(depPaths.CrtEnd, linkFile.Path())
case dynamicLinkerDepTag:
depPaths.DynamicLinker = linkFile
}
@@ -2918,8 +2930,8 @@
transitiveStaticLibsBuilder.Transitive(staticDep.TransitiveStaticLibrariesForOrdering)
}
for _, sharedDep := range sharedDeps {
- if sharedDep.StaticAnalogue != nil {
- transitiveStaticLibsBuilder.Transitive(sharedDep.StaticAnalogue.TransitiveStaticLibrariesForOrdering)
+ if sharedDep.TransitiveStaticLibrariesForOrdering != nil {
+ transitiveStaticLibsBuilder.Transitive(sharedDep.TransitiveStaticLibrariesForOrdering)
}
}
transitiveStaticLibs := transitiveStaticLibsBuilder.Build()
@@ -3376,7 +3388,7 @@
}
func (c *Module) IsSdkVariant() bool {
- return c.Properties.IsSdkVariant || c.AlwaysSdk()
+ return c.Properties.IsSdkVariant
}
func kytheExtractAllFactory() android.Singleton {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e0fae5a..2d0d78b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -4231,3 +4231,152 @@
)
})
}
+
+func TestIncludeDirectoryOrdering(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "libfoo",
+ srcs: ["foo.c"],
+ local_include_dirs: ["local_include_dirs"],
+ export_include_dirs: ["export_include_dirs"],
+ export_system_include_dirs: ["export_system_include_dirs"],
+ static_libs: ["libstatic1", "libstatic2"],
+ whole_static_libs: ["libwhole1", "libwhole2"],
+ shared_libs: ["libshared1", "libshared2"],
+ header_libs: ["libheader1", "libheader2"],
+ target: {
+ android: {
+ shared_libs: ["libandroid"],
+ local_include_dirs: ["android_local_include_dirs"],
+ export_include_dirs: ["android_export_include_dirs"],
+ },
+ android_arm: {
+ shared_libs: ["libandroid_arm"],
+ local_include_dirs: ["android_arm_local_include_dirs"],
+ export_include_dirs: ["android_arm_export_include_dirs"],
+ },
+ linux: {
+ shared_libs: ["liblinux"],
+ local_include_dirs: ["linux_local_include_dirs"],
+ export_include_dirs: ["linux_export_include_dirs"],
+ },
+ },
+ multilib: {
+ lib32: {
+ shared_libs: ["lib32"],
+ local_include_dirs: ["lib32_local_include_dirs"],
+ export_include_dirs: ["lib32_export_include_dirs"],
+ },
+ },
+ arch: {
+ arm: {
+ shared_libs: ["libarm"],
+ local_include_dirs: ["arm_local_include_dirs"],
+ export_include_dirs: ["arm_export_include_dirs"],
+ },
+ },
+ stl: "libc++",
+ sdk_version: "20",
+ }
+
+ cc_library_headers {
+ name: "libheader1",
+ export_include_dirs: ["libheader1"],
+ sdk_version: "20",
+ stl: "none",
+ }
+
+ cc_library_headers {
+ name: "libheader2",
+ export_include_dirs: ["libheader2"],
+ sdk_version: "20",
+ stl: "none",
+ }
+ `
+
+ libs := []string{
+ "libstatic1",
+ "libstatic2",
+ "libwhole1",
+ "libwhole2",
+ "libshared1",
+ "libshared2",
+ "libandroid",
+ "libandroid_arm",
+ "liblinux",
+ "lib32",
+ "libarm",
+ }
+
+ for _, lib := range libs {
+ bp += fmt.Sprintf(`
+ cc_library {
+ name: "%s",
+ export_include_dirs: ["%s"],
+ sdk_version: "20",
+ stl: "none",
+ }
+ `, lib, lib)
+ }
+
+ ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
+ // Use the arm variant instead of the arm64 variant so that it gets headers from
+ // ndk_libandroid_support to test LateStaticLibs.
+ cflags := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_sdk_static").Output("obj/foo.o").Args["cFlags"]
+
+ var includes []string
+ flags := strings.Split(cflags, " ")
+ for i, flag := range flags {
+ if strings.Contains(flag, "Cflags") {
+ includes = append(includes, flag)
+ } else if strings.HasPrefix(flag, "-I") {
+ includes = append(includes, strings.TrimPrefix(flag, "-I"))
+ } else if flag == "-isystem" {
+ includes = append(includes, flags[i+1])
+ }
+ }
+
+ want := []string{
+ "${config.ArmClangThumbCflags}",
+ "${config.ArmClangCflags}",
+ "${config.CommonClangGlobalCflags}",
+ "${config.DeviceClangGlobalCflags}",
+ "${config.ClangExternalCflags}",
+ "${config.ArmToolchainClangCflags}",
+ "${config.ArmClangArmv7ANeonCflags}",
+ "${config.ArmClangGenericCflags}",
+ "export_include_dirs",
+ "linux_export_include_dirs",
+ "android_export_include_dirs",
+ "arm_export_include_dirs",
+ "lib32_export_include_dirs",
+ "android_arm_export_include_dirs",
+ "android_arm_local_include_dirs",
+ "lib32_local_include_dirs",
+ "arm_local_include_dirs",
+ "android_local_include_dirs",
+ "linux_local_include_dirs",
+ "local_include_dirs",
+ ".",
+ "libheader1",
+ "libheader2",
+ "libwhole1",
+ "libwhole2",
+ "libstatic1",
+ "libstatic2",
+ "libshared1",
+ "libshared2",
+ "liblinux",
+ "libandroid",
+ "libarm",
+ "lib32",
+ "libandroid_arm",
+ "defaults/cc/common/ndk_libc++_shared",
+ "defaults/cc/common/ndk_libandroid_support",
+ "out/soong/ndk/sysroot/usr/include",
+ "out/soong/ndk/sysroot/usr/include/arm-linux-androideabi",
+ "${config.NoOverrideClangGlobalCflags}",
+ }
+
+ android.AssertArrayString(t, "includes", want, includes)
+}
diff --git a/cc/image.go b/cc/image.go
index c9c0e63..15ec1c8 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -341,11 +341,11 @@
}
func (m *Module) ExtraVariants() []string {
- return m.Properties.ExtraVariants
+ return m.Properties.ExtraVersionedImageVariations
}
func (m *Module) AppendExtraVariant(extraVariant string) {
- m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+ m.Properties.ExtraVersionedImageVariations = append(m.Properties.ExtraVersionedImageVariations, extraVariant)
}
func (m *Module) SetRamdiskVariantNeeded(b bool) {
@@ -629,7 +629,7 @@
}
func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
- return c.Properties.ExtraVariants
+ return c.Properties.ExtraVersionedImageVariations
}
func squashVendorSrcs(m *Module) {
diff --git a/cc/installer.go b/cc/installer.go
index e551c63..f95b493 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -25,6 +25,10 @@
type InstallerProperties struct {
// install to a subdirectory of the default install path for the module
Relative_install_path *string `android:"arch_variant"`
+
+ // Install output directly in {partition}/, not in any subdir. This is only intended for use by
+ // init_first_stage.
+ Install_in_root *bool `android:"arch_variant"`
}
type installLocation int
@@ -66,6 +70,11 @@
if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
dir = installer.dir64
}
+
+ if installer.installInRoot() {
+ dir = ""
+ }
+
if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
@@ -110,3 +119,7 @@
func (installer *baseInstaller) makeUninstallable(mod *Module) {
mod.ModuleBase.MakeUninstallable()
}
+
+func (installer *baseInstaller) installInRoot() bool {
+ return Bool(installer.Properties.Install_in_root)
+}
diff --git a/cc/library.go b/cc/library.go
index c88c29a..5b6c623 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -28,6 +28,7 @@
"android/soong/android"
"android/soong/bazel"
+ "android/soong/bazel/cquery"
"android/soong/cc/config"
)
@@ -234,30 +235,24 @@
Whole_archive_deps bazel.LabelListAttribute
Includes bazel.StringListAttribute
Linkopts bazel.StringListAttribute
+ Use_libcrt bazel.BoolAttribute
- // Attributes pertaining to shared variant.
- Shared_srcs bazel.LabelListAttribute
- Shared_srcs_c bazel.LabelListAttribute
- Shared_srcs_as bazel.LabelListAttribute
- Shared_copts bazel.StringListAttribute
+ // This is shared only.
+ Version_script bazel.LabelAttribute
- Exported_deps_for_shared bazel.LabelListAttribute
- Static_deps_for_shared bazel.LabelListAttribute
- Dynamic_deps_for_shared bazel.LabelListAttribute
- Whole_archive_deps_for_shared bazel.LabelListAttribute
- User_link_flags bazel.StringListAttribute
- Version_script bazel.LabelAttribute
+ // Common properties shared between both shared and static variants.
+ Shared staticOrSharedAttributes
+ Static staticOrSharedAttributes
- // Attributes pertaining to static variant.
- Static_srcs bazel.LabelListAttribute
- Static_srcs_c bazel.LabelListAttribute
- Static_srcs_as bazel.LabelListAttribute
- Static_copts bazel.StringListAttribute
+ Strip stripAttributes
+}
- Exported_deps_for_static bazel.LabelListAttribute
- Static_deps_for_static bazel.LabelListAttribute
- Dynamic_deps_for_static bazel.LabelListAttribute
- Whole_archive_deps_for_static bazel.LabelListAttribute
+type stripAttributes struct {
+ Keep_symbols bazel.BoolAttribute
+ Keep_symbols_and_debug_frame bazel.BoolAttribute
+ Keep_symbols_list bazel.StringListAttribute
+ All bazel.BoolAttribute
+ None bazel.BoolAttribute
}
type bazelCcLibrary struct {
@@ -320,23 +315,21 @@
Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
Includes: exportedIncludes,
Linkopts: linkerAttrs.linkopts,
+ Use_libcrt: linkerAttrs.useLibcrt,
- Shared_srcs: sharedAttrs.srcs,
- Shared_srcs_c: sharedAttrs.srcs_c,
- Shared_srcs_as: sharedAttrs.srcs_as,
- Shared_copts: sharedAttrs.copts,
- Static_deps_for_shared: sharedAttrs.staticDeps,
- Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
- Dynamic_deps_for_shared: sharedAttrs.dynamicDeps,
- Version_script: linkerAttrs.versionScript,
+ Version_script: linkerAttrs.versionScript,
- Static_srcs: staticAttrs.srcs,
- Static_srcs_c: staticAttrs.srcs_c,
- Static_srcs_as: staticAttrs.srcs_as,
- Static_copts: staticAttrs.copts,
- Static_deps_for_static: staticAttrs.staticDeps,
- Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps,
- Dynamic_deps_for_static: staticAttrs.dynamicDeps,
+ Strip: stripAttributes{
+ Keep_symbols: linkerAttrs.stripKeepSymbols,
+ Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
+ Keep_symbols_list: linkerAttrs.stripKeepSymbolsList,
+ All: linkerAttrs.stripAll,
+ None: linkerAttrs.stripNone,
+ },
+
+ Shared: sharedAttrs,
+
+ Static: staticAttrs,
}
props := bazel.BazelTargetModuleProperties{
@@ -559,22 +552,10 @@
module *Module
}
-func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
- if !handler.module.static() {
- // TODO(cparsons): Support shared libraries.
- return false
- }
- bazelCtx := ctx.Config().BazelContext
- ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
- if err != nil {
- ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
- return false
- }
- if !ok {
- return ok
- }
+// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
rootStaticArchives := ccInfo.RootStaticArchives
- objPaths := ccInfo.CcObjectFiles
if len(rootStaticArchives) != 1 {
ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
return false
@@ -582,6 +563,7 @@
outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+ objPaths := ccInfo.CcObjectFiles
objFiles := make(android.Paths, len(objPaths))
for i, objPath := range objPaths {
objFiles[i] = android.PathForBazelOut(ctx, objPath)
@@ -595,26 +577,109 @@
ReuseObjects: objects,
Objects: objects,
- // TODO(cparsons): Include transitive static libraries in this provider to support
+ // TODO(b/190524881): Include transitive static libraries in this provider to support
// static libraries with deps.
TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
Direct(outputFilePath).
Build(),
})
- ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo))
+ return true
+}
+
+// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+ rootDynamicLibraries := ccInfo.RootDynamicLibraries
+
+ if len(rootDynamicLibraries) != 1 {
+ ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
+ return false
+ }
+ outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+ handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+
+ handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
+
+ tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+ handler.module.linker.(*libraryDecorator).tocFile = tocFile
+
+ ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+ TableOfContents: tocFile,
+ SharedLibrary: outputFilePath,
+ Target: ctx.Target(),
+ // TODO(b/190524881): Include transitive static libraries in this provider to support
+ // static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering.
+ })
+ return true
+}
+
+// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
+// contains the table of contents of all symbols of a shared object.
+func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
+ var tocFile string
+ for _, file := range outputFiles {
+ if strings.HasSuffix(file, ".so.toc") {
+ if tocFile != "" {
+ ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
+ }
+ tocFile = file
+ // Don't break to validate that there are no multiple .toc files per .so.
+ }
+ }
+ if tocFile == "" {
+ return android.OptionalPath{}
+ }
+ return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
+}
+
+func (handler *ccLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+ bazelCtx := ctx.Config().BazelContext
+ ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+ if err != nil {
+ ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+ return false
+ }
+ if !ok {
+ return ok
+ }
+
+ if handler.module.static() {
+ if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok {
+ return false
+ }
+ } else if handler.module.Shared() {
+ if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok {
+ return false
+ }
+ } else {
+ return false
+ }
+
+ handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo)
+ handler.module.maybeUnhideFromMake()
+
if i, ok := handler.module.linker.(snapshotLibraryInterface); ok {
// Dependencies on this library will expect collectedSnapshotHeaders to
// be set, otherwise validation will fail. For now, set this to an empty
// list.
- // TODO(cparsons): More closely mirror the collectHeadersForSnapshot
+ // TODO(b/190533363): More closely mirror the collectHeadersForSnapshot
// implementation.
i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
}
-
return ok
}
+func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
+ flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
+ // flag exporters consolidates properties like includes, flags, dependencies that should be
+ // exported from this module to other modules
+ ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+ // Store flag info to be passed along to androidmk
+ // TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
+ library.flagExporterInfo = &flagExporterInfo
+}
+
func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
ret := android.Paths{}
@@ -1110,8 +1175,8 @@
deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
} else if library.shared() {
if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
- deps.CrtBegin = "crtbegin_so"
- deps.CrtEnd = "crtend_so"
+ deps.CrtBegin = []string{"crtbegin_so"}
+ deps.CrtEnd = []string{"crtend_so"}
}
deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1341,7 +1406,7 @@
linkerDeps = append(linkerDeps, objs.tidyFiles...)
transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
- linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
+ linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, nil)
objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1352,19 +1417,17 @@
library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
- var staticAnalogue *StaticLibraryInfo
+ var transitiveStaticLibrariesForOrdering *android.DepSet
if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
s := ctx.OtherModuleProvider(static[0], StaticLibraryInfoProvider).(StaticLibraryInfo)
- staticAnalogue = &s
+ transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
}
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- TableOfContents: android.OptionalPathForPath(tocFile),
- SharedLibrary: unstrippedOutputFile,
- UnstrippedSharedLibrary: library.unstrippedOutputFile,
- CoverageSharedLibrary: library.coverageOutputFile,
- StaticAnalogue: staticAnalogue,
- Target: ctx.Target(),
+ TableOfContents: android.OptionalPathForPath(tocFile),
+ SharedLibrary: unstrippedOutputFile,
+ TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
+ Target: ctx.Target(),
})
stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
@@ -2262,6 +2325,7 @@
Whole_archive_deps bazel.LabelListAttribute
Linkopts bazel.StringListAttribute
Linkstatic bool
+ Use_libcrt bazel.BoolAttribute
Includes bazel.StringListAttribute
Hdrs bazel.LabelListAttribute
@@ -2298,6 +2362,7 @@
Linkopts: linkerAttrs.linkopts,
Linkstatic: true,
+ Use_libcrt: linkerAttrs.useLibcrt,
Includes: exportedIncludes,
Cppflags: compilerAttrs.cppFlags,
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 2065929..d6b4529 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -73,13 +73,7 @@
// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
- flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
- // Store flag info to be passed along to androimk
- // TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
- h.library.flagExporterInfo = &flagExporterInfo
- // flag exporters consolidates properties like includes, flags, dependencies that should be
- // exported from this module to other modules
- ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+ h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
// Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
// validation will fail. For now, set this to an empty list.
diff --git a/cc/library_test.go b/cc/library_test.go
index 7975275..ba372a8 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -19,6 +19,7 @@
"testing"
"android/soong/android"
+ "android/soong/bazel/cquery"
)
func TestLibraryReuse(t *testing.T) {
@@ -240,3 +241,48 @@
testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp)
}
+
+func TestCcLibraryWithBazel(t *testing.T) {
+ bp := `
+cc_library {
+ name: "foo",
+ srcs: ["foo.cc"],
+ bazel_module: { label: "//foo/bar:bar" },
+}`
+ config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToCcInfo: map[string]cquery.CcInfo{
+ "//foo/bar:bar": cquery.CcInfo{
+ CcObjectFiles: []string{"foo.o"},
+ Includes: []string{"include"},
+ SystemIncludes: []string{"system_include"},
+ RootStaticArchives: []string{"foo.a"},
+ RootDynamicLibraries: []string{"foo.so"},
+ },
+ },
+ }
+ ctx := testCcWithConfig(t, config)
+
+ staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+ outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+ outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+ }
+ expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+ entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+ expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+ gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+ android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index b583b69..0a5d16c 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -305,14 +305,13 @@
// SharedLibraryInfo is a provider to propagate information about a shared C++ library.
type SharedLibraryInfo struct {
- SharedLibrary android.Path
- UnstrippedSharedLibrary android.Path
- Target android.Target
+ SharedLibrary android.Path
+ Target android.Target
- TableOfContents android.OptionalPath
- CoverageSharedLibrary android.OptionalPath
+ TableOfContents android.OptionalPath
- StaticAnalogue *StaticLibraryInfo
+ // should be obtained from static analogue
+ TransitiveStaticLibrariesForOrdering *android.DepSet
}
var SharedLibraryInfoProvider = blueprint.NewProvider(SharedLibraryInfo{})
diff --git a/cc/linker.go b/cc/linker.go
index 1d8c649..d9ee0cf 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -122,7 +122,7 @@
// version script for vendor or product variant
Version_script *string `android:"arch_variant"`
- }
+ } `android:"arch_variant"`
Recovery struct {
// list of shared libs that only should be used to build the recovery
// variant of the C/C++ module.
@@ -182,7 +182,7 @@
// variant of the C/C++ module.
Exclude_static_libs []string
}
- }
+ } `android:"arch_variant"`
// make android::build:GetBuildNumber() available containing the build ID.
Use_version_lib *bool `android:"arch_variant"`
@@ -200,6 +200,18 @@
Exclude_shared_libs []string `android:"arch_variant"`
}
+func invertBoolPtr(value *bool) *bool {
+ if value == nil {
+ return nil
+ }
+ ret := !(*value)
+ return &ret
+}
+
+func (blp *BaseLinkerProperties) libCrt() *bool {
+ return invertBoolPtr(blp.No_libcrt)
+}
+
func NewBaseLinker(sanitize *sanitize) *baseLinker {
return &baseLinker{sanitize: sanitize}
}
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index b9b57af..927fa2e 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -46,7 +46,7 @@
if m, ok := module.(*Module); ok {
if installer, ok := m.installer.(*stubDecorator); ok {
- if canDumpAbi(m) {
+ if canDumpAbi() {
depPaths = append(depPaths, installer.abiDumpPath)
}
}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index f3d2ba1..a458380 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "runtime"
"strings"
"sync"
@@ -332,12 +333,12 @@
}
// Feature flag.
-func canDumpAbi(module android.Module) bool {
- return true
+func canDumpAbi() bool {
+ return runtime.GOOS != "darwin"
}
// Feature flag to disable diffing against prebuilts.
-func canDiffAbi(module android.Module) bool {
+func canDiffAbi() bool {
return false
}
@@ -452,9 +453,9 @@
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
c.versionScriptPath = nativeAbiResult.versionScript
- if canDumpAbi(ctx.Module()) {
+ if canDumpAbi() {
c.dumpAbi(ctx, nativeAbiResult.symbolList)
- if canDiffAbi(ctx.Module()) {
+ if canDiffAbi() {
c.diffAbi(ctx)
}
}
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index b91c737..51ec6b8 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -186,9 +186,8 @@
})
} else {
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- SharedLibrary: lib,
- UnstrippedSharedLibrary: lib,
- Target: ctx.Target(),
+ SharedLibrary: lib,
+ Target: ctx.Target(),
})
}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 4a9601b..2726b1a 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -144,13 +144,13 @@
Inputs: licensePaths,
})
- baseDepPaths := append(installPaths, combinedLicense,
- getNdkAbiDiffTimestampFile(ctx))
+ baseDepPaths := append(installPaths, combinedLicense)
ctx.Build(pctx, android.BuildParams{
- Rule: android.Touch,
- Output: getNdkBaseTimestampFile(ctx),
- Implicits: baseDepPaths,
+ Rule: android.Touch,
+ Output: getNdkBaseTimestampFile(ctx),
+ Implicits: baseDepPaths,
+ Validation: getNdkAbiDiffTimestampFile(ctx),
})
fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 2387e69..5e6b8f5 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -108,7 +108,14 @@
parser.add_argument(
'--llndk', action='store_true', help='Use the LLNDK variant.')
parser.add_argument(
- '--apex', action='store_true', help='Use the APEX variant.')
+ '--apex',
+ action='store_true',
+ help='Use the APEX variant. Note: equivalent to --system-api.')
+ parser.add_argument(
+ '--system-api',
+ action='store_true',
+ dest='apex',
+ help='Use the SystemAPI variant. Note: equivalent to --apex.')
parser.add_argument('--api-map',
type=resolved_path,
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 3dbab61..09551ea 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -19,9 +19,10 @@
import textwrap
import unittest
-import ndkstubgen
import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tags
+
+import ndkstubgen
# pylint: disable=missing-docstring
@@ -38,23 +39,25 @@
version_file, symbol_list_file,
Arch('arm'), 9, False, False)
- version = symbolfile.Version('VERSION_PRIVATE', None, [], [
- symbolfile.Symbol('foo', []),
+ version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION', None, [Tag('x86')], [
- symbolfile.Symbol('foo', []),
- ])
+ version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
+ [
+ symbolfile.Symbol('foo', Tags()),
+ ])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
- symbolfile.Symbol('foo', []),
- ])
+ version = symbolfile.Version('VERSION', None,
+ Tags.from_strs(['introduced=14']), [
+ symbolfile.Symbol('foo', Tags()),
+ ])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
@@ -69,29 +72,29 @@
version_file, symbol_list_file,
Arch('arm'), 9, False, False)
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('x86')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('introduced=14')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('llndk')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
self.assertEqual('', version_file.getvalue())
- version = symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', [Tag('apex')]),
+ version = symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
])
generator.write_version(version)
self.assertEqual('', src_file.getvalue())
@@ -106,18 +109,17 @@
Arch('arm'), 9, False, False)
versions = [
- symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('var')]),
- symbolfile.Symbol('woodly', [Tag('weak')]),
- symbolfile.Symbol('doodly',
- [Tag('weak'), Tag('var')]),
+ symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['var'])),
+ symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
+ symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
]),
- symbolfile.Version('VERSION_2', 'VERSION_1', [], [
- symbolfile.Symbol('baz', []),
+ symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
+ symbolfile.Symbol('baz', Tags()),
]),
- symbolfile.Version('VERSION_3', 'VERSION_1', [], [
- symbolfile.Symbol('qux', [Tag('versioned=14')]),
+ symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
+ symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
]),
]
diff --git a/cc/pgo.go b/cc/pgo.go
index 95c9c2e..e78549e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -56,7 +56,7 @@
type PgoProperties struct {
Pgo struct {
Instrumentation *bool
- Sampling *bool
+ Sampling *bool `android:"arch_variant"`
Profile_file *string `android:"arch_variant"`
Benchmarks []string
Enable_profile_use *bool `android:"arch_variant"`
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index bea1782..fd310a2 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -183,9 +183,8 @@
})
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- SharedLibrary: outputFile,
- UnstrippedSharedLibrary: p.unstrippedOutputFile,
- Target: ctx.Target(),
+ SharedLibrary: outputFile,
+ Target: ctx.Target(),
TableOfContents: p.tocFile,
})
diff --git a/cc/sabi.go b/cc/sabi.go
index 384dcc1..1f331cb 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -192,7 +192,7 @@
// Mark all of its static library dependencies.
mctx.VisitDirectDeps(func(child android.Module) {
depTag := mctx.OtherModuleDependencyTag(child)
- if libDepTag, ok := depTag.(libraryDependencyTag); ok && libDepTag.static() {
+ if IsStaticDepTag(depTag) || depTag == reuseObjTag {
if c, ok := child.(*Module); ok && c.sabi != nil {
// Mark this module so that .sdump for this static library can be generated.
c.sabi.Properties.ShouldCreateSourceAbiDump = true
diff --git a/cc/sdk.go b/cc/sdk.go
index aec950b..69ad311 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -35,7 +35,8 @@
if !m.UseSdk() && !m.SplitPerApiLevel() {
ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
}
- ctx.CreateVariations("sdk")
+ modules := ctx.CreateVariations("sdk")
+ modules[0].(*Module).Properties.IsSdkVariant = true
} else if m.UseSdk() || m.SplitPerApiLevel() {
modules := ctx.CreateVariations("", "sdk")
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 81590a5..3a382a1 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -642,9 +642,8 @@
transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- SharedLibrary: in,
- UnstrippedSharedLibrary: p.unstrippedOutputFile,
- Target: ctx.Target(),
+ SharedLibrary: in,
+ Target: ctx.Target(),
TableOfContents: p.tocFile,
})
diff --git a/cc/stl.go b/cc/stl.go
index 75921c6..06dc840 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -199,7 +199,9 @@
deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
}
if needsLibAndroidSupport(ctx) {
- deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
+ // Use LateStaticLibs for ndk_libandroid_support so that its include directories
+ // come after ndk_libc++_static or ndk_libc++_shared.
+ deps.LateStaticLibs = append(deps.LateStaticLibs, "ndk_libandroid_support")
}
deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
default:
diff --git a/cc/strip.go b/cc/strip.go
index b1f34bb..c60e135 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -59,6 +59,7 @@
return !forceDisable && (forceEnable || defaultEnable)
}
+// Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
flags StripFlags, isStaticLib bool) {
if actx.Darwin() {
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 5678e7d..31c4443 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -14,18 +14,22 @@
# limitations under the License.
#
"""Parser for Android's version script information."""
-from dataclasses import dataclass
+from __future__ import annotations
+
+from dataclasses import dataclass, field
import logging
import re
from typing import (
Dict,
Iterable,
+ Iterator,
List,
Mapping,
NewType,
Optional,
TextIO,
Tuple,
+ Union,
)
@@ -52,11 +56,52 @@
@dataclass
+class Tags:
+ """Container class for the tags attached to a symbol or version."""
+
+ tags: tuple[Tag, ...] = field(default_factory=tuple)
+
+ @classmethod
+ def from_strs(cls, strs: Iterable[str]) -> Tags:
+ """Constructs tags from a collection of strings.
+
+ Does not decode API levels.
+ """
+ return Tags(tuple(Tag(s) for s in strs))
+
+ def __contains__(self, tag: Union[Tag, str]) -> bool:
+ return tag in self.tags
+
+ def __iter__(self) -> Iterator[Tag]:
+ yield from self.tags
+
+ @property
+ def has_mode_tags(self) -> bool:
+ """Returns True if any mode tags (apex, llndk, etc) are set."""
+ return self.has_apex_tags or self.has_llndk_tags
+
+ @property
+ def has_apex_tags(self) -> bool:
+ """Returns True if any APEX tags are set."""
+ return 'apex' in self.tags or 'systemapi' in self.tags
+
+ @property
+ def has_llndk_tags(self) -> bool:
+ """Returns True if any LL-NDK tags are set."""
+ return 'llndk' in self.tags
+
+ @property
+ def has_platform_only_tags(self) -> bool:
+ """Returns True if any platform-only tags are set."""
+ return 'platform-only' in self.tags
+
+
+@dataclass
class Symbol:
"""A symbol definition from a symbol file."""
name: str
- tags: List[Tag]
+ tags: Tags
@dataclass
@@ -65,14 +110,22 @@
name: str
base: Optional[str]
- tags: List[Tag]
+ tags: Tags
symbols: List[Symbol]
+ @property
+ def is_private(self) -> bool:
+ """Returns True if this version block is private (platform only)."""
+ return self.name.endswith('_PRIVATE') or self.name.endswith('_PLATFORM')
-def get_tags(line: str) -> List[Tag]:
+
+def get_tags(line: str, api_map: ApiMap) -> Tags:
"""Returns a list of all tags on this line."""
_, _, all_tags = line.strip().partition('#')
- return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
+ return Tags(tuple(
+ decode_api_level_tag(Tag(e), api_map)
+ for e in re.split(r'\s+', all_tags) if e.strip()
+ ))
def is_api_level_tag(tag: Tag) -> bool:
@@ -104,24 +157,21 @@
return api_map[api]
-def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
- """Decodes API level code names in a list of tags.
+def decode_api_level_tag(tag: Tag, api_map: ApiMap) -> Tag:
+ """Decodes API level code name in a tag.
Raises:
ParseError: An unknown version name was found in a tag.
"""
- decoded_tags = list(tags)
- for idx, tag in enumerate(tags):
- if not is_api_level_tag(tag):
- continue
- name, value = split_tag(tag)
+ if not is_api_level_tag(tag):
+ return tag
- try:
- decoded = str(decode_api_level(value, api_map))
- decoded_tags[idx] = Tag('='.join([name, decoded]))
- except KeyError:
- raise ParseError(f'Unknown version name in tag: {tag}')
- return decoded_tags
+ name, value = split_tag(tag)
+ try:
+ decoded = str(decode_api_level(value, api_map))
+ return Tag(f'{name}={decoded}')
+ except KeyError as ex:
+ raise ParseError(f'Unknown version name in tag: {tag}') from ex
def split_tag(tag: Tag) -> Tuple[str, str]:
@@ -149,55 +199,52 @@
return split_tag(tag)[1]
-def version_is_private(version: str) -> bool:
- """Returns True if the version name should be treated as private."""
- return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
+def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
+ apex: bool) -> bool:
+ """Returns True if the tagged object should be omitted.
+
+ This defines the rules shared between version tagging and symbol tagging.
+ """
+ # The apex and llndk tags will only exclude APIs from other modes. If in
+ # APEX or LLNDK mode and neither tag is provided, we fall back to the
+ # default behavior because all NDK symbols are implicitly available to APEX
+ # and LLNDK.
+ if tags.has_mode_tags:
+ if not apex and not llndk:
+ return True
+ if apex and not tags.has_apex_tags:
+ return True
+ if llndk and not tags.has_llndk_tags:
+ return True
+ if not symbol_in_arch(tags, arch):
+ return True
+ if not symbol_in_api(tags, arch, api):
+ return True
+ return False
def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool:
- """Returns True if the version section should be ommitted.
+ """Returns True if the version section should be omitted.
We want to omit any sections that do not have any symbols we'll have in the
stub library. Sections that contain entirely future symbols or only symbols
for certain architectures.
"""
- if version_is_private(version.name):
+ if version.is_private:
return True
- if 'platform-only' in version.tags:
+ if version.tags.has_platform_only_tags:
return True
-
- no_llndk_no_apex = ('llndk' not in version.tags
- and 'apex' not in version.tags)
- keep = no_llndk_no_apex or \
- ('llndk' in version.tags and llndk) or \
- ('apex' in version.tags and apex)
- if not keep:
- return True
- if not symbol_in_arch(version.tags, arch):
- return True
- if not symbol_in_api(version.tags, arch, api):
- return True
- return False
+ return _should_omit_tags(version.tags, arch, api, llndk, apex)
def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
apex: bool) -> bool:
"""Returns True if the symbol should be omitted."""
- no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
- keep = no_llndk_no_apex or \
- ('llndk' in symbol.tags and llndk) or \
- ('apex' in symbol.tags and apex)
- if not keep:
- return True
- if not symbol_in_arch(symbol.tags, arch):
- return True
- if not symbol_in_api(symbol.tags, arch, api):
- return True
- return False
+ return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
-def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
+def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
"""Returns true if the symbol is present for the given architecture."""
has_arch_tags = False
for tag in tags:
@@ -325,8 +372,7 @@
"""Parses a single version section and returns a Version object."""
assert self.current_line is not None
name = self.current_line.split('{')[0].strip()
- tags = get_tags(self.current_line)
- tags = decode_api_level_tags(tags, self.api_map)
+ tags = get_tags(self.current_line, self.api_map)
symbols: List[Symbol] = []
global_scope = True
cpp_symbols = False
@@ -373,8 +419,7 @@
'Wildcard global symbols are not permitted.')
# Line is now in the format "<symbol-name>; # tags"
name, _, _ = self.current_line.strip().partition(';')
- tags = get_tags(self.current_line)
- tags = decode_api_level_tags(tags, self.api_map)
+ tags = get_tags(self.current_line, self.api_map)
return Symbol(name, tags)
def next_line(self) -> str:
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 92b1399..c1e8219 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,7 @@
import unittest
import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tag, Tags, Version
# pylint: disable=missing-docstring
@@ -35,12 +35,14 @@
class TagsTest(unittest.TestCase):
def test_get_tags_no_tags(self) -> None:
- self.assertEqual([], symbolfile.get_tags(''))
- self.assertEqual([], symbolfile.get_tags('foo bar baz'))
+ self.assertEqual(Tags(), symbolfile.get_tags('', {}))
+ self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
def test_get_tags(self) -> None:
- self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
- self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
+ self.assertEqual(Tags.from_strs(['foo', 'bar']),
+ symbolfile.get_tags('# foo bar', {}))
+ self.assertEqual(Tags.from_strs(['bar', 'baz']),
+ symbolfile.get_tags('foo # bar baz', {}))
def test_split_tag(self) -> None:
self.assertTupleEqual(('foo', 'bar'),
@@ -77,12 +79,14 @@
}
tags = [
- Tag('introduced=9'),
- Tag('introduced-arm=14'),
- Tag('versioned=16'),
- Tag('arm'),
- Tag('introduced=O'),
- Tag('introduced=P'),
+ symbolfile.decode_api_level_tag(t, api_map) for t in (
+ Tag('introduced=9'),
+ Tag('introduced-arm=14'),
+ Tag('versioned=16'),
+ Tag('arm'),
+ Tag('introduced=O'),
+ Tag('introduced=P'),
+ )
]
expected_tags = [
Tag('introduced=9'),
@@ -92,33 +96,37 @@
Tag('introduced=9000'),
Tag('introduced=9001'),
]
- self.assertListEqual(
- expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
+ self.assertListEqual(expected_tags, tags)
with self.assertRaises(symbolfile.ParseError):
- symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
+ symbolfile.decode_api_level_tag(Tag('introduced=O'), {})
class PrivateVersionTest(unittest.TestCase):
def test_version_is_private(self) -> None:
- self.assertFalse(symbolfile.version_is_private('foo'))
- self.assertFalse(symbolfile.version_is_private('PRIVATE'))
- self.assertFalse(symbolfile.version_is_private('PLATFORM'))
- self.assertFalse(symbolfile.version_is_private('foo_private'))
- self.assertFalse(symbolfile.version_is_private('foo_platform'))
- self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
- self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
+ def mock_version(name: str) -> Version:
+ return Version(name, base=None, tags=Tags(), symbols=[])
- self.assertTrue(symbolfile.version_is_private('foo_PRIVATE'))
- self.assertTrue(symbolfile.version_is_private('foo_PLATFORM'))
+ self.assertFalse(mock_version('foo').is_private)
+ self.assertFalse(mock_version('PRIVATE').is_private)
+ self.assertFalse(mock_version('PLATFORM').is_private)
+ self.assertFalse(mock_version('foo_private').is_private)
+ self.assertFalse(mock_version('foo_platform').is_private)
+ self.assertFalse(mock_version('foo_PRIVATE_').is_private)
+ self.assertFalse(mock_version('foo_PLATFORM_').is_private)
+
+ self.assertTrue(mock_version('foo_PRIVATE').is_private)
+ self.assertTrue(mock_version('foo_PLATFORM').is_private)
class SymbolPresenceTest(unittest.TestCase):
def test_symbol_in_arch(self) -> None:
- self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
- self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
+ self.assertTrue(symbolfile.symbol_in_arch(Tags(), Arch('arm')))
+ self.assertTrue(
+ symbolfile.symbol_in_arch(Tags.from_strs(['arm']), Arch('arm')))
- self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
+ self.assertFalse(
+ symbolfile.symbol_in_arch(Tags.from_strs(['x86']), Arch('arm')))
def test_symbol_in_api(self) -> None:
self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
@@ -197,81 +205,99 @@
def test_omit_private(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
- 9, False, False))
+ symbolfile.Version('foo_PRIVATE', None, Tags(), []),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
- 9, False, False))
+ symbolfile.Version('foo_PLATFORM', None, Tags(), []),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('platform-only')], []),
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['platform-only']), []),
Arch('arm'), 9, False, False))
def test_omit_llndk(self) -> None:
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('llndk')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ True, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('llndk')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None:
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('apex')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- True))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, True))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('apex')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
Arch('arm'), 9, False, True))
+ def test_omit_systemapi(self) -> None:
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+ []), Arch('arm'), 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, True))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+ []), Arch('arm'), 9, False, True))
+
def test_omit_arch(self) -> None:
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
self.assertFalse(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
- 9, False, False))
-
- self.assertTrue(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
- 9, False, False))
-
- def test_omit_api(self) -> None:
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
- False))
- self.assertFalse(
- symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('introduced=9')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_version(
- symbolfile.Version('foo', None, [Tag('introduced=14')], []),
+ symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
+ Arch('arm'), 9, False, False))
+
+ def test_omit_api(self) -> None:
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+ False, False))
+ self.assertFalse(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['introduced=9']), []),
+ Arch('arm'), 9, False, False))
+
+ self.assertTrue(
+ symbolfile.should_omit_version(
+ symbolfile.Version('foo', None,
+ Tags.from_strs(['introduced=14']), []),
Arch('arm'), 9, False, False))
@@ -279,58 +305,72 @@
def test_omit_llndk(self) -> None:
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
- False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+ Arch('arm'), 9, False, False))
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, True, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+ Arch('arm'), 9, True, False))
def test_omit_apex(self) -> None:
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+ Arch('arm'), 9, False, False))
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, True))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
- True))
+ symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+ Arch('arm'), 9, False, True))
+
+ def test_omit_systemapi(self) -> None:
+ self.assertTrue(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+ Arch('arm'), 9, False, False))
+
+ self.assertFalse(
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
+ Arch('arm'), 9, False, True))
+ self.assertFalse(
+ symbolfile.should_omit_symbol(
+ symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+ Arch('arm'), 9, False, True))
def test_omit_arch(self) -> None:
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
+ 9, False, False))
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
- False))
+ symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
+ 9, False, False))
def test_omit_api(self) -> None:
self.assertFalse(
- symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+ symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
Arch('arm'), 9, False, False))
self.assertFalse(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
- 9, False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
+ Arch('arm'), 9, False, False))
self.assertTrue(
symbolfile.should_omit_symbol(
- symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
- 9, False, False))
+ symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
+ Arch('arm'), 9, False, False))
class SymbolFileParseTest(unittest.TestCase):
@@ -376,11 +416,11 @@
version = parser.parse_version()
self.assertEqual('VERSION_1', version.name)
self.assertIsNone(version.base)
- self.assertEqual(['foo', 'bar'], version.tags)
+ self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
expected_symbols = [
- symbolfile.Symbol('baz', []),
- symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
+ symbolfile.Symbol('baz', Tags()),
+ symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
]
self.assertEqual(expected_symbols, version.symbols)
@@ -388,7 +428,7 @@
version = parser.parse_version()
self.assertEqual('VERSION_2', version.name)
self.assertEqual('VERSION_1', version.base)
- self.assertEqual([], version.tags)
+ self.assertEqual(Tags(), version.tags)
def test_parse_version_eof(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
@@ -423,12 +463,12 @@
parser.next_line()
symbol = parser.parse_symbol()
self.assertEqual('foo', symbol.name)
- self.assertEqual([], symbol.tags)
+ self.assertEqual(Tags(), symbol.tags)
parser.next_line()
symbol = parser.parse_symbol()
self.assertEqual('bar', symbol.name)
- self.assertEqual(['baz', 'qux'], symbol.tags)
+ self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
def test_wildcard_symbol_global(self) -> None:
input_file = io.StringIO(textwrap.dedent("""\
@@ -497,14 +537,15 @@
versions = parser.parse()
expected = [
- symbolfile.Version('VERSION_1', None, [], [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('baz')]),
+ symbolfile.Version('VERSION_1', None, Tags(), [
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
]),
- symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
- symbolfile.Symbol('woodly', []),
- symbolfile.Symbol('doodly', [Tag('asdf')]),
- ]),
+ symbolfile.Version(
+ 'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
+ symbolfile.Symbol('woodly', Tags()),
+ symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+ ]),
]
self.assertEqual(expected, versions)
@@ -527,10 +568,10 @@
self.assertIsNone(version.base)
expected_symbols = [
- symbolfile.Symbol('foo', []),
- symbolfile.Symbol('bar', [Tag('llndk')]),
- symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
- symbolfile.Symbol('qux', [Tag('apex')]),
+ symbolfile.Symbol('foo', Tags()),
+ symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
+ symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+ symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
]
self.assertEqual(expected_symbols, version.symbols)
diff --git a/cc/testing.go b/cc/testing.go
index f5c5ec5..80cc0ef 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -34,6 +34,7 @@
ctx.RegisterModuleType("cc_object", ObjectFactory)
ctx.RegisterModuleType("cc_genrule", genRuleFactory)
ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
+ ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
}
@@ -403,7 +404,7 @@
cc_library {
name: "ndk_libunwind",
- sdk_version: "current",
+ sdk_version: "minimum",
stl: "none",
system_shared_libs: [],
}
@@ -428,6 +429,12 @@
ndk_prebuilt_shared_stl {
name: "ndk_libc++_shared",
+ export_include_dirs: ["ndk_libc++_shared"],
+ }
+
+ ndk_prebuilt_static_stl {
+ name: "ndk_libandroid_support",
+ export_include_dirs: ["ndk_libandroid_support"],
}
cc_library_static {
@@ -490,7 +497,7 @@
}
cc_genrule {
- name: "host_bionic_linker_flags",
+ name: "host_bionic_linker_script",
host_supported: true,
device_supported: false,
target: {
@@ -501,7 +508,7 @@
enabled: true,
},
},
- out: ["linker.flags"],
+ out: ["linker.script"],
}
cc_defaults {
@@ -578,9 +585,11 @@
// Additional files needed in tests that disallow non-existent source.
android.MockFS{
- "defaults/cc/common/libc.map.txt": nil,
- "defaults/cc/common/libdl.map.txt": nil,
- "defaults/cc/common/libm.map.txt": nil,
+ "defaults/cc/common/libc.map.txt": nil,
+ "defaults/cc/common/libdl.map.txt": nil,
+ "defaults/cc/common/libm.map.txt": nil,
+ "defaults/cc/common/ndk_libandroid_support": nil,
+ "defaults/cc/common/ndk_libc++_shared": nil,
}.AddToFixture(),
// Place the default cc test modules that are common to all platforms in a location that will not
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 141f438..da34f36 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -168,9 +168,8 @@
}
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
- SharedLibrary: in,
- UnstrippedSharedLibrary: p.unstrippedOutputFile,
- Target: ctx.Target(),
+ SharedLibrary: in,
+ Target: ctx.Target(),
TableOfContents: p.tocFile,
})
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index ea0bf4e..2dcb894 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -13,7 +13,7 @@
// limitations under the License.
// This tool extracts ELF LOAD segments from our linker binary, and produces an
-// assembly file and linker flags which will embed those segments as sections
+// assembly file and linker script which will embed those segments as sections
// in another binary.
package main
@@ -31,10 +31,10 @@
func main() {
var asmPath string
- var flagsPath string
+ var scriptPath string
flag.StringVar(&asmPath, "s", "", "Path to save the assembly file")
- flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags")
+ flag.StringVar(&scriptPath, "T", "", "Path to save the linker script")
flag.Parse()
f, err := os.Open(flag.Arg(0))
@@ -49,20 +49,30 @@
}
asm := &bytes.Buffer{}
+ script := &bytes.Buffer{}
baseLoadAddr := uint64(0x1000)
load := 0
- linkFlags := []string{}
fmt.Fprintln(asm, ".globl __dlwrap_linker_offset")
fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr)
+ fmt.Fprintln(script, "ENTRY(__dlwrap__start)")
+ fmt.Fprintln(script, "SECTIONS {")
+
for _, prog := range ef.Progs {
if prog.Type != elf.PT_LOAD {
continue
}
- sectionName := fmt.Sprintf(".linker.sect%d", load)
- symName := fmt.Sprintf("__dlwrap_linker_sect%d", load)
+ var progName string
+ progSection := progToFirstSection(prog, ef.Sections)
+ if progSection != nil {
+ progName = progSection.Name
+ } else {
+ progName = fmt.Sprintf(".sect%d", load)
+ }
+ sectionName := ".linker" + progName
+ symName := "__dlwrap_linker" + strings.ReplaceAll(progName, ".", "_")
flags := ""
if prog.Flags&elf.PF_W != 0 {
@@ -75,10 +85,9 @@
fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
- linkFlags = append(linkFlags,
- fmt.Sprintf("-Wl,--undefined=%s", symName),
- fmt.Sprintf("-Wl,--section-start=%s=0x%x",
- sectionName, baseLoadAddr+prog.Vaddr))
+ fmt.Fprintf(script, " %s %d : {\n", sectionName, baseLoadAddr+prog.Vaddr)
+ fmt.Fprintf(script, " KEEP(*(%s));\n", sectionName)
+ fmt.Fprintln(script, " }")
buffer, _ := ioutil.ReadAll(prog.Open())
bytesToAsm(asm, buffer)
@@ -97,16 +106,18 @@
load += 1
}
+ fmt.Fprintln(script, "}")
+ fmt.Fprintln(script, "INSERT BEFORE .note.android.ident;")
+
if asmPath != "" {
if err := ioutil.WriteFile(asmPath, asm.Bytes(), 0777); err != nil {
log.Fatalf("Unable to write %q: %v", asmPath, err)
}
}
- if flagsPath != "" {
- flags := strings.Join(linkFlags, " ")
- if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil {
- log.Fatalf("Unable to write %q: %v", flagsPath, err)
+ if scriptPath != "" {
+ if err := ioutil.WriteFile(scriptPath, script.Bytes(), 0777); err != nil {
+ log.Fatalf("Unable to write %q: %v", scriptPath, err)
}
}
}
@@ -125,3 +136,12 @@
}
fmt.Fprintln(asm)
}
+
+func progToFirstSection(prog *elf.Prog, sections []*elf.Section) *elf.Section {
+ for _, section := range sections {
+ if section.Addr == prog.Vaddr {
+ return section
+ }
+ }
+ return nil
+}
diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/host_bionic_verify/Android.bp
similarity index 82%
rename from cmd/host_bionic_inject/Android.bp
rename to cmd/host_bionic_verify/Android.bp
index 16bc179..4e7c379 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/cmd/host_bionic_verify/Android.bp
@@ -17,8 +17,7 @@
}
blueprint_go_binary {
- name: "host_bionic_inject",
- deps: ["soong-symbol_inject"],
- srcs: ["host_bionic_inject.go"],
- testSrcs: ["host_bionic_inject_test.go"],
+ name: "host_bionic_verify",
+ srcs: ["host_bionic_verify.go"],
+ testSrcs: ["host_bionic_verify_test.go"],
}
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_verify/host_bionic_verify.go
similarity index 68%
rename from cmd/host_bionic_inject/host_bionic_inject.go
rename to cmd/host_bionic_verify/host_bionic_verify.go
index ce8b062..52400a3 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_verify/host_bionic_verify.go
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Verifies a host bionic executable with an embedded linker, then injects
-// the address of the _start function for the linker_wrapper to use.
+// Verifies a host bionic executable with an embedded linker.
package main
import (
@@ -22,19 +21,16 @@
"fmt"
"io"
"os"
-
- "android/soong/symbol_inject"
)
func main() {
- var inputFile, linkerFile, outputFile string
+ var inputFile, linkerFile string
flag.StringVar(&inputFile, "i", "", "Input file")
flag.StringVar(&linkerFile, "l", "", "Linker file")
- flag.StringVar(&outputFile, "o", "", "Output file")
flag.Parse()
- if inputFile == "" || linkerFile == "" || outputFile == "" || flag.NArg() != 0 {
+ if inputFile == "" || linkerFile == "" || flag.NArg() != 0 {
flag.Usage()
os.Exit(1)
}
@@ -46,75 +42,52 @@
}
defer r.Close()
- file, err := symbol_inject.OpenFile(r)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(3)
- }
-
linker, err := elf.Open(linkerFile)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(4)
}
- startAddr, err := parseElf(r, linker)
+ err = checkElf(r, linker)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(5)
}
-
- w, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(6)
- }
- defer w.Close()
-
- err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", startAddr)
- if err != nil {
- fmt.Fprintln(os.Stderr, err.Error())
- os.Exit(7)
- }
}
// Check the ELF file, and return the address to the _start function
-func parseElf(r io.ReaderAt, linker *elf.File) (uint64, error) {
+func checkElf(r io.ReaderAt, linker *elf.File) error {
file, err := elf.NewFile(r)
if err != nil {
- return 0, err
+ return err
}
symbols, err := file.Symbols()
if err != nil {
- return 0, err
+ return err
}
for _, prog := range file.Progs {
if prog.Type == elf.PT_INTERP {
- return 0, fmt.Errorf("File should not have a PT_INTERP header")
+ return fmt.Errorf("File should not have a PT_INTERP header")
}
}
if dlwrap_start, err := findSymbol(symbols, "__dlwrap__start"); err != nil {
- return 0, err
+ return err
} else if dlwrap_start.Value != file.Entry {
- return 0, fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
+ return fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
file.Entry, dlwrap_start.Value)
}
err = checkLinker(file, linker, symbols)
if err != nil {
- return 0, fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
+ return fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
"linker might not be in sync with crtbegin_dynamic.o.",
err)
}
- start, err := findSymbol(symbols, "_start")
- if err != nil {
- return 0, fmt.Errorf("Failed to find _start symbol")
- }
- return start.Value, nil
+ return nil
}
func findSymbol(symbols []elf.Symbol, name string) (elf.Symbol, error) {
diff --git a/cmd/host_bionic_inject/host_bionic_inject_test.go b/cmd/host_bionic_verify/host_bionic_verify_test.go
similarity index 100%
rename from cmd/host_bionic_inject/host_bionic_inject_test.go
rename to cmd/host_bionic_verify/host_bionic_verify_test.go
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 20f146a..55a5470 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -460,19 +460,21 @@
}
}()
- buildWhat := build.BuildProductConfig
+ config.SetSkipNinja(true)
+
+ buildWhat := build.RunProductConfig
if !*onlyConfig {
- buildWhat |= build.BuildSoong
+ buildWhat |= build.RunSoong
if !*onlySoong {
- buildWhat |= build.BuildKati
+ buildWhat |= build.RunKati
}
}
before := time.Now()
- build.Build(ctx, config, buildWhat)
+ build.Build(ctx, config)
// Save std_full.log if Kati re-read the makefiles
- if buildWhat&build.BuildKati != 0 {
+ if buildWhat&build.RunKati != 0 {
if after, err := os.Stat(config.KatiBuildNinjaFile()); err == nil && after.ModTime().After(before) {
err := copyFile(stdLog, filepath.Join(filepath.Dir(stdLog), "std_full.log"))
if err != nil {
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 390a9ec..22922c0 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -59,12 +59,10 @@
run func(ctx build.Context, config build.Config, args []string, logsDir string)
}
-const makeModeFlagName = "--make-mode"
-
// list of supported commands (flags) supported by soong ui
var commands []command = []command{
{
- flag: makeModeFlagName,
+ flag: "--make-mode",
description: "build the modules by the target name (i.e. soong_docs)",
config: func(ctx build.Context, args ...string) build.Config {
return build.NewConfig(ctx, args...)
@@ -506,15 +504,7 @@
ctx.Fatal("done")
}
- toBuild := build.BuildAll
- if config.UseBazel() {
- toBuild = build.BuildAllWithBazel
- }
-
- if config.Checkbuild() {
- toBuild |= build.RunBuildTests
- }
- build.Build(ctx, config, toBuild)
+ build.Build(ctx, config)
}
// getCommand finds the appropriate command based on args[1] flag. args[0]
diff --git a/cuj/cuj.go b/cuj/cuj.go
index 3333012..b4ae9a2 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -115,7 +115,7 @@
defer f.Shutdown()
build.FindSources(buildCtx, config, f)
- build.Build(buildCtx, config, build.BuildAll)
+ build.Build(buildCtx, config)
t.results.metrics = met
}
diff --git a/docs/map_files.md b/docs/map_files.md
index 192530f..1388059 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -70,9 +70,11 @@
### apex
-Indicates that the version or symbol is to be exposed in the APEX stubs rather
-than the NDK. May be used in combination with `llndk` if the symbol is exposed
-to both APEX and the LL-NDK.
+Indicates that the version or symbol is to be exposed by an APEX rather than the
+NDK. For APIs exposed by the platform *for* APEX, use `systemapi`.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
### future
@@ -144,6 +146,12 @@
preferable to keep such APIs in an entirely separate library to protect them
from access via `dlsym`, but this is not always possible.
+### systemapi
+
+This is a synonym of the `apex` tag. It should be used to clarify that the API
+is an API exposed by the system for an APEX, whereas `apex` should be used for
+APIs exposed by an APEX to the platform or another APEX.
+
### var
Used to define a public global variable. By default all symbols are exposed as
diff --git a/java/Android.bp b/java/Android.bp
index 680f3a1..5952602 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -33,6 +33,7 @@
"bootclasspath.go",
"bootclasspath_fragment.go",
"builder.go",
+ "classpath_element.go",
"classpath_fragment.go",
"device_host_converter.go",
"dex.go",
diff --git a/java/base.go b/java/base.go
index 440b004..a7cc58e 100644
--- a/java/base.go
+++ b/java/base.go
@@ -155,6 +155,13 @@
// List of java_plugin modules that provide extra errorprone checks.
Extra_check_modules []string
+
+ // This property can be in 3 states. When set to true, errorprone will
+ // be run during the regular build. When set to false, errorprone will
+ // never be run. When unset, errorprone will be run when the RUN_ERROR_PRONE
+ // environment variable is true. Setting this to false will improve build
+ // performance more than adding -XepDisableAllChecks in javacflags.
+ Enabled *bool
}
Proto struct {
@@ -701,7 +708,8 @@
// javaVersion flag.
flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
- if ctx.Config().RunErrorProne() {
+ epEnabled := j.properties.Errorprone.Enabled
+ if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) {
if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
}
@@ -972,14 +980,23 @@
}
if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
var extraJarDeps android.Paths
- if ctx.Config().RunErrorProne() {
- // If error-prone is enabled, add an additional rule to compile the java files into
- // a separate set of classes (so that they don't overwrite the normal ones and require
- // a rebuild when error-prone is turned off).
- // TODO(ccross): Once we always compile with javac9 we may be able to conditionally
- // enable error-prone without affecting the output class files.
+ if Bool(j.properties.Errorprone.Enabled) {
+ // If error-prone is enabled, enable errorprone flags on the regular
+ // build.
+ flags = enableErrorproneFlags(flags)
+ } else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
+ // Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
+ // a new jar file just for compiling with the errorprone compiler to.
+ // This is because we don't want to cause the java files to get completely
+ // rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
+ // We also don't want to run this if errorprone is enabled by default for
+ // this module, or else we could have duplicated errorprone messages.
+ errorproneFlags := enableErrorproneFlags(flags)
errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
- RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
+
+ transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+ "errorprone", "errorprone")
+
extraJarDeps = append(extraJarDeps, errorprone)
}
@@ -1303,6 +1320,21 @@
j.outputFile = outputFile.WithoutRel()
}
+// Returns a copy of the supplied flags, but with all the errorprone-related
+// fields copied to the regular build's fields.
+func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags {
+ flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
+
+ if len(flags.errorProneExtraJavacFlags) > 0 {
+ if len(flags.javacFlags) > 0 {
+ flags.javacFlags += " " + flags.errorProneExtraJavacFlags
+ } else {
+ flags.javacFlags = flags.errorProneExtraJavacFlags
+ }
+ }
+ return flags
+}
+
func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 7abda80..86ebe36 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -18,37 +18,6 @@
"android/soong/android"
)
-func init() {
- android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
-}
-
-func bootJarsSingletonFactory() android.Singleton {
- return &bootJarsSingleton{}
-}
-
-type bootJarsSingleton struct{}
-
-func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
- for i := 0; i < list.Len(); i++ {
- module := list.Jar(i)
- // Ignore jacocoagent it is only added when instrumenting and so has no impact on
- // app compatibility.
- if module == "jacocoagent" {
- continue
- }
- apex := list.Apex(i)
- if existing, ok := moduleToApex[module]; ok {
- ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
- module, existing, apex)
- return false
- }
-
- moduleToApex[module] = apex
- }
-
- return true
-}
-
// isActiveModule returns true if the given module should be considered for boot
// jars, i.e. if it's enabled and the preferred one in case of source and
// prebuilt alternatives.
@@ -59,73 +28,17 @@
return android.IsModulePreferred(module)
}
-func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
- config := ctx.Config()
- if config.SkipBootJarsCheck() {
- return
- }
-
- // Populate a map from module name to APEX from the boot jars. If there is a
- // problem such as duplicate modules then fail and return immediately. Note
- // that both module and APEX names are tracked by base names here, so we need
- // to be careful to remove "prebuilt_" prefixes when comparing them with
- // actual modules and APEX bundles.
- moduleToApex := make(map[string]string)
- if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
- !populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
- return
- }
-
- // Map from module name to the correct apex variant.
- nameToApexVariant := make(map[string]android.Module)
-
- // Scan all the modules looking for the module/apex variants corresponding to the
- // boot jars.
- ctx.VisitAllModules(func(module android.Module) {
- if !isActiveModule(module) {
- return
- }
-
- name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
- if apex, ok := moduleToApex[name]; ok {
- apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexModule(apex) {
- // The module name/apex variant should be unique in the system but double check
- // just in case something has gone wrong.
- if existing, ok := nameToApexVariant[name]; ok {
- ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
- }
- nameToApexVariant[name] = module
- }
- }
- })
-
+// buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package
+// check.
+func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) {
timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
rule := android.NewRuleBuilder(pctx, ctx)
- checkBootJars := rule.Command().BuiltTool("check_boot_jars").
+ rule.Command().BuiltTool("check_boot_jars").
Input(ctx.Config().HostToolPath(ctx, "dexdump")).
- Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
-
- // If this is not an unbundled build and missing dependencies are not allowed
- // then all the boot jars listed must have been found.
- strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
-
- // Iterate over the module names on the boot classpath in order
- for _, name := range android.SortedStringKeys(moduleToApex) {
- if apexVariant, ok := nameToApexVariant[name]; ok {
- if dep, ok := apexVariant.(interface{ DexJarBuildPath() android.Path }); ok {
- // Add the dex implementation jar for the module to be checked.
- checkBootJars.Input(dep.DexJarBuildPath())
- } else {
- ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
- }
- } else if strict {
- ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
- }
- }
-
- checkBootJars.Text("&& touch").Output(timestamp)
+ Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")).
+ Inputs(bootDexJarByModule.bootDexJarsWithoutCoverage()).
+ Text("&& touch").Output(timestamp)
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.
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 7936c2b..4d23820 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -81,6 +81,9 @@
// they were listed in java_libs.
func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
+// Contents of bootclasspath fragments require files from prebuilt apex files.
+func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
+
// The tag used for the dependency between the bootclasspath_fragment module and its contents.
var bootclasspathFragmentContentDepTag = bootclasspathFragmentContentDependencyTag{}
@@ -88,6 +91,7 @@
var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
var _ android.SdkMemberTypeDependencyTag = bootclasspathFragmentContentDepTag
var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
+var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
return tag == bootclasspathFragmentContentDepTag
@@ -140,14 +144,29 @@
// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
// bootclasspath fragment modules.
type commonBootclasspathFragment interface {
- // produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files.
+ // produceHiddenAPIOutput produces the all-flags.csv and intermediate files and encodes the flags
+ // into dex files.
//
- // Updates the supplied hiddenAPIInfo with the paths to the generated files set.
- produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput
+ // Returns a *HiddenAPIOutput containing the paths for the generated files. Returns nil if the
+ // module cannot contribute to hidden API processing, e.g. because it is a prebuilt module in a
+ // versioned sdk.
+ produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput
+
+ // produceBootImageFiles produces the boot image (i.e. .art, .oat and .vdex) files for each of the
+ // required android.ArchType values in the returned map.
+ //
+ // It must return nil if the boot image files cannot be produced for whatever reason.
+ produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch
}
var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
func bootclasspathFragmentFactory() android.Module {
m := &BootclasspathFragmentModule{}
m.AddProperties(&m.properties)
@@ -233,14 +252,6 @@
commonApex, apex)
}
}
-
- if len(contents) != 0 {
- // Nothing to do.
- return
- }
-
- // Store the jars in the Contents property so that they can be used to add dependencies.
- m.properties.Contents = modules.CopyOfJars()
}
// bootclasspathImageNameContentsConsistencyCheck checks that the configuration that applies to this
@@ -293,11 +304,11 @@
modules android.ConfiguredJarList
// Map from arch type to the boot image files.
- bootImageFilesByArch map[android.ArchType]android.OutputPaths
+ bootImageFilesByArch bootImageFilesByArch
- // Map from the name of the context module (as returned by Name()) to the hidden API encoded dex
- // jar path.
- contentModuleDexJarPaths map[string]android.Path
+ // Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
+ // hidden API encoded dex jar path.
+ contentModuleDexJarPaths bootDexJarByModule
}
func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
@@ -307,7 +318,7 @@
// Get a map from ArchType to the associated boot image's contents for Android.
//
// Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
return i.bootImageFilesByArch
}
@@ -315,6 +326,8 @@
//
// The dex boot jar is one which has had hidden API encoding performed on it.
func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) {
+ // A bootclasspath_fragment cannot use a prebuilt library so Name() will return the base name
+ // without a prebuilt_ prefix so is safe to use as the key for the contentModuleDexJarPaths.
name := module.Name()
if dexJar, ok := i.contentModuleDexJarPaths[name]; ok {
return dexJar, nil
@@ -406,102 +419,93 @@
fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag)
- // Perform hidden API processing.
- hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
-
// Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a
// prebuilt which will not use the image config.
imageConfig := b.getImageConfig(ctx)
- // A prebuilt fragment cannot contribute to the apex.
- if !android.IsModulePrebuilt(ctx.Module()) {
- // Provide the apex content info.
- b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput)
+ // A versioned prebuilt_bootclasspath_fragment cannot and does not need to perform hidden API
+ // processing. It cannot do it because it is not part of a prebuilt_apex and so has no access to
+ // the correct dex implementation jar. It does not need to because the platform-bootclasspath
+ // always references the latest bootclasspath_fragments.
+ if !android.IsModuleInVersionedSdk(ctx.Module()) {
+ // Perform hidden API processing.
+ hiddenAPIOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments)
+
+ var bootImageFilesByArch bootImageFilesByArch
+ if imageConfig != nil {
+ // Delegate the production of the boot image files to a module type specific method.
+ common := ctx.Module().(commonBootclasspathFragment)
+ bootImageFilesByArch = common.produceBootImageFiles(ctx, imageConfig, contents)
+
+ if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ // Copy the dex jars of this fragment's content modules to their predefined locations.
+ copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
+ }
+ }
+
+ // A prebuilt fragment cannot contribute to an apex.
+ if !android.IsModulePrebuilt(ctx.Module()) {
+ // Provide the apex content info.
+ b.provideApexContentInfo(ctx, imageConfig, hiddenAPIOutput, bootImageFilesByArch)
+ }
}
}
+// shouldCopyBootFilesToPredefinedLocations determines whether the current module should copy boot
+// files, e.g. boot dex jars or boot image files, to the predefined location expected by the rest
+// of the build.
+//
+// This ensures that only a single module will copy its files to the image configuration.
+func shouldCopyBootFilesToPredefinedLocations(ctx android.ModuleContext, imageConfig *bootImageConfig) bool {
+ // Bootclasspath fragment modules that are for the platform do not produce boot related files.
+ apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ return false
+ }
+
+ // If the image configuration has no modules specified then it means that the build has been
+ // configured to build something other than a boot image, e.g. an sdk, so do not try and copy the
+ // files.
+ if imageConfig.modules.Len() == 0 {
+ return false
+ }
+
+ // Only copy files from the module that is preferred.
+ return isActiveModule(ctx.Module())
+}
+
// provideApexContentInfo creates, initializes and stores the apex content info for use by other
// modules.
-func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
+func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, hiddenAPIOutput *HiddenAPIOutput, bootImageFilesByArch bootImageFilesByArch) {
// Construct the apex content info from the config.
- info := BootclasspathFragmentApexContentInfo{}
-
- // Populate the apex content info with paths to the dex jars.
- b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput)
+ info := BootclasspathFragmentApexContentInfo{
+ // Populate the apex content info with paths to the dex jars.
+ contentModuleDexJarPaths: hiddenAPIOutput.EncodedBootDexFilesByModule,
+ }
if imageConfig != nil {
info.modules = imageConfig.modules
-
- if !SkipDexpreoptBootJars(ctx) {
- // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
- // GenerateSingletonBuildActions method as it cannot create it for itself.
- dexpreopt.GetGlobalSoongConfig(ctx)
-
- // Only generate the boot image if the configuration does not skip it.
- if b.generateBootImageBuildActions(ctx, contents, imageConfig) {
- // Allow the apex to access the boot image files.
- files := map[android.ArchType]android.OutputPaths{}
- for _, variant := range imageConfig.variants {
- // We also generate boot images for host (for testing), but we don't need those in the apex.
- // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
- if variant.target.Os == android.Android {
- files[variant.target.Arch.ArchType] = variant.imagesDeps
- }
- }
- info.bootImageFilesByArch = files
- }
- }
}
+ info.bootImageFilesByArch = bootImageFilesByArch
+
// Make the apex content info available for other modules.
ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
}
-// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the
-// apex content info.
-func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) {
-
- info.contentModuleDexJarPaths = map[string]android.Path{}
- if hiddenAPIFlagOutput != nil {
- // Hidden API encoding has been performed.
- flags := hiddenAPIFlagOutput.AllFlagsPath
- for _, m := range contents {
- h := m.(hiddenAPIModule)
- unencodedDex := h.bootDexJar()
- if unencodedDex == nil {
- // This is an error. Sometimes Soong will report the error directly, other times it will
- // defer the error reporting to happen only when trying to use the missing file in ninja.
- // Either way it is handled by extractBootDexJarsFromHiddenAPIModules which must have been
- // called before this as it generates the flags that are used to encode these files.
- continue
- }
-
- outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
- encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, flags, *h.uncompressDex(), outputDir)
- info.contentModuleDexJarPaths[m.Name()] = encodedDex
- }
- } else {
- for _, m := range contents {
- j := m.(UsesLibraryDependency)
- dexJar := j.DexJarBuildPath()
- info.contentModuleDexJarPaths[m.Name()] = dexJar
- }
- }
-}
-
// generateClasspathProtoBuildActions generates all required build actions for classpath.proto config
func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
var classpathJars []classpathJar
if "art" == proptools.String(b.properties.Image_name) {
// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
- classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
} else {
- classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType)
+ classpathJars = configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), b.classpathType)
}
b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
}
-func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
if "art" == proptools.String(b.properties.Image_name) {
return b.getImageConfig(ctx).modules
}
@@ -522,7 +526,16 @@
// Only create configs for updatable boot jars. Non-updatable boot jars must be part of the
// platform_bootclasspath's classpath proto config to guarantee that they come before any
// updatable jars at runtime.
- return global.UpdatableBootJars.Filter(stems)
+ jars := global.UpdatableBootJars.Filter(stems)
+
+ // TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
+ // config. However, any test specific jars would not be present in UpdatableBootJars. Instead,
+ // we should check if we are creating a config for apex_test via ApexInfo and amend the values.
+ // This is an exception to support end-to-end test for SdkExtensions, until such support exists.
+ if android.InList("test_framework-sdkextensions", stems) {
+ jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions")
+ }
+ return jars
}
func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
@@ -545,12 +558,12 @@
}
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput {
+func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
// Create hidden API input structure.
input := b.createHiddenAPIFlagInput(ctx, contents, fragments)
- var output *HiddenAPIFlagOutput
+ var output *HiddenAPIOutput
// Hidden API processing is conditional as a temporary workaround as not all
// bootclasspath_fragments provide the appropriate information needed for hidden API processing
@@ -558,12 +571,16 @@
// TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment
// modules have been updated to support it.
if input.canPerformHiddenAPIProcessing(ctx, b.properties) {
- // Get the content modules that contribute to the hidden API processing.
- hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents)
-
// Delegate the production of the hidden API all-flags.csv file to a module type specific method.
common := ctx.Module().(commonBootclasspathFragment)
- output = common.produceHiddenAPIAllFlagsFile(ctx, hiddenAPIModules, input)
+ output = common.produceHiddenAPIOutput(ctx, contents, input)
+ } else {
+ // As hidden API processing cannot be performed fall back to trying to retrieve the legacy
+ // encoded boot dex files, i.e. those files encoded by the individual libraries and returned
+ // from the DexJarBuildPath() method.
+ output = &HiddenAPIOutput{
+ EncodedBootDexFilesByModule: retrieveLegacyEncodedBootDexFiles(ctx, contents),
+ }
}
// Initialize a HiddenAPIInfo structure.
@@ -580,11 +597,9 @@
TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(),
}
- if output != nil {
- // The monolithic hidden API processing also needs access to all the output files produced by
- // hidden API processing of this fragment.
- hiddenAPIInfo.HiddenAPIFlagOutput = *output
- }
+ // The monolithic hidden API processing also needs access to all the output files produced by
+ // hidden API processing of this fragment.
+ hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput
// Provide it for use by other modules.
ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
@@ -592,10 +607,24 @@
return output
}
+// retrieveLegacyEncodedBootDexFiles attempts to retrieve the legacy encoded boot dex jar files.
+func retrieveLegacyEncodedBootDexFiles(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ // If the current bootclasspath_fragment is the active module or a source module then retrieve the
+ // encoded dex files, otherwise return an empty map.
+ //
+ // An inactive (i.e. not preferred) bootclasspath_fragment needs to retrieve the encoded dex jars
+ // as they are still needed by an apex. An inactive prebuilt_bootclasspath_fragment does not need
+ // to do so and may not yet have access to dex boot jars from a prebuilt_apex/apex_set.
+ if isActiveModule(ctx.Module()) || !android.IsModulePrebuilt(ctx.Module()) {
+ return extractEncodedDexJarsFromModules(ctx, contents)
+ } else {
+ return nil
+ }
+}
+
// createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived
// from the properties on this module and its dependencies.
func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput {
-
// Merge the HiddenAPIInfo from all the fragment dependencies.
dependencyHiddenApiInfo := newHiddenAPIInfo()
dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments)
@@ -615,12 +644,36 @@
return input
}
-// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files)
-// for the fragment.
-func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// produceHiddenAPIOutput produces the hidden API all-flags.csv file (and supporting files)
+// for the fragment as well as encoding the flags in the boot dex jars.
+func (b *BootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
// Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the
// paths to the created files.
- return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input)
+ return hiddenAPIRulesForBootclasspathFragment(ctx, contents, input)
+}
+
+// produceBootImageFiles builds the boot image files from the source if it is required.
+func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if SkipDexpreoptBootJars(ctx) {
+ return nil
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ // Only generate the boot image if the configuration does not skip it.
+ if !b.generateBootImageBuildActions(ctx, contents, imageConfig) {
+ return nil
+ }
+
+ // Only make the files available to an apex if they were actually generated.
+ files := bootImageFilesByArch{}
+ for _, variant := range imageConfig.apexVariants() {
+ files[variant.target.Arch.ArchType] = variant.imagesDeps.Paths()
+ }
+
+ return files
}
// generateBootImageBuildActions generates ninja rules to create the boot image if required for this
@@ -644,9 +697,6 @@
return false
}
- // Copy the dex jars of this fragment's content modules to their predefined locations.
- copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths)
-
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
buildBootImage(ctx, imageConfig, profile)
@@ -766,7 +816,7 @@
// Copy manually curated flag files specified on the bootclasspath_fragment.
if b.Flag_files_by_category != nil {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := b.Flag_files_by_category[category]
if len(paths) > 0 {
dests := []string{}
@@ -775,7 +825,7 @@
builder.CopyToSnapshot(p, dest)
dests = append(dests, dest)
}
- hiddenAPISet.AddProperty(category.propertyName, dests)
+ hiddenAPISet.AddProperty(category.PropertyName, dests)
}
}
}
@@ -841,9 +891,8 @@
return module.prebuilt.Name(module.ModuleBase.Name())
}
-// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is
-// specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
+func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
pathForOptionalSrc := func(src *string) android.Path {
if src == nil {
// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
@@ -852,19 +901,106 @@
return android.PathForModuleSrc(ctx, *src)
}
- output := HiddenAPIFlagOutput{
- StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
- AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
- MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
- IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
- AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+ // Retrieve the dex files directly from the content modules. They in turn should retrieve the
+ // encoded dex jars from the prebuilt .apex files.
+ encodedBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, contents)
+
+ output := HiddenAPIOutput{
+ HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+ StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
+ AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
+ MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
+ IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
+ AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+ },
+ EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
}
return &output
}
+// produceBootImageFiles extracts the boot image files from the APEX if available.
+func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module) bootImageFilesByArch {
+ if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
+ return nil
+ }
+
+ var deapexerModule android.Module
+ ctx.VisitDirectDeps(func(module android.Module) {
+ tag := ctx.OtherModuleDependencyTag(module)
+ // Save away the `deapexer` module on which this depends, if any.
+ if tag == android.DeapexerTag {
+ deapexerModule = module
+ }
+ })
+
+ if deapexerModule == nil {
+ // This should never happen as a variant for a prebuilt_apex is only created if the
+ // deapexer module has been configured to export the dex implementation jar for this module.
+ ctx.ModuleErrorf("internal error: module does not depend on a `deapexer` module")
+ return nil
+ }
+
+ di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, toPath := range variant.imagesDeps {
+ apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+ // Get the path to the file that the deapexer extracted from the prebuilt apex file.
+ fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+ // Copy the file to the predefined location.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.Cp,
+ Input: fromPath,
+ Output: toPath,
+ })
+ }
+ }
+
+ // The returned files will be made available to APEXes that include a bootclasspath_fragment.
+ // However, as a prebuilt_bootclasspath_fragment can never contribute to an APEX there is no point
+ // in returning any files.
+ return nil
+}
+
var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
+// files that a module requires from the prebuilt .apex file.
+func createBootImageTag(arch android.ArchType, baseName string) string {
+ tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
+ return tag
+}
+
+// RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
+// requires from a prebuilt .apex file.
+//
+// If there is no image config associated with this fragment then it returns nil. Otherwise, it
+// returns the files that are listed in the image config.
+func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+ imageConfig := module.getImageConfig(ctx)
+ if imageConfig != nil {
+ // Add the boot image files, e.g. .art, .oat and .vdex files.
+ files := []string{}
+ for _, variant := range imageConfig.apexVariants() {
+ arch := variant.target.Arch.ArchType
+ for _, path := range variant.imagesDeps.Paths() {
+ base := path.Base()
+ files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+ }
+ }
+ return files
+ }
+ return nil
+}
+
+func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string {
+ return filepath.Join("javalib", arch.String(), base)
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+
func prebuiltBootclasspathFragmentFactory() android.Module {
m := &prebuiltBootclasspathFragmentModule{}
m.AddProperties(&m.properties, &m.prebuiltProperties)
diff --git a/java/builder.go b/java/builder.go
index cde8731..ea011b8 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -279,23 +279,6 @@
transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
}
-func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
-
- flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
-
- if len(flags.errorProneExtraJavacFlags) > 0 {
- if len(flags.javacFlags) > 0 {
- flags.javacFlags += " " + flags.errorProneExtraJavacFlags
- } else {
- flags.javacFlags = flags.errorProneExtraJavacFlags
- }
- }
-
- transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
- "errorprone", "errorprone")
-}
-
// Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
// to compile with given set of builder flags, etc.
func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
diff --git a/java/classpath_element.go b/java/classpath_element.go
new file mode 100644
index 0000000..753e7f8
--- /dev/null
+++ b/java/classpath_element.go
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java
+
+import (
+ "fmt"
+ "strings"
+
+ "android/soong/android"
+ "github.com/google/blueprint"
+)
+
+// Supports constructing a list of ClasspathElement from a set of fragments and modules.
+
+// ClasspathElement represents a component that contributes to a classpath. That can be
+// either a java module or a classpath fragment module.
+type ClasspathElement interface {
+ Module() android.Module
+ String() string
+}
+
+type ClasspathElements []ClasspathElement
+
+// ClasspathFragmentElement is a ClasspathElement that encapsulates a classpath fragment module.
+type ClasspathFragmentElement struct {
+ Fragment android.Module
+ Contents []android.Module
+}
+
+func (b *ClasspathFragmentElement) Module() android.Module {
+ return b.Fragment
+}
+
+func (b *ClasspathFragmentElement) String() string {
+ contents := []string{}
+ for _, module := range b.Contents {
+ contents = append(contents, module.String())
+ }
+ return fmt.Sprintf("fragment(%s, %s)", b.Fragment, strings.Join(contents, ", "))
+}
+
+var _ ClasspathElement = (*ClasspathFragmentElement)(nil)
+
+// ClasspathLibraryElement is a ClasspathElement that encapsulates a java library.
+type ClasspathLibraryElement struct {
+ Library android.Module
+}
+
+func (b *ClasspathLibraryElement) Module() android.Module {
+ return b.Library
+}
+
+func (b *ClasspathLibraryElement) String() string {
+ return fmt.Sprintf("library{%s}", b.Library)
+}
+
+var _ ClasspathElement = (*ClasspathLibraryElement)(nil)
+
+// ClasspathElementContext defines the context methods needed by CreateClasspathElements
+type ClasspathElementContext interface {
+ OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool
+ OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{}
+ ModuleErrorf(fmt string, args ...interface{})
+}
+
+// CreateClasspathElements creates a list of ClasspathElement objects from a list of libraries and
+// a list of fragments.
+//
+// The libraries parameter contains the set of libraries from which the classpath is constructed.
+// The fragments parameter contains the classpath fragment modules whose contents are libraries that
+// are part of the classpath. Each library in the libraries parameter may be part of a fragment. The
+// determination as to which libraries belong to fragments and which do not is based on the apex to
+// which they belong, if any.
+//
+// Every fragment in the fragments list must be part of one or more apexes and each apex is assumed
+// to contain only a single fragment from the fragments list. A library in the libraries parameter
+// that is part of an apex must be provided by a classpath fragment in the corresponding apex.
+//
+// This will return a ClasspathElements list that contains a ClasspathElement for each standalone
+// library and each fragment. The order of the elements in the list is such that if the list was
+// flattened into a list of library modules that it would result in the same list or modules as the
+// input libraries. Flattening the list can be done by replacing each ClasspathFragmentElement in
+// the list with its Contents field.
+//
+// Requirements/Assumptions:
+// * A fragment can be associated with more than one apex but each apex must only be associated with
+// a single fragment from the fragments list.
+// * All of a fragment's contents must appear as a contiguous block in the same order in the
+// libraries list.
+// * Each library must only appear in a single fragment.
+//
+// The apex is used to identify which libraries belong to which fragment. First a mapping is created
+// from apex to fragment. Then the libraries are iterated over and any library in an apex is
+// associated with an element for the fragment to which it belongs. Otherwise, the libraries are
+// standalone and have their own element.
+//
+// e.g. Given the following input:
+// libraries: com.android.art:core-oj, com.android.art:core-libart, framework, ext
+// fragments: com.android.art:art-bootclasspath-fragment
+//
+// Then this will return:
+// ClasspathFragmentElement(art-bootclasspath-fragment, [core-oj, core-libart]),
+// ClasspathLibraryElement(framework),
+// ClasspathLibraryElement(ext),
+func CreateClasspathElements(ctx ClasspathElementContext, libraries []android.Module, fragments []android.Module) ClasspathElements {
+ // Create a map from apex name to the fragment module. This makes it easy to find the fragment
+ // associated with a particular apex.
+ apexToFragment := map[string]android.Module{}
+ for _, fragment := range fragments {
+ if !ctx.OtherModuleHasProvider(fragment, android.ApexInfoProvider) {
+ ctx.ModuleErrorf("fragment %s is not part of an apex", fragment)
+ continue
+ }
+
+ apexInfo := ctx.OtherModuleProvider(fragment, android.ApexInfoProvider).(android.ApexInfo)
+ for _, apex := range apexInfo.InApexVariants {
+ if existing, ok := apexToFragment[apex]; ok {
+ ctx.ModuleErrorf("apex %s has multiple fragments, %s and %s", apex, fragment, existing)
+ continue
+ }
+ apexToFragment[apex] = fragment
+ }
+ }
+
+ fragmentToElement := map[android.Module]*ClasspathFragmentElement{}
+ elements := []ClasspathElement{}
+ var currentElement ClasspathElement
+
+skipLibrary:
+ // Iterate over the libraries to construct the ClasspathElements list.
+ for _, library := range libraries {
+ var element ClasspathElement
+ if ctx.OtherModuleHasProvider(library, android.ApexInfoProvider) {
+ apexInfo := ctx.OtherModuleProvider(library, android.ApexInfoProvider).(android.ApexInfo)
+
+ var fragment android.Module
+
+ // Make sure that the library is in only one fragment of the classpath.
+ for _, apex := range apexInfo.InApexVariants {
+ if f, ok := apexToFragment[apex]; ok {
+ if fragment == nil {
+ // This is the first fragment so just save it away.
+ fragment = f
+ } else if f != fragment {
+ // This apex variant of the library is in a different fragment.
+ ctx.ModuleErrorf("library %s is in two separate fragments, %s and %s", library, fragment, f)
+ // Skip over this library entirely as otherwise the resulting classpath elements would
+ // be invalid.
+ continue skipLibrary
+ }
+ } else {
+ // There is no fragment associated with the library's apex.
+ }
+ }
+
+ if fragment == nil {
+ ctx.ModuleErrorf("library %s is from apexes %s which have no corresponding fragment in %s",
+ library, apexInfo.InApexVariants, fragments)
+ // Skip over this library entirely as otherwise the resulting classpath elements would
+ // be invalid.
+ continue skipLibrary
+ } else if existingFragmentElement, ok := fragmentToElement[fragment]; ok {
+ // This library is in a fragment element that has already been added.
+
+ // If the existing fragment element is still the current element then this library is
+ // contiguous with other libraries in that fragment so there is nothing more to do.
+ // Otherwise this library is not contiguous with other libraries in the same fragment which
+ // is an error.
+ if existingFragmentElement != currentElement {
+ separator := ""
+ if fragmentElement, ok := currentElement.(*ClasspathFragmentElement); ok {
+ separator = fmt.Sprintf("libraries from fragment %s like %s", fragmentElement.Fragment, fragmentElement.Contents[0])
+ } else {
+ libraryElement := currentElement.(*ClasspathLibraryElement)
+ separator = fmt.Sprintf("library %s", libraryElement.Library)
+ }
+
+ // Get the library that precedes this library in the fragment. That is the last library as
+ // this library has not yet been added.
+ precedingLibraryInFragment := existingFragmentElement.Contents[len(existingFragmentElement.Contents)-1]
+ ctx.ModuleErrorf("libraries from the same fragment must be contiguous, however %s and %s from fragment %s are separated by %s",
+ precedingLibraryInFragment, library, fragment, separator)
+ }
+
+ // Add this library to the fragment element's contents.
+ existingFragmentElement.Contents = append(existingFragmentElement.Contents, library)
+ } else {
+ // This is the first library in this fragment so add a new element for the fragment,
+ // including the library.
+ fragmentElement := &ClasspathFragmentElement{
+ Fragment: fragment,
+ Contents: []android.Module{library},
+ }
+
+ // Store it away so we can detect when attempting to create another element for the same
+ // fragment.
+ fragmentToElement[fragment] = fragmentElement
+ element = fragmentElement
+ }
+ } else {
+ // The library is from the platform so just add an element for it.
+ element = &ClasspathLibraryElement{Library: library}
+ }
+
+ // If no element was created then it means that the library has been added to an existing
+ // fragment element so the list of elements and current element are unaffected.
+ if element != nil {
+ // Add the element to the list and make it the current element for the next iteration.
+ elements = append(elements, element)
+ currentElement = element
+ }
+ }
+
+ return elements
+}
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 0e14d24..3d72580 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -19,6 +19,7 @@
import (
"fmt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"strings"
"android/soong/android"
@@ -44,6 +45,11 @@
}
type classpathFragmentProperties struct {
+ // Whether to generated classpaths.proto config instance for the fragment. If the config is not
+ // generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
+ // or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
+ // them as part of dexopt on device. Defaults to true.
+ Generate_classpaths_proto *bool
}
// classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
@@ -52,10 +58,6 @@
android.Module
classpathFragmentBase() *ClasspathFragmentBase
-
- // ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all
- // the jars in this classpath fragment.
- ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList
}
// ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
@@ -105,24 +107,28 @@
}
func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
- outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
- c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
- c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+ generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
+ if generateProto {
+ outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
+ c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+ c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
- generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
- writeClasspathsJson(ctx, generatedJson, jars)
+ generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+ writeClasspathsJson(ctx, generatedJson, jars)
- rule := android.NewRuleBuilder(pctx, ctx)
- rule.Command().
- BuiltTool("conv_classpaths_proto").
- Flag("encode").
- Flag("--format=json").
- FlagWithInput("--input=", generatedJson).
- FlagWithOutput("--output=", c.outputFilepath)
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().
+ BuiltTool("conv_classpaths_proto").
+ Flag("encode").
+ Flag("--format=json").
+ FlagWithInput("--input=", generatedJson).
+ FlagWithOutput("--output=", c.outputFilepath)
- rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+ }
classpathProtoInfo := ClasspathFragmentProtoContentInfo{
+ ClasspathFragmentProtoGenerated: generateProto,
ClasspathFragmentProtoInstallDir: c.installDirPath,
ClasspathFragmentProtoOutput: c.outputFilepath,
}
@@ -168,6 +174,9 @@
var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
type ClasspathFragmentProtoContentInfo struct {
+ // Whether the classpaths.proto config is generated for the fragment.
+ ClasspathFragmentProtoGenerated bool
+
// ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
//
// The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index e1a3650..bb85784 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -15,7 +15,6 @@
package java
import (
- "fmt"
"path/filepath"
"sort"
"strings"
@@ -254,6 +253,9 @@
dexPaths android.WritablePaths // for this image
dexPathsDeps android.WritablePaths // for the dependency images and in this image
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
// File path to a zip archive with all image files (or nil, if not needed).
zip android.WritablePath
@@ -276,13 +278,24 @@
dexLocationsDeps []string // for the dependency images and in this image
// Paths to image files.
- imagePathOnHost android.OutputPath // first image file path on host
- imagePathOnDevice string // first image file path on device
- imagesDeps android.OutputPaths // all files
+ imagePathOnHost android.OutputPath // first image file path on host
+ imagePathOnDevice string // first image file path on device
- // Only for extensions, paths to the primary boot images.
+ // All the files that constitute this image variant, i.e. .art, .oat and .vdex files.
+ imagesDeps android.OutputPaths
+
+ // The path to the primary image variant's imagePathOnHost field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
primaryImages android.OutputPath
+ // The paths to the primary image variant's imagesDeps field, where primary image variant
+ // means the image variant that this extends.
+ //
+ // This is only set for a variant of an image that extends another image.
+ primaryImagesDeps android.Paths
+
// Rules which should be used in make to install the outputs.
installs android.RuleBuilderInstalls
vdexInstalls android.RuleBuilderInstalls
@@ -345,6 +358,19 @@
return ret
}
+// apexVariants returns a list of all *bootImageVariant that could be included in an apex.
+func (image *bootImageConfig) apexVariants() []*bootImageVariant {
+ variants := []*bootImageVariant{}
+ for _, variant := range image.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ variants = append(variants, variant)
+ }
+ }
+ return variants
+}
+
// Return boot image locations (as a list of symbolic paths).
//
// The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -450,59 +476,33 @@
return true
}
-// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to
-// predefined paths in the global config.
-func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) {
- jarPaths := make(android.Paths, bootjars.Len())
- for i, module := range bootModules {
- if module != nil {
- bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
- jarPaths[i] = bootDexJar
+// copyBootJarsToPredefinedLocations generates commands that will copy boot jars to predefined
+// paths in the global config.
+func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, srcBootDexJarsByModule bootDexJarByModule, dstBootJarsByModule map[string]android.WritablePath) {
+ // Create the super set of module names.
+ names := []string{}
+ names = append(names, android.SortedStringKeys(srcBootDexJarsByModule)...)
+ names = append(names, android.SortedStringKeys(dstBootJarsByModule)...)
+ names = android.SortedUniqueStrings(names)
+ for _, name := range names {
+ src := srcBootDexJarsByModule[name]
+ dst := dstBootJarsByModule[name]
- name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module))
- if bootjars.Jar(i) != name {
- ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name)
- }
- }
- }
-
- // 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 jarPaths {
- input := jarPaths[i]
- output := jarPathsPredefined[i]
- module := bootjars.Jar(i)
- if input == nil {
- if ctx.Config().AllowMissingDependencies() {
- apex := bootjars.Apex(i)
-
- // Create an error rule that pretends to create the output file but will actually fail if it
- // is run.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.ErrorRule,
- Output: output,
- Args: map[string]string{
- "error": fmt.Sprintf("missing dependencies: dex jar for %s:%s", module, apex),
- },
- })
- } else {
- ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+
- ", note that some jars may be filtered out by module constraints", module)
- }
-
+ if src == nil {
+ ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+ } else if dst == nil {
+ ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
} else {
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
- Input: input,
- Output: output,
+ Input: src,
+ Output: dst,
})
}
}
}
-// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
+// buildBootImage takes a bootImageConfig, and creates rules to build it.
func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
var zipFiles android.Paths
for _, variant := range image.variants {
@@ -588,7 +588,15 @@
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)
+ // Add the path to the first file in the boot image with the arch specific directory removed,
+ // dex2oat will reconstruct the path to the actual file when it needs it. As the actual path
+ // to the file cannot be passed to the command make sure to add the actual path as an Implicit
+ // dependency to ensure that it is built before the command runs.
+ FlagWithArg("--boot-image=", dexpreopt.PathToLocation(artImage, arch)).Implicit(artImage).
+ // Similarly, the dex2oat tool will automatically find the paths to other files in the base
+ // boot image so make sure to add them as implicit dependencies to ensure that they are built
+ // before this command is run.
+ Implicits(image.primaryImagesDeps)
} else {
// It is a primary image, so it needs a base address.
cmd.FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress())
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 39a3e11..b13955f 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -100,6 +100,7 @@
// TODO(b/143682396): use module dependencies instead
inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
c.dexPaths = c.modules.BuildPaths(ctx, inputDir)
+ c.dexPathsByModule = c.modules.BuildPathsByModule(ctx, inputDir)
c.dexPathsDeps = c.dexPaths
// Create target-specific variants.
@@ -125,6 +126,7 @@
frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
for i := range targets {
frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
+ frameworkCfg.variants[i].primaryImagesDeps = artCfg.variants[i].imagesDeps.Paths()
frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
}
@@ -152,6 +154,9 @@
// later on a singleton adds commands to copy actual jars to the predefined paths.
dexPaths android.WritablePaths
+ // Map from module name (without prebuilt_ prefix) to the predefined build path.
+ dexPathsByModule map[string]android.WritablePath
+
// A list of dex locations (a.k.a. on-device paths) to the boot jars.
dexLocations []string
}
@@ -165,10 +170,11 @@
dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
dexPaths := updatableBootJars.BuildPaths(ctx, dir)
+ dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir)
dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
- return updatableBootConfig{updatableBootJars, dexPaths, dexLocations}
+ return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations}
}).(updatableBootConfig)
}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 56e6247..869a598 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -425,23 +425,6 @@
}
srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
- // While metalava needs package html files, it does not need them to be explicit on the command
- // line. javadoc complains if it receives html files on the command line. The filter
- // below excludes html files from the rsp file metalava. Note that the html
- // files are still included as implicit inputs for successful remote execution and correct
- // incremental builds.
- filterHtml := func(srcs []android.Path) []android.Path {
- filtered := []android.Path{}
- for _, src := range srcs {
- if src.Ext() == ".html" {
- continue
- }
- filtered = append(filtered, src)
- }
- return filtered
- }
- srcFiles = filterHtml(srcFiles)
-
aidlFlags := j.collectAidlFlags(ctx, deps)
srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 17c7a7b..d348b55 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -392,8 +392,7 @@
}
func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
- srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths,
- homeDir android.WritablePath) *android.RuleBuilderCommand {
+ srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
rule.Command().Text("rm -rf").Flag(homeDir.String())
rule.Command().Text("mkdir -p").Flag(homeDir.String())
@@ -430,12 +429,6 @@
cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
}
- if len(sourcepaths) > 0 {
- cmd.FlagWithList("-sourcepath ", sourcepaths.Strings(), ":")
- } else {
- cmd.FlagWithArg("-sourcepath ", `""`)
- }
-
cmd.Flag("--no-banner").
Flag("--color").
Flag("--quiet").
@@ -479,7 +472,7 @@
homeDir := android.PathForModuleOut(ctx, "metalava", "home")
cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
- deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, homeDir)
+ deps.bootClasspath, deps.classpath, homeDir)
cmd.Implicits(d.Javadoc.implicits)
d.stubsFlags(ctx, cmd, stubsDir)
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index e9693c6..f901434 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -219,7 +219,6 @@
BuiltTool("merge_csv").
Flag("--zip_input").
Flag("--key_field signature").
- FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties").
FlagWithOutput("--output=", indexCSV).
Inputs(classesJars)
rule.Build(desc, desc)
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index f2649d3..de72b39 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -248,8 +248,8 @@
}
type hiddenAPIFlagFileCategory struct {
- // propertyName is the name of the property for this category.
- propertyName string
+ // PropertyName is the name of the property for this category.
+ PropertyName string
// propertyValueReader retrieves the value of the property for this category from the set of
// properties.
@@ -262,12 +262,12 @@
// The flag file category for removed members of the API.
//
-// This is extracted from hiddenAPIFlagFileCategories as it is needed to add the dex signatures
+// This is extracted from HiddenAPIFlagFileCategories as it is needed to add the dex signatures
// list of removed API members that are generated automatically from the removed.txt files provided
// by API stubs.
var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{
// See HiddenAPIFlagFileProperties.Removed
- propertyName: "removed",
+ PropertyName: "removed",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Removed
},
@@ -276,10 +276,10 @@
},
}
-var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
+var HiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{
// See HiddenAPIFlagFileProperties.Unsupported
{
- propertyName: "unsupported",
+ PropertyName: "unsupported",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Unsupported
},
@@ -290,7 +290,7 @@
hiddenAPIRemovedFlagFileCategory,
// See HiddenAPIFlagFileProperties.Max_target_r_low_priority
{
- propertyName: "max_target_r_low_priority",
+ PropertyName: "max_target_r_low_priority",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_r_low_priority
},
@@ -300,7 +300,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_q
{
- propertyName: "max_target_q",
+ PropertyName: "max_target_q",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_q
},
@@ -310,7 +310,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_p
{
- propertyName: "max_target_p",
+ PropertyName: "max_target_p",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_p
},
@@ -320,7 +320,7 @@
},
// See HiddenAPIFlagFileProperties.Max_target_o_low_priority
{
- propertyName: "max_target_o_low_priority",
+ PropertyName: "max_target_o_low_priority",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Max_target_o_low_priority
},
@@ -330,7 +330,7 @@
},
// See HiddenAPIFlagFileProperties.Blocked
{
- propertyName: "blocked",
+ PropertyName: "blocked",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Blocked
},
@@ -340,7 +340,7 @@
},
// See HiddenAPIFlagFileProperties.Unsupported_packages
{
- propertyName: "unsupported_packages",
+ PropertyName: "unsupported_packages",
propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string {
return properties.Unsupported_packages
},
@@ -355,7 +355,7 @@
// append appends the supplied flags files to the corresponding category in this map.
func (s FlagFilesByCategory) append(other FlagFilesByCategory) {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
s[category] = append(s[category], other[category]...)
}
}
@@ -540,7 +540,7 @@
// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the
// supplied properties and stores them in this struct.
func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) {
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p))
i.FlagFilesByCategory[category] = paths
}
@@ -571,6 +571,45 @@
AllFlagsPath android.Path
}
+// bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
+// path.
+type bootDexJarByModule map[string]android.Path
+
+// addPath adds the path for a module to the map.
+func (b bootDexJarByModule) addPath(module android.Module, path android.Path) {
+ b[android.RemoveOptionalPrebuiltPrefix(module.Name())] = path
+}
+
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexJarByModule) bootDexJars() android.Paths {
+ paths := android.Paths{}
+ for _, k := range android.SortedStringKeys(b) {
+ paths = append(paths, b[k])
+ }
+ return paths
+}
+
+// bootDexJarsWithoutCoverage returns the boot dex jar paths sorted by their keys without coverage
+// libraries if present.
+func (b bootDexJarByModule) bootDexJarsWithoutCoverage() android.Paths {
+ paths := android.Paths{}
+ for _, k := range android.SortedStringKeys(b) {
+ if k == "jacocoagent" {
+ continue
+ }
+ paths = append(paths, b[k])
+ }
+ return paths
+}
+
+// HiddenAPIOutput encapsulates the output from the hidden API processing.
+type HiddenAPIOutput struct {
+ HiddenAPIFlagOutput
+
+ // The map from base module name to the path to the encoded boot dex file.
+ EncodedBootDexFilesByModule bootDexJarByModule
+}
+
// pathForValidation creates a path of the same type as the supplied type but with a name of
// <path>.valid.
//
@@ -630,7 +669,7 @@
FlagWithOutput("--output ", tempPath)
// Add the options for the different categories of flag files.
- for _, category := range hiddenAPIFlagFileCategories {
+ for _, category := range HiddenAPIFlagFileCategories {
paths := flagFilesByCategory[category]
for _, path := range paths {
category.commandMutator(command, path)
@@ -656,8 +695,8 @@
rule.Build(name, desc)
}
-// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment
-// of the bootclasspath.
+// hiddenAPIRulesForBootclasspathFragment will generate all the flags for a fragment of the
+// bootclasspath and then encode the flags into the boot dex files.
//
// It takes:
// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind.
@@ -670,19 +709,20 @@
// * metadata.csv
// * index.csv
// * all-flags.csv
-func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput {
+// * encoded boot dex files
+func hiddenAPIRulesForBootclasspathFragment(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
hiddenApiSubDir := "modular-hiddenapi"
- // Gather the dex files for the boot libraries provided by this fragment.
- bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, contents)
+ // Gather information about the boot dex files for the boot libraries provided by this fragment.
+ bootDexInfoByModule := extractBootDexInfoFromModules(ctx, contents)
// Generate the stub-flags.csv.
stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv")
- rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input)
+ rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexInfoByModule.bootDexJars(), input)
rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags")
// Extract the classes jars from the contents.
- classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents)
+ classesJars := extractClassesJarsFromModules(contents)
// Generate the set of flags from the annotations in the source code.
annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv")
@@ -706,16 +746,29 @@
// Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex
// files.
- outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
- buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures)
+ allFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv")
+ buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", allFlagsCSV, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures)
+
+ // Encode the flags into the boot dex files.
+ encodedBootDexJarsByModule := map[string]android.Path{}
+ outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath
+ for _, name := range android.SortedStringKeys(bootDexInfoByModule) {
+ bootDexInfo := bootDexInfoByModule[name]
+ unencodedDex := bootDexInfo.path
+ encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, allFlagsCSV, bootDexInfo.uncompressDex, outputDir)
+ encodedBootDexJarsByModule[name] = encodedDex
+ }
// Store the paths in the info for use by other modules and sdk snapshot generation.
- output := HiddenAPIFlagOutput{
- StubFlagsPath: stubFlagsCSV,
- AnnotationFlagsPath: annotationFlagsCSV,
- MetadataPath: metadataCSV,
- IndexPath: indexCSV,
- AllFlagsPath: outputPath,
+ output := HiddenAPIOutput{
+ HiddenAPIFlagOutput: HiddenAPIFlagOutput{
+ StubFlagsPath: stubFlagsCSV,
+ AnnotationFlagsPath: annotationFlagsCSV,
+ MetadataPath: metadataCSV,
+ IndexPath: indexCSV,
+ AllFlagsPath: allFlagsCSV,
+ },
+ EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
}
return &output
}
@@ -737,58 +790,215 @@
return android.OptionalPathForPath(output)
}
-// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents.
-func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule {
- hiddenAPIModules := []hiddenAPIModule{}
+// extractBootDexJarsFromModules extracts the boot dex jars from the supplied modules.
+func extractBootDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ bootDexJars := bootDexJarByModule{}
for _, module := range contents {
- if hiddenAPI, ok := module.(hiddenAPIModule); ok {
- hiddenAPIModules = append(hiddenAPIModules, hiddenAPI)
- } else if _, ok := module.(*DexImport); ok {
- // Ignore this for the purposes of hidden API processing
- } else {
- ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
+ hiddenAPIModule := hiddenAPIModuleFromModule(ctx, module)
+ if hiddenAPIModule == nil {
+ continue
}
- }
- return hiddenAPIModules
-}
-
-// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules.
-func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
- bootDexJars := android.Paths{}
- for _, module := range contents {
- bootDexJar := module.bootDexJar()
- if bootDexJar == nil {
- if ctx.Config().AlwaysUsePrebuiltSdks() {
- // TODO(b/179354495): Remove this work around when it is unnecessary.
- // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
- // create a fake one that will cause a build error only if it is used.
- fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name())
-
- // Create an error rule that pretends to create the output file but will actually fail if it
- // is run.
- ctx.Build(pctx, android.BuildParams{
- Rule: android.ErrorRule,
- Output: fake,
- Args: map[string]string{
- "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
- },
- })
- bootDexJars = append(bootDexJars, fake)
- } else {
- ctx.ModuleErrorf("module %s does not provide a dex jar", module)
- }
- } else {
- bootDexJars = append(bootDexJars, bootDexJar)
- }
+ bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+ bootDexJars.addPath(module, bootDexJar)
}
return bootDexJars
}
-// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules.
-func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths {
+func hiddenAPIModuleFromModule(ctx android.BaseModuleContext, module android.Module) hiddenAPIModule {
+ if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+ return hiddenAPIModule
+ } else if _, ok := module.(*DexImport); ok {
+ // Ignore this for the purposes of hidden API processing
+ } else {
+ ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module)
+ }
+
+ return nil
+}
+
+// bootDexInfo encapsulates both the path and uncompressDex status retrieved from a hiddenAPIModule.
+type bootDexInfo struct {
+ // The path to the dex jar that has not had hidden API flags encoded into it.
+ path android.Path
+
+ // Indicates whether the dex jar needs uncompressing before encoding.
+ uncompressDex bool
+}
+
+// bootDexInfoByModule is a map from module name (as returned by module.Name()) to the boot dex
+// path (as returned by hiddenAPIModule.bootDexJar()) and the uncompressDex flag.
+type bootDexInfoByModule map[string]bootDexInfo
+
+// bootDexJars returns the boot dex jar paths sorted by their keys.
+func (b bootDexInfoByModule) bootDexJars() android.Paths {
+ paths := android.Paths{}
+ for _, m := range android.SortedStringKeys(b) {
+ paths = append(paths, b[m].path)
+ }
+ return paths
+}
+
+// extractBootDexInfoFromModules extracts the boot dex jar and uncompress dex state from
+// each of the supplied modules which must implement hiddenAPIModule.
+func extractBootDexInfoFromModules(ctx android.ModuleContext, contents []android.Module) bootDexInfoByModule {
+ bootDexJarsByModule := bootDexInfoByModule{}
+ for _, module := range contents {
+ hiddenAPIModule := module.(hiddenAPIModule)
+ bootDexJar := retrieveBootDexJarFromHiddenAPIModule(ctx, hiddenAPIModule)
+ bootDexJarsByModule[module.Name()] = bootDexInfo{
+ path: bootDexJar,
+ uncompressDex: *hiddenAPIModule.uncompressDex(),
+ }
+ }
+
+ return bootDexJarsByModule
+}
+
+// retrieveBootDexJarFromHiddenAPIModule retrieves the boot dex jar from the hiddenAPIModule.
+//
+// If the module does not provide a boot dex jar, i.e. the returned boot dex jar is nil, then that
+// create a fake path and either report an error immediately or defer reporting of the error until
+// the path is actually used.
+func retrieveBootDexJarFromHiddenAPIModule(ctx android.ModuleContext, module hiddenAPIModule) android.Path {
+ bootDexJar := module.bootDexJar()
+ if bootDexJar == nil {
+ fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/boot-dex/%s.jar", module.Name()))
+ bootDexJar = fake
+
+ handleMissingDexBootFile(ctx, module, fake)
+ }
+ return bootDexJar
+}
+
+// extractClassesJarsFromModules extracts the class jars from the supplied modules.
+func extractClassesJarsFromModules(contents []android.Module) android.Paths {
classesJars := android.Paths{}
for _, module := range contents {
- classesJars = append(classesJars, module.classesJars()...)
+ classesJars = append(classesJars, retrieveClassesJarsFromModule(module)...)
}
return classesJars
}
+
+// retrieveClassesJarsFromModule retrieves the classes jars from the supplied module.
+func retrieveClassesJarsFromModule(module android.Module) android.Paths {
+ if hiddenAPIModule, ok := module.(hiddenAPIModule); ok {
+ return hiddenAPIModule.classesJars()
+ }
+
+ return nil
+}
+
+// deferReportingMissingBootDexJar returns true if a missing boot dex jar should not be reported by
+// Soong but should instead only be reported in ninja if the file is actually built.
+func deferReportingMissingBootDexJar(ctx android.ModuleContext, module android.Module) bool {
+ // TODO(b/179354495): Remove this workaround when it is unnecessary.
+ // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So,
+ // create a fake one that will cause a build error only if it is used.
+ if ctx.Config().AlwaysUsePrebuiltSdks() {
+ return true
+ }
+
+ // Any missing dependency should be allowed.
+ if ctx.Config().AllowMissingDependencies() {
+ return true
+ }
+
+ // This is called for both platform_bootclasspath and bootclasspath_fragment modules.
+ //
+ // A bootclasspath_fragment module should only use the APEX variant of source or prebuilt modules.
+ // Ideally, a bootclasspath_fragment module should never have a platform variant created for it
+ // but unfortunately, due to b/187910671 it does.
+ //
+ // That causes issues when obtaining a boot dex jar for a prebuilt module as a prebuilt module
+ // used by a bootclasspath_fragment can only provide a boot dex jar when it is part of APEX, i.e.
+ // has an APEX variant not a platform variant.
+ //
+ // There are some other situations when a prebuilt module used by a bootclasspath_fragment cannot
+ // provide a boot dex jar:
+ // 1. If the bootclasspath_fragment is not exported by the prebuilt_apex/apex_set module then it
+ // does not have an APEX variant and only has a platform variant and neither do its content
+ // modules.
+ // 2. Some build configurations, e.g. setting TARGET_BUILD_USE_PREBUILT_SDKS causes all
+ // java_sdk_library_import modules to be treated as preferred and as many of them are not part
+ // of an apex they cannot provide a boot dex jar.
+ //
+ // The first case causes problems when the affected prebuilt modules are preferred but that is an
+ // invalid configuration and it is ok for it to fail as the work to enable that is not yet
+ // complete. The second case is used for building targets that do not use boot dex jars and so
+ // deferring error reporting to ninja is fine as the affected ninja targets should never be built.
+ // That is handled above.
+ //
+ // A platform_bootclasspath module can use libraries from both platform and APEX variants. Unlike
+ // the bootclasspath_fragment it supports dex_import modules which provides the dex file. So, it
+ // can obtain a boot dex jar from a prebuilt that is not part of an APEX. However, it is assumed
+ // that if the library can be part of an APEX then it is the APEX variant that is used.
+ //
+ // This check handles the slightly different requirements of the bootclasspath_fragment and
+ // platform_bootclasspath modules by only deferring error reporting for the platform variant of
+ // a prebuilt modules that has other variants which are part of an APEX.
+ //
+ // TODO(b/187910671): Remove this once platform variants are no longer created unnecessarily.
+ if android.IsModulePrebuilt(module) {
+ if am, ok := module.(android.ApexModule); ok && am.InAnyApex() {
+ apexInfo := ctx.OtherModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+ if apexInfo.IsForPlatform() {
+ return true
+ }
+ }
+ }
+
+ // A bootclasspath module that is part of a versioned sdk never provides a boot dex jar as there
+ // is no equivalently versioned prebuilt APEX file from which it can be obtained. However,
+ // versioned bootclasspath modules are processed by Soong so in order to avoid them causing build
+ // failures missing boot dex jars need to be deferred.
+ if android.IsModuleInVersionedSdk(ctx.Module()) {
+ return true
+ }
+
+ return false
+}
+
+// handleMissingDexBootFile will either log a warning or create an error rule to create the fake
+// file depending on the value returned from deferReportingMissingBootDexJar.
+func handleMissingDexBootFile(ctx android.ModuleContext, module android.Module, fake android.WritablePath) {
+ if deferReportingMissingBootDexJar(ctx, module) {
+ // Create an error rule that pretends to create the output file but will actually fail if it
+ // is run.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: android.ErrorRule,
+ Output: fake,
+ Args: map[string]string{
+ "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module),
+ },
+ })
+ } else {
+ ctx.ModuleErrorf("module %s does not provide a dex jar", module)
+ }
+}
+
+// retrieveEncodedBootDexJarFromModule returns a path to the boot dex jar from the supplied module's
+// DexJarBuildPath() method.
+//
+// The returned path will usually be to a dex jar file that has been encoded with hidden API flags.
+// However, under certain conditions, e.g. errors, or special build configurations it will return
+// a path to a fake file.
+func retrieveEncodedBootDexJarFromModule(ctx android.ModuleContext, module android.Module) android.Path {
+ bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath()
+ if bootDexJar == nil {
+ fake := android.PathForModuleOut(ctx, fmt.Sprintf("fake/encoded-dex/%s.jar", module.Name()))
+ bootDexJar = fake
+
+ handleMissingDexBootFile(ctx, module, fake)
+ }
+ return bootDexJar
+}
+
+// extractEncodedDexJarsFromModules extracts the encoded dex jars from the supplied modules.
+func extractEncodedDexJarsFromModules(ctx android.ModuleContext, contents []android.Module) bootDexJarByModule {
+ encodedDexJarsByModuleName := bootDexJarByModule{}
+ for _, module := range contents {
+ path := retrieveEncodedBootDexJarFromModule(ctx, module)
+ encodedDexJarsByModuleName.addPath(module, path)
+ }
+ return encodedDexJarsByModuleName
+}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index a6bf8c7..edf4235 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -99,4 +99,4 @@
i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
}
-var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
+var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
diff --git a/java/java.go b/java/java.go
index 2bbb5b1..ae8adf2 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1309,7 +1309,7 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
j.dexJarFile = dexOutputPath
// Initialize the hiddenapi structure.
@@ -1426,6 +1426,29 @@
return nil
}
+// requiredFilesFromPrebuiltApexForImport returns information about the files that a java_import or
+// java_sdk_library_import with the specified base module name requires to be exported from a
+// prebuilt_apex/apex_set.
+func requiredFilesFromPrebuiltApexForImport(name string) []string {
+ // Add the dex implementation jar to the set of exported files.
+ return []string{
+ apexRootRelativePathToJavaLib(name),
+ }
+}
+
+// apexRootRelativePathToJavaLib returns the path, relative to the root of the apex's contents, for
+// the java library with the specified name.
+func apexRootRelativePathToJavaLib(name string) string {
+ return filepath.Join("javalib", name+".jar")
+}
+
+var _ android.RequiredFilesFromPrebuiltApex = (*Import)(nil)
+
+func (j *Import) RequiredFilesFromPrebuiltApex(_ android.BaseModuleContext) []string {
+ name := j.BaseModuleName()
+ return requiredFilesFromPrebuiltApexForImport(name)
+}
+
// Add compile time check for interface implementation
var _ android.IDEInfo = (*Import)(nil)
var _ android.IDECustomizedModuleName = (*Import)(nil)
diff --git a/java/java_test.go b/java/java_test.go
index bd373c1..0f9965d 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1392,3 +1392,95 @@
assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
module.properties.Installable)
}
+
+func TestErrorproneEnabled(t *testing.T) {
+ ctx, _ := testJava(t, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ errorprone: {
+ enabled: true,
+ },
+ }
+ `)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+ // Test that the errorprone plugins are passed to javac
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Modules with errorprone { enabled: true } will include errorprone checks
+ // in the main javac build rule. Only when RUN_ERROR_PRONE is true will
+ // the explicit errorprone build rule be created.
+ errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+ if errorprone.RuleParams.Description != "" {
+ t.Errorf("expected errorprone build rule to not exist, but it did")
+ }
+}
+
+func TestErrorproneDisabled(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ errorprone: {
+ enabled: false,
+ },
+ }
+ `
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(map[string]string{
+ "RUN_ERROR_PRONE": "true",
+ }),
+ ).RunTestWithBp(t, bp)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+ // Test that the errorprone plugins are not passed to javac, like they would
+ // be if enabled was true.
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Check that no errorprone build rule is created, like there would be
+ // if enabled was unset and RUN_ERROR_PRONE was true.
+ errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+ if errorprone.RuleParams.Description != "" {
+ t.Errorf("expected errorprone build rule to not exist, but it did")
+ }
+}
+
+func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+ bp := `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ }
+ `
+ ctx := android.GroupFixturePreparers(
+ PrepareForTestWithJavaDefaultModules,
+ android.FixtureMergeEnv(map[string]string{
+ "RUN_ERROR_PRONE": "true",
+ }),
+ ).RunTestWithBp(t, bp)
+
+ javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+ errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+
+ // Check that the errorprone plugins are not passed to javac, because they
+ // will instead be passed to the separate errorprone compilation
+ expectedSubstring := "-Xplugin:ErrorProne"
+ if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+
+ // Check that the errorprone plugin is enabled
+ if !strings.Contains(errorprone.Args["javacFlags"], expectedSubstring) {
+ t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+ }
+}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 1c146a1..fd2f3ca 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -185,7 +185,6 @@
buildOS := android.BuildOs.String()
kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
- //kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
javac := result.ModuleForTests("foo", "android_common").Description("javac")
errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 5949edd..8c401a7 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -20,6 +20,7 @@
)
var legacyCorePlatformApiModules = []string{
+ "ArcSettings",
"ahat-test-dump",
"android.car",
"android.test.mock",
@@ -42,6 +43,7 @@
"car-service-test-lib",
"car-service-test-static-lib",
"CertInstaller",
+ "com.qti.media.secureprocessor",
"ConnectivityManagerTest",
"ContactsProvider",
"CorePerfTests",
@@ -120,6 +122,7 @@
"services.usage",
"services.usb",
"Settings-core",
+ "SettingsGoogle",
"SettingsLib",
"SettingsProvider",
"SettingsProviderTest",
diff --git a/java/lint.go b/java/lint.go
index 9f769df..1511cfe 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -361,10 +361,7 @@
Labels: map[string]string{"type": "tool", "name": "lint"},
ExecStrategy: lintRBEExecStrategy(ctx),
ToolchainInputs: []string{config.JavaCmd(ctx).String()},
- EnvironmentVariables: []string{
- "LANG",
- },
- Platform: map[string]string{remoteexec.PoolKey: pool},
+ Platform: map[string]string{remoteexec.PoolKey: pool},
})
}
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 87c695c..02343ad 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -189,7 +189,8 @@
b.generateClasspathProtoBuildActions(ctx)
- b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+ bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
+ buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
// Nothing to do if skipping the dexpreopt of boot image jars.
if SkipDexpreoptBootJars(ctx) {
@@ -202,11 +203,11 @@
// Generate classpaths.proto config
func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
- classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+ classpathJars := configuredJarListToClasspathJars(ctx, b.configuredJars(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
}
-func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
return b.getImageConfig(ctx).modules
}
@@ -258,7 +259,7 @@
}
// generateHiddenAPIBuildActions generates all the hidden API related build rules.
-func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) {
+func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) bootDexJarByModule {
// Save the paths to the monolithic files for retrieval via OutputFiles().
b.hiddenAPIFlagsCSV = hiddenAPISingletonPaths(ctx).flags
@@ -276,11 +277,10 @@
Output: path,
})
}
- return
+ return nil
}
monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments)
-
// Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile
input := newHiddenAPIFlagInput()
@@ -291,16 +291,14 @@
// Use the flag files from this module and all the fragments.
input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory
- hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules)
-
// Generate the monolithic stub-flags.csv file.
- bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
+ bootDexJarByModule := extractBootDexJarsFromModules(ctx, modules)
stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
- rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input)
+ rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJarByModule.bootDexJars(), input)
rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags")
// Extract the classes jars from the contents.
- classesJars := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules)
+ classesJars := extractClassesJarsFromModules(modules)
// Generate the annotation-flags.csv file from all the module annotations.
annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv")
@@ -325,6 +323,8 @@
// jars.
indexCSV := hiddenAPISingletonPaths(ctx).index
buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV)
+
+ return bootDexJarByModule
}
// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for
@@ -342,7 +342,7 @@
monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments)
// Store the information for testing.
- ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo)
+ ctx.SetProvider(MonolithicHiddenAPIInfoProvider, monolithicInfo)
return monolithicInfo
}
@@ -390,11 +390,13 @@
generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
// Copy non-updatable module dex jars to their predefined locations.
- copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths)
+ nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules)
+ copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule)
// Copy updatable module dex jars to their predefined locations.
config := GetUpdatableBootConfig(ctx)
- copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths)
+ updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules)
+ copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule)
// Build a profile for the image config and then use that to build the boot image.
profile := bootImageProfileRule(ctx, imageConfig)
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index ed5549d..0318a07 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -15,8 +15,6 @@
package java
import (
- "fmt"
- "strings"
"testing"
"android/soong/android"
@@ -152,116 +150,6 @@
})
}
-func TestPlatformBootclasspath_Fragments(t *testing.T) {
- result := android.GroupFixturePreparers(
- prepareForTestWithPlatformBootclasspath,
- PrepareForTestWithJavaSdkLibraryFiles,
- FixtureWithLastReleaseApis("foo"),
- android.FixtureWithRootAndroidBp(`
- platform_bootclasspath {
- name: "platform-bootclasspath",
- fragments: [
- {module:"bar-fragment"},
- ],
- hidden_api: {
- unsupported: [
- "unsupported.txt",
- ],
- removed: [
- "removed.txt",
- ],
- max_target_r_low_priority: [
- "max-target-r-low-priority.txt",
- ],
- max_target_q: [
- "max-target-q.txt",
- ],
- max_target_p: [
- "max-target-p.txt",
- ],
- max_target_o_low_priority: [
- "max-target-o-low-priority.txt",
- ],
- blocked: [
- "blocked.txt",
- ],
- unsupported_packages: [
- "unsupported-packages.txt",
- ],
- },
- }
-
- bootclasspath_fragment {
- name: "bar-fragment",
- contents: ["bar"],
- api: {
- stub_libs: ["foo"],
- },
- hidden_api: {
- unsupported: [
- "bar-unsupported.txt",
- ],
- removed: [
- "bar-removed.txt",
- ],
- max_target_r_low_priority: [
- "bar-max-target-r-low-priority.txt",
- ],
- max_target_q: [
- "bar-max-target-q.txt",
- ],
- max_target_p: [
- "bar-max-target-p.txt",
- ],
- max_target_o_low_priority: [
- "bar-max-target-o-low-priority.txt",
- ],
- blocked: [
- "bar-blocked.txt",
- ],
- unsupported_packages: [
- "bar-unsupported-packages.txt",
- ],
- },
- }
-
- java_library {
- name: "bar",
- srcs: ["a.java"],
- system_modules: "none",
- sdk_version: "none",
- compile_dex: true,
- }
-
- java_sdk_library {
- name: "foo",
- srcs: ["a.java"],
- public: {
- enabled: true,
- },
- compile_dex: true,
- }
- `),
- ).RunTest(t)
-
- pbcp := result.Module("platform-bootclasspath", "android_common")
- info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo)
-
- for _, category := range hiddenAPIFlagFileCategories {
- name := category.propertyName
- message := fmt.Sprintf("category %s", name)
- filename := strings.ReplaceAll(name, "_", "-")
- expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)}
- android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
- }
-
- android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
- android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
- android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
- android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/index.csv"}, info.IndexPaths)
- android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
-}
-
func TestPlatformBootclasspathVariant(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForTestWithPlatformBootclasspath,
diff --git a/java/rro.go b/java/rro.go
index 2e58c04..0b4d091 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -90,6 +90,22 @@
Theme() string
}
+// RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go
+// The default partition for RRO is "/product" and not "/system"
+func rroPartition(ctx android.ModuleContext) string {
+ var partition string
+ if ctx.DeviceSpecific() {
+ partition = ctx.DeviceConfig().OdmPath()
+ } else if ctx.SocSpecific() {
+ partition = ctx.DeviceConfig().VendorPath()
+ } else if ctx.SystemExtSpecific() {
+ partition = ctx.DeviceConfig().SystemExtPath()
+ } else {
+ partition = ctx.DeviceConfig().ProductPath()
+ }
+ return partition
+}
+
func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
sdkDep := decodeSdkDep(ctx, android.SdkContext(r))
if sdkDep.hasFrameworkLibs() {
@@ -137,7 +153,8 @@
r.certificate = certificates[0]
r.outputFile = signed
- r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+ partition := rroPartition(ctx)
+ r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme))
ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
}
diff --git a/java/rro_test.go b/java/rro_test.go
index bad60bc..27abbe4 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -177,7 +177,7 @@
// Check device location.
path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
- expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
+ expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
}
@@ -343,3 +343,57 @@
})
}
}
+
+func TestRuntimeResourceOverlayPartition(t *testing.T) {
+ bp := `
+ runtime_resource_overlay {
+ name: "device_specific",
+ device_specific: true,
+ }
+ runtime_resource_overlay {
+ name: "soc_specific",
+ soc_specific: true,
+ }
+ runtime_resource_overlay {
+ name: "system_ext_specific",
+ system_ext_specific: true,
+ }
+ runtime_resource_overlay {
+ name: "product_specific",
+ product_specific: true,
+ }
+ runtime_resource_overlay {
+ name: "default"
+ }
+ `
+ testCases := []struct {
+ name string
+ expectedPath string
+ }{
+ {
+ name: "device_specific",
+ expectedPath: "out/soong/target/product/test_device/odm/overlay",
+ },
+ {
+ name: "soc_specific",
+ expectedPath: "out/soong/target/product/test_device/vendor/overlay",
+ },
+ {
+ name: "system_ext_specific",
+ expectedPath: "out/soong/target/product/test_device/system_ext/overlay",
+ },
+ {
+ name: "product_specific",
+ expectedPath: "out/soong/target/product/test_device/product/overlay",
+ },
+ {
+ name: "default",
+ expectedPath: "out/soong/target/product/test_device/product/overlay",
+ },
+ }
+ for _, testCase := range testCases {
+ ctx, _ := testJava(t, bp)
+ mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
+ android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
+ }
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index ecf2b1a..41097ca 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -435,9 +435,6 @@
// a list of top-level directories containing Java stub files to merge show/hide annotations from.
Merge_inclusion_annotations_dirs []string
- // If set to true, the path of dist files is apistubs/core. Defaults to false.
- Core_lib *bool
-
// If set to true then don't create dist rules.
No_dist *bool
@@ -458,7 +455,7 @@
Dist_stem *string
// The subdirectory for the artifacts that are copied to the dist directory. If not specified
- // then defaults to "android". Should be set to "android" for anything that should be published
+ // then defaults to "unknown". Should be set to "android" for anything that should be published
// in the public Android SDK.
Dist_group *string
@@ -1203,11 +1200,7 @@
// The dist path of the stub artifacts
func (module *SdkLibrary) apiDistPath(apiScope *apiScope) string {
- if Bool(module.sdkLibraryProperties.Core_lib) {
- return path.Join("apistubs", "core", apiScope.name)
- } else {
- return path.Join("apistubs", module.distGroup(), apiScope.name)
- }
+ return path.Join("apistubs", module.distGroup(), apiScope.name)
}
// Get the sdk version for use when compiling the stubs library.
@@ -1233,15 +1226,7 @@
// distGroup returns the subdirectory of the dist path of the stub artifacts.
func (module *SdkLibrary) distGroup() string {
- if group := proptools.String(module.sdkLibraryProperties.Dist_group); group != "" {
- return group
- }
- // TODO(b/186723288): Remove this once everything uses dist_group.
- if owner := module.ModuleBase.Owner(); owner != "" {
- return owner
- }
- // TODO(b/186723288): Make this "unknown".
- return "android"
+ return proptools.StringDefault(module.sdkLibraryProperties.Dist_group, "unknown")
}
func (module *SdkLibrary) latestApiFilegroupName(apiScope *apiScope) string {
@@ -2159,7 +2144,7 @@
// Get the path of the dex implementation jar from the `deapexer` module.
di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
- if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil {
+ if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
module.dexJarFile = dexOutputPath
module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
} else {
@@ -2284,6 +2269,13 @@
}
}
+var _ android.RequiredFilesFromPrebuiltApex = (*SdkLibraryImport)(nil)
+
+func (module *SdkLibraryImport) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+ name := module.BaseModuleName()
+ return requiredFilesFromPrebuiltApexForImport(name)
+}
+
//
// java_sdk_library_xml
//
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 0fe6e72..2520dde 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -844,40 +844,34 @@
PrepareForTestWithJavaBuildComponents,
PrepareForTestWithJavaDefaultModules,
PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis(
+ "sdklib_no_group",
+ "sdklib_group_foo",
+ "sdklib_owner_foo",
+ "foo"),
).RunTestWithBp(t, `
java_sdk_library {
- name: "sdklib_no_owner",
- unsafe_ignore_missing_latest_api: true,
+ name: "sdklib_no_group",
srcs: ["foo.java"],
}
java_sdk_library {
name: "sdklib_group_foo",
- unsafe_ignore_missing_latest_api: true,
srcs: ["foo.java"],
dist_group: "foo",
}
java_sdk_library {
name: "sdklib_owner_foo",
- unsafe_ignore_missing_latest_api: true,
srcs: ["foo.java"],
owner: "foo",
}
java_sdk_library {
name: "sdklib_stem_foo",
- unsafe_ignore_missing_latest_api: true,
srcs: ["foo.java"],
dist_stem: "foo",
}
-
- java_sdk_library {
- name: "sdklib_core_lib",
- unsafe_ignore_missing_latest_api: true,
- srcs: ["foo.java"],
- core_lib: true,
- }
`)
type testCase struct {
@@ -887,9 +881,9 @@
}
testCases := []testCase{
{
- module: "sdklib_no_owner",
- distDir: "apistubs/android/public",
- distStem: "sdklib_no_owner.jar",
+ module: "sdklib_no_group",
+ distDir: "apistubs/unknown/public",
+ distStem: "sdklib_no_group.jar",
},
{
module: "sdklib_group_foo",
@@ -897,20 +891,16 @@
distStem: "sdklib_group_foo.jar",
},
{
+ // Owner doesn't affect distDir after b/186723288.
module: "sdklib_owner_foo",
- distDir: "apistubs/foo/public",
+ distDir: "apistubs/unknown/public",
distStem: "sdklib_owner_foo.jar",
},
{
module: "sdklib_stem_foo",
- distDir: "apistubs/android/public",
+ distDir: "apistubs/unknown/public",
distStem: "foo.jar",
},
- {
- module: "sdklib_core_lib",
- distDir: "apistubs/core/public",
- distStem: "sdklib_core_lib.jar",
- },
}
for _, tt := range testCases {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 5bd25cd..a0decb7 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -48,11 +48,11 @@
}
func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- classpathJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType)
+ classpathJars := configuredJarListToClasspathJars(ctx, p.configuredJars(ctx), p.classpathType)
p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
}
-func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
global := dexpreopt.GetGlobalConfig(ctx)
return global.SystemServerJars
}
@@ -94,14 +94,14 @@
ctx.PropertyErrorf("contents", "empty contents are not allowed")
}
- classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx), s.classpathType)
+ classpathJars := configuredJarListToClasspathJars(ctx, s.configuredJars(ctx), s.classpathType)
s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
// Collect the module directory for IDE info in java/jdeps.go.
s.modulePaths = append(s.modulePaths, ctx.ModuleDir())
}
-func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
global := dexpreopt.GetGlobalConfig(ctx)
// Convert content names to their appropriate stems, in case a test library is overriding an actual boot jar
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index ef4672a..9e7a0f1 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -50,8 +50,14 @@
)
var (
- defaultLabels = map[string]string{"type": "tool"}
- defaultExecStrategy = LocalExecStrategy
+ defaultLabels = map[string]string{"type": "tool"}
+ defaultExecStrategy = LocalExecStrategy
+ defaultEnvironmentVariables = []string{
+ // This is a subset of the allowlist in ui/build/ninja.go that makes sense remotely.
+ "LANG",
+ "LC_MESSAGES",
+ "PYTHONDONTWRITEBYTECODE",
+ }
)
// REParams holds information pertinent to the remote execution of a rule.
@@ -149,8 +155,10 @@
args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
}
- if len(r.EnvironmentVariables) > 0 {
- args += " --env_var_allowlist=" + strings.Join(r.EnvironmentVariables, ",")
+ envVarAllowlist := append(r.EnvironmentVariables, defaultEnvironmentVariables...)
+
+ if len(envVarAllowlist) > 0 {
+ args += " --env_var_allowlist=" + strings.Join(envVarAllowlist, ",")
}
return args + " -- "
diff --git a/remoteexec/remoteexec_test.go b/remoteexec/remoteexec_test.go
index b117b89..3686316 100644
--- a/remoteexec/remoteexec_test.go
+++ b/remoteexec/remoteexec_test.go
@@ -36,7 +36,7 @@
PoolKey: "default",
},
},
- want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage),
+ want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage),
},
{
name: "all params",
@@ -52,7 +52,7 @@
PoolKey: "default",
},
},
- want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ -- ", DefaultImage),
+ want: fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=remote --inputs=$in --input_list_paths=$out.rsp,out2.rsp --output_files=$out --toolchain_inputs=clang++ --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage),
},
}
for _, test := range tests {
@@ -74,7 +74,7 @@
PoolKey: "default",
},
}
- want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
+ want := fmt.Sprintf("prebuilts/remoteexecution-client/live/rewrapper --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage)
if got := params.NoVarTemplate(DefaultWrapperPath); got != want {
t.Errorf("NoVarTemplate() returned\n%s\nwant\n%s", got, want)
}
@@ -90,7 +90,7 @@
PoolKey: "default",
},
}
- want := fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out -- ", DefaultImage)
+ want := fmt.Sprintf("${android.RBEWrapper} --labels=compiler=clang,lang=cpp,type=compile --platform=\"Pool=default,container-image=%s\" --exec_strategy=local --inputs=$in --output_files=$out --env_var_allowlist=LANG,LC_MESSAGES,PYTHONDONTWRITEBYTECODE -- ", DefaultImage)
for i := 0; i < 1000; i++ {
if got := r.Template(); got != want {
t.Fatalf("Template() returned\n%s\nwant\n%s", got, want)
diff --git a/rust/library.go b/rust/library.go
index 1a56ef6..747a29d 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -503,9 +503,8 @@
if library.shared() {
ctx.SetProvider(cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
- SharedLibrary: outputFile,
- UnstrippedSharedLibrary: outputFile,
- Target: ctx.Target(),
+ SharedLibrary: outputFile,
+ Target: ctx.Target(),
})
}
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 4b08ac3..8a5513e 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -1,14 +1,24 @@
#!/bin/bash -e
-# This is a wrapper around "m" that builds the given modules in multi-arch mode
-# for all architectures supported by Mainline modules. The make (kati) stage is
-# skipped, so the build targets in the arguments can only be Soong modules or
-# intermediate output files - make targets and normal installed paths are not
-# supported.
+# This script is similar to "m" but builds in --soong-only mode, and handles
+# special cases to make that mode work. All arguments are passed on to
+# build/soong/soong_ui.bash.
#
-# This script is typically used with "sdk" or "module_export" modules, which
-# Soong will install in $OUT_DIR/soong/mainline-sdks (cf
-# PathForMainlineSdksInstall in android/paths.go).
+# --soong-only bypasses the kati step and hence the make logic that e.g. doesn't
+# handle more than two device architectures. It is particularly intended for use
+# with TARGET_PRODUCT=mainline_sdk to build 'sdk' and 'module_export' Soong
+# modules in TARGET_ARCH_SUITE=mainline_sdk mode so that they get all four
+# device architectures (artifacts get installed in $OUT_DIR/soong/mainline-sdks
+# - cf PathForMainlineSdksInstall in android/paths.go).
+#
+# TODO(b/174315599): Replace this script completely with a 'soong_ui.bash
+# --soong-only' invocation. For now it is still necessary to set up
+# build_number.txt.
+
+if [ ! -e build/soong/soong_ui.bash ]; then
+ echo "$0 must be run from the top of the tree"
+ exit 1
+fi
export OUT_DIR=${OUT_DIR:-out}
@@ -23,109 +33,19 @@
OUT_DIR=${AML_OUT_DIR}
fi
-if [ ! -e "build/envsetup.sh" ]; then
- echo "$0 must be run from the top of the tree"
- exit 1
-fi
+mkdir -p ${OUT_DIR}/soong
-source build/envsetup.sh
-
-my_get_build_var() {
- # get_build_var will run Soong in normal in-make mode where it creates
- # .soong.kati_enabled. That would clobber our real out directory, so we need
- # to run it in a different one.
- OUT_DIR=${OUT_DIR}/get_build_var get_build_var "$@"
-}
-
-readonly SOONG_OUT=${OUT_DIR}/soong
-mkdir -p ${SOONG_OUT}
+# The --dumpvars-mode invocation will run Soong in normal make mode where it
+# creates .soong.kati_enabled. That would clobber our real out directory, so we
+# need to use a different OUT_DIR.
+vars="$(OUT_DIR=${OUT_DIR}/dumpvars_mode build/soong/soong_ui.bash \
+ --dumpvars-mode --vars=BUILD_NUMBER)"
+# Assign to a variable and eval that, since bash ignores any error status
+# from the command substitution if it's directly on the eval line.
+eval $vars
# Some Soong build rules may require this, and the failure mode if it's missing
# is confusing (b/172548608).
-readonly BUILD_NUMBER="$(my_get_build_var BUILD_NUMBER)"
-echo -n ${BUILD_NUMBER} > ${SOONG_OUT}/build_number.txt
+echo -n ${BUILD_NUMBER} > ${OUT_DIR}/soong/build_number.txt
-readonly PLATFORM_SDK_VERSION="$(my_get_build_var PLATFORM_SDK_VERSION)"
-readonly PLATFORM_VERSION="$(my_get_build_var PLATFORM_VERSION)"
-PLATFORM_VERSION_ALL_CODENAMES="$(my_get_build_var PLATFORM_VERSION_ALL_CODENAMES)"
-
-# PLATFORM_VERSION_ALL_CODENAMES is a comma separated list like O,P. We need to
-# turn this into ["O","P"].
-PLATFORM_VERSION_ALL_CODENAMES="${PLATFORM_VERSION_ALL_CODENAMES/,/'","'}"
-PLATFORM_VERSION_ALL_CODENAMES="[\"${PLATFORM_VERSION_ALL_CODENAMES}\"]"
-
-# Get the list of missing <uses-library> modules and convert it to a JSON array
-# (quote module names, add comma separator and wrap in brackets).
-MISSING_USES_LIBRARIES="$(my_get_build_var INTERNAL_PLATFORM_MISSING_USES_LIBRARIES)"
-MISSING_USES_LIBRARIES="[$(echo $MISSING_USES_LIBRARIES | sed -e 's/\([^ ]\+\)/\"\1\"/g' -e 's/[ ]\+/, /g')]"
-
-# Logic from build/make/core/goma.mk
-if [ "${USE_GOMA}" = true ]; then
- if [ -n "${GOMA_DIR}" ]; then
- goma_dir="${GOMA_DIR}"
- else
- goma_dir="${HOME}/goma"
- fi
- GOMA_CC="${goma_dir}/gomacc"
- export CC_WRAPPER="${CC_WRAPPER}${CC_WRAPPER:+ }${GOMA_CC}"
- export CXX_WRAPPER="${CXX_WRAPPER}${CXX_WRAPPER:+ }${GOMA_CC}"
- export JAVAC_WRAPPER="${JAVAC_WRAPPER}${JAVAC_WRAPPER:+ }${GOMA_CC}"
-else
- USE_GOMA=false
-fi
-
-readonly SOONG_VARS=${SOONG_OUT}/soong.variables
-
-# Aml_abis: true
-# - This flag configures Soong to compile for all architectures required for
-# Mainline modules.
-# CrossHost: linux_bionic
-# CrossHostArch: x86_64
-# - Enable Bionic on host as ART needs prebuilts for it.
-# VendorVars.art_mdoule.source_build
-# - TODO(b/172480615): Change default to false when platform uses ART Module
-# prebuilts by default.
-cat > ${SOONG_VARS}.new << EOF
-{
- "BuildNumberFile": "build_number.txt",
-
- "Platform_version_name": "${PLATFORM_VERSION}",
- "Platform_sdk_version": ${PLATFORM_SDK_VERSION},
- "Platform_sdk_codename": "${PLATFORM_VERSION}",
- "Platform_version_active_codenames": ${PLATFORM_VERSION_ALL_CODENAMES},
-
- "DeviceName": "generic_arm64",
- "HostArch": "x86_64",
- "HostSecondaryArch": "x86",
- "CrossHost": "linux_bionic",
- "CrossHostArch": "x86_64",
- "Aml_abis": true,
-
- "Allow_missing_dependencies": ${SOONG_ALLOW_MISSING_DEPENDENCIES:-false},
- "Unbundled_build": ${TARGET_BUILD_UNBUNDLED:-false},
- "UseGoma": ${USE_GOMA},
-
- "VendorVars": {
- "art_module": {
- "source_build": "${ENABLE_ART_SOURCE_BUILD:-true}"
- }
- },
-
- "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
-}
-EOF
-
-if [ -f ${SOONG_VARS} ] && cmp -s ${SOONG_VARS} ${SOONG_VARS}.new; then
- # Don't touch soong.variables if we don't have to, to avoid Soong rebuilding
- # the ninja file when it isn't necessary.
- rm ${SOONG_VARS}.new
-else
- mv ${SOONG_VARS}.new ${SOONG_VARS}
-fi
-
-# We use force building LLVM components flag (even though we actually don't
-# compile them) because we don't have bionic host prebuilts
-# for them.
-export FORCE_BUILD_LLVM_COMPONENTS=true
-
-m --skip-make "$@"
+build/soong/soong_ui.bash --make-mode --soong-only "$@"
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 39c8fba..b05861b 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -28,7 +28,6 @@
runtime-module-host-exports
runtime-module-sdk
statsd-module-sdk
- statsd-module-sdk-for-art
tzdata-module-test-exports
)
@@ -93,11 +92,17 @@
done
done
+# We use force building LLVM components flag (even though we actually don't
+# compile them) because we don't have bionic host prebuilts
+# for them.
+export FORCE_BUILD_LLVM_COMPONENTS=true
+
# Create multi-archs SDKs in a different out directory. The multi-arch script
# uses Soong in --skip-make mode which cannot use the same directory as normal
# mode with make.
export OUT_DIR=${OUT_DIR}/aml
-echo_and_run build/soong/scripts/build-aml-prebuilts.sh ${MODULES_SDK_AND_EXPORTS[@]}
+echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
+ TARGET_PRODUCT=mainline_sdk ${MODULES_SDK_AND_EXPORTS[@]}
rm -rf ${DIST_DIR}/mainline-sdks
echo_and_run cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index b047aab..a65326c 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -55,14 +55,15 @@
if entry.endswith('.uau'):
csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
-headers = set()
if args.header:
fieldnames = args.header.split(',')
else:
+ headers = {}
# Build union of all columns from source files:
for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
- fieldnames = sorted(headers)
+ for fieldname in reader.fieldnames:
+ headers[fieldname] = ""
+ fieldnames = list(headers.keys())
# By default chain the csv readers together so that the resulting output is
# the concatenation of the rows from each of them:
diff --git a/scripts/rbc-run b/scripts/rbc-run
new file mode 100755
index 0000000..e2fa6d1
--- /dev/null
+++ b/scripts/rbc-run
@@ -0,0 +1,16 @@
+#! /bin/bash
+# Convert and run one configuration
+# Args: <product>-<variant>
+[[ $# -eq 1 && "$1" =~ ^(.*)-(.*)$ ]] || { echo Usage: ${0##*/} PRODUCT-VARIANT >&2; exit 1; }
+declare -r product="${BASH_REMATCH[1]:-aosp_arm}"
+declare -r variant="${BASH_REMATCH[2]:-eng}"
+set -eu
+declare -r output_root=${OUT_DIR:-out}
+declare -r runner="$output_root/soong/.bootstrap/bin/rbcrun"
+declare -r converter="$output_root/soong/.bootstrap/bin/mk2rbc"
+declare -r launcher=$output_root/launchers/run.rbc
+$converter -mode=write -r --outdir $output_root --launcher=$launcher $product
+printf "#TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant\n"
+env TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant \
+ $runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $launcher
+
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index d9fe281..1f28ed3 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -15,12 +15,54 @@
package sdk
import (
+ "fmt"
+ "path/filepath"
"testing"
"android/soong/android"
"android/soong/java"
)
+// fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that
+// references the bootclasspath fragment.
+func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+ return android.GroupFixturePreparers(
+ // Add a platform_bootclasspath module.
+ android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(`
+ platform_bootclasspath {
+ name: "platform-bootclasspath",
+ fragments: [
+ {
+ apex: "%s",
+ module: "%s",
+ },
+ ],
+ }
+ `, apex, fragment)),
+ android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+ android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
+ )
+}
+
+// fixtureAddPrebuiltApexForBootclasspathFragment adds a prebuilt_apex that exports the fragment.
+func fixtureAddPrebuiltApexForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
+ apexFile := fmt.Sprintf("%s.apex", apex)
+ dir := "prebuilts/apex"
+ return android.GroupFixturePreparers(
+ // A preparer to add a prebuilt apex to the test fixture.
+ android.FixtureAddTextFile(filepath.Join(dir, "Android.bp"), fmt.Sprintf(`
+ prebuilt_apex {
+ name: "%s",
+ src: "%s",
+ exported_bootclasspath_fragments: [
+ "%s",
+ ],
+ }
+ `, apex, apexFile, fragment)),
+ android.FixtureAddFile(filepath.Join(dir, apexFile), nil),
+ )
+}
+
func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForSdkTestWithJava,
@@ -34,20 +76,8 @@
"system/sepolicy/apex/com.android.art-file_contexts": nil,
}),
- // platform_bootclasspath that depends on the fragment.
- android.FixtureAddTextFile("frameworks/base/boot/Android.bp", `
- platform_bootclasspath {
- name: "platform-bootclasspath",
- fragments: [
- {
- apex: "com.android.art",
- module: "mybootclasspathfragment",
- },
- ],
- }
- `),
- // Needed for platform_bootclasspath
- android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("com.android.art", "mybootclasspathfragment"),
java.FixtureConfigureBootJars("com.android.art:mybootlib"),
android.FixtureWithRootAndroidBp(`
@@ -89,19 +119,8 @@
`),
).RunTest(t)
- // A preparer to add a prebuilt apex to the test fixture.
- prepareWithPrebuiltApex := android.GroupFixturePreparers(
- android.FixtureAddTextFile("prebuilts/apex/Android.bp", `
- prebuilt_apex {
- name: "com.android.art",
- src: "art.apex",
- exported_bootclasspath_fragments: [
- "mybootclasspathfragment",
- ],
- }
- `),
- android.FixtureAddFile("prebuilts/apex/art.apex", nil),
- )
+ // A preparer to update the test fixture used when processing an unpackage snapshot.
+ preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "mybootclasspathfragment")
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
@@ -154,10 +173,30 @@
checkAllCopyRules(`
.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
`),
- snapshotTestPreparer(checkSnapshotWithoutSource, prepareWithPrebuiltApex),
- snapshotTestPreparer(checkSnapshotWithSourcePreferred, prepareWithPrebuiltApex),
- snapshotTestPreparer(checkSnapshotPreferredWithSource, prepareWithPrebuiltApex),
+ snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+
+ // Check the behavior of the snapshot without the source.
+ snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
+ // Make sure that the boot jars package check rule includes the dex jar retrieved from the prebuilt apex.
+ checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/prebuilts/apex/com.android.art.deapexer/android_common/deapexer/javalib/mybootlib.jar")
+ }),
+
+ snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
+
+ // Make sure that the boot jars package check rule includes the dex jar created from the source.
+ checkBootJarsPackageCheckRule(t, result, "out/soong/.intermediates/mybootlib/android_common_apex10000/aligned/mybootlib.jar")
+}
+
+// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
+// package check rule.
+func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModule string) {
+ platformBcp := result.ModuleForTests("platform-bootclasspath", "android_common")
+ bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
+ command := bootJarsCheckRule.RuleParams.Command
+ expectedCommandArgs := " out/soong/host/linux-x86/bin/dexdump build/soong/scripts/check_boot_jars/package_allowed_list.txt " + expectedModule + " &&"
+ android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
}
func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
@@ -166,6 +205,12 @@
java.PrepareForTestWithJavaDefaultModules,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
+ java.FixtureConfigureUpdatableBootJars("myapex:mybootlib", "myapex:myothersdklibrary"),
+ prepareForSdkTestWithApex,
+
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
+
android.FixtureWithRootAndroidBp(`
sdk {
name: "mysdk",
@@ -179,8 +224,16 @@
],
}
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ min_sdk_version: "2",
+ bootclasspath_fragments: ["mybootclasspathfragment"],
+ }
+
bootclasspath_fragment {
name: "mybootclasspathfragment",
+ apex_available: ["myapex"],
contents: [
// This should be automatically added to the sdk_snapshot as a java_boot_libs module.
"mybootlib",
@@ -198,35 +251,48 @@
java_library {
name: "mybootlib",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
system_modules: "none",
sdk_version: "none",
+ min_sdk_version: "2",
compile_dex: true,
+ permitted_packages: ["mybootlib"],
}
java_sdk_library {
name: "mysdklibrary",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
shared_library: false,
public: {enabled: true},
+ min_sdk_version: "2",
}
java_sdk_library {
name: "myothersdklibrary",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
shared_library: false,
public: {enabled: true},
+ min_sdk_version: "2",
+ permitted_packages: ["myothersdklibrary"],
}
java_sdk_library {
name: "mycoreplatform",
+ apex_available: ["myapex"],
srcs: ["Test.java"],
shared_library: false,
public: {enabled: true},
+ min_sdk_version: "2",
}
`),
).RunTest(t)
+ // A preparer to update the test fixture used when processing an unpackage snapshot.
+ preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
+
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -235,7 +301,7 @@
name: "mybootclasspathfragment",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
contents: [
"mybootlib",
"myothersdklibrary",
@@ -259,7 +325,7 @@
name: "mybootlib",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
jars: ["java/mybootlib.jar"],
}
@@ -267,7 +333,7 @@
name: "myothersdklibrary",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
@@ -282,7 +348,7 @@
name: "mysdklibrary",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
@@ -297,7 +363,7 @@
name: "mycoreplatform",
prefer: false,
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
@@ -315,7 +381,7 @@
name: "mysdk_mybootclasspathfragment@current",
sdk_member_name: "mybootclasspathfragment",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
contents: [
"mysdk_mybootlib@current",
"mysdk_myothersdklibrary@current",
@@ -339,7 +405,7 @@
name: "mysdk_mybootlib@current",
sdk_member_name: "mybootlib",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
jars: ["java/mybootlib.jar"],
}
@@ -347,7 +413,7 @@
name: "mysdk_myothersdklibrary@current",
sdk_member_name: "myothersdklibrary",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
@@ -362,7 +428,7 @@
name: "mysdk_mysdklibrary@current",
sdk_member_name: "mysdklibrary",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
@@ -377,7 +443,7 @@
name: "mysdk_mycoreplatform@current",
sdk_member_name: "mycoreplatform",
visibility: ["//visibility:public"],
- apex_available: ["//apex_available:platform"],
+ apex_available: ["myapex"],
shared_library: false,
public: {
jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
@@ -416,7 +482,11 @@
.intermediates/mycoreplatform.stubs/android_common/javac/mycoreplatform.stubs.jar -> sdk_library/public/mycoreplatform-stubs.jar
.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
.intermediates/mycoreplatform.stubs.source/android_common/metalava/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
-`))
+`),
+ snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
+ )
}
// Test that bootclasspath_fragment works with sdk.
@@ -482,7 +552,12 @@
java.PrepareForTestWithJavaDefaultModules,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mysdklibrary"),
+ java.FixtureConfigureUpdatableBootJars("myapex:mybootlib"),
prepareForSdkTestWithApex,
+
+ // Add a platform_bootclasspath that depends on the fragment.
+ fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
+
android.MockFS{
"my-blocked.txt": nil,
"my-max-target-o-low-priority.txt": nil,
@@ -549,6 +624,7 @@
sdk_version: "none",
min_sdk_version: "1",
compile_dex: true,
+ permitted_packages: ["mybootlib"],
}
java_sdk_library {
@@ -560,6 +636,9 @@
`),
).RunTest(t)
+ // A preparer to update the test fixture used when processing an unpackage snapshot.
+ preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
+
CheckSnapshot(t, result, "mysdk", "",
checkUnversionedAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.
@@ -633,5 +712,8 @@
.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
`),
+ snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+ snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
)
}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index f1c2d0d..a29d4c3 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -145,6 +145,9 @@
// If set to true, allow this module to be dexed and installed on devices.
Installable *bool
+ // Make this module available when building for ramdisk
+ Ramdisk_available *bool
+
// Make this module available when building for recovery
Recovery_available *bool
@@ -396,6 +399,7 @@
Recovery_available *bool
Vendor_available *bool
Product_available *bool
+ Ramdisk_available *bool
Host_supported *bool
Apex_available []string
Min_sdk_version *string
@@ -475,6 +479,7 @@
ccProps.Recovery_available = m.properties.Recovery_available
ccProps.Vendor_available = m.properties.Vendor_available
ccProps.Product_available = m.properties.Product_available
+ ccProps.Ramdisk_available = m.properties.Ramdisk_available
ccProps.Host_supported = m.properties.Host_supported
ccProps.Apex_available = m.ApexProperties.Apex_available
ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
diff --git a/ui/build/build.go b/ui/build/build.go
index c2ad057..8f050d9 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -60,15 +60,15 @@
{{end -}}
pool highmem_pool
depth = {{.HighmemParallel}}
-{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
+{{if and (not .SkipKatiNinja) .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
subninja {{.KatiPackageNinjaFile}}
{{end -}}
subninja {{.SoongNinjaFile}}
`))
func createCombinedBuildNinjaFile(ctx Context, config Config) {
- // If we're in SkipKati mode, skip creating this file if it already exists
- if config.SkipKati() {
+ // If we're in SkipKati mode but want to run kati ninja, skip creating this file if it already exists
+ if config.SkipKati() && !config.SkipKatiNinja() {
if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
return
}
@@ -87,15 +87,22 @@
// 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
- BuildSoong = 1 << iota
- BuildKati = 1 << iota
- BuildNinja = 1 << iota
- BuildBazel = 1 << iota
- RunBuildTests = 1 << iota
- BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
- BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
+ _ = iota
+ // Whether to run the kati config step.
+ RunProductConfig = 1 << iota
+ // Whether to run soong to generate a ninja file.
+ RunSoong = 1 << iota
+ // Whether to run kati to generate a ninja file.
+ RunKati = 1 << iota
+ // Whether to include the kati-generated ninja file in the combined ninja.
+ RunKatiNinja = 1 << iota
+ // Whether to run ninja on the combined ninja.
+ RunNinja = 1 << iota
+ // Whether to run bazel on the combined ninja.
+ RunBazel = 1 << iota
+ RunBuildTests = 1 << iota
+ RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja
+ RunAllWithBazel = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunBazel
)
// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
@@ -173,7 +180,7 @@
// 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) {
+func Build(ctx Context, config Config) {
ctx.Verboseln("Starting build with args:", config.Arguments())
ctx.Verboseln("Environment:", config.Environment().Environ())
@@ -208,33 +215,39 @@
SetupPath(ctx, config)
+ what := RunAll
+ if config.UseBazel() {
+ what = RunAllWithBazel
+ }
+ if config.Checkbuild() {
+ what |= RunBuildTests
+ }
if config.SkipConfig() {
ctx.Verboseln("Skipping Config as requested")
- what = what &^ BuildProductConfig
+ what = what &^ RunProductConfig
}
-
if config.SkipKati() {
ctx.Verboseln("Skipping Kati as requested")
- what = what &^ BuildKati
+ what = what &^ RunKati
}
-
+ if config.SkipKatiNinja() {
+ ctx.Verboseln("Skipping use of Kati ninja as requested")
+ what = what &^ RunKatiNinja
+ }
if config.SkipNinja() {
ctx.Verboseln("Skipping Ninja as requested")
- what = what &^ BuildNinja
+ what = what &^ RunNinja
}
if config.StartGoma() {
- // Ensure start Goma compiler_proxy
startGoma(ctx, config)
}
if config.StartRBE() {
- // Ensure RBE proxy is started
startRBE(ctx, config)
}
- if what&BuildProductConfig != 0 {
- // Run make for product config
+ if what&RunProductConfig != 0 {
runMakeProductConfig(ctx, config)
}
@@ -254,8 +267,7 @@
return
}
- if what&BuildSoong != 0 {
- // Run Soong
+ if what&RunSoong != 0 {
runSoong(ctx, config)
if config.bazelBuildMode() == generateBuildFiles {
@@ -264,15 +276,14 @@
}
}
- if what&BuildKati != 0 {
- // Run ckati
+ if what&RunKati != 0 {
genKatiSuffix(ctx, config)
runKatiCleanSpec(ctx, config)
runKatiBuild(ctx, config)
runKatiPackage(ctx, config)
ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
- } else {
+ } else if what&RunKatiNinja != 0 {
// Load last Kati Suffix if it exists
if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
@@ -289,17 +300,16 @@
testForDanglingRules(ctx, config)
}
- if what&BuildNinja != 0 {
- if what&BuildKati != 0 {
+ if what&RunNinja != 0 {
+ if what&RunKati != 0 {
installCleanIfNecessary(ctx, config)
}
- // Run ninja
runNinjaForBuild(ctx, config)
}
// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
- if what&BuildBazel != 0 {
+ if what&RunBazel != 0 {
runBazel(ctx, config)
}
}
diff --git a/ui/build/config.go b/ui/build/config.go
index f1f5989..220e734 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -48,6 +48,7 @@
dist bool
skipConfig bool
skipKati bool
+ skipKatiNinja bool
skipNinja bool
skipSoongTests bool
@@ -567,8 +568,7 @@
func (c *configImpl) parseArgs(ctx Context, args []string) {
for i := 0; i < len(args); i++ {
arg := strings.TrimSpace(args[i])
- if arg == "--make-mode" {
- } else if arg == "showcommands" {
+ if arg == "showcommands" {
c.verbose = true
} else if arg == "--skip-ninja" {
c.skipNinja = true
@@ -576,7 +576,11 @@
c.skipConfig = true
c.skipKati = true
} else if arg == "--skip-kati" {
+ // TODO: remove --skip-kati once module builds have been migrated to --song-only
c.skipKati = true
+ } else if arg == "--soong-only" {
+ c.skipKati = true
+ c.skipKatiNinja = true
} else if arg == "--skip-soong-tests" {
c.skipSoongTests = true
} else if len(arg) > 0 && arg[0] == '-' {
@@ -792,10 +796,18 @@
return c.skipKati
}
+func (c *configImpl) SkipKatiNinja() bool {
+ return c.skipKatiNinja
+}
+
func (c *configImpl) SkipNinja() bool {
return c.skipNinja
}
+func (c *configImpl) SetSkipNinja(v bool) {
+ c.skipNinja = v
+}
+
func (c *configImpl) SkipConfig() bool {
return c.skipConfig
}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 7128414..19a47ae 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -99,6 +99,21 @@
"--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
}
}
+
+func writeEmptyGlobFile(ctx Context, path string) {
+ err := os.MkdirAll(filepath.Dir(path), 0777)
+ if err != nil {
+ ctx.Fatalf("Failed to create parent directories of empty ninja glob file '%s': %s", path, err)
+ }
+
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ err = ioutil.WriteFile(path, nil, 0666)
+ if err != nil {
+ ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err)
+ }
+ }
+}
+
func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
defer ctx.EndTrace()
@@ -106,8 +121,10 @@
var args bootstrap.Args
mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
- globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
+ // .bootstrap/build.ninja "includes" .bootstrap/build-globs.ninja for incremental builds
+ // generate an empty glob before running any rule in .bootstrap/build.ninja
+ writeEmptyGlobFile(ctx, bootstrapGlobFile)
bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
args.RunGoTests = !config.skipSoongTests
@@ -117,7 +134,10 @@
args.TopFile = "Android.bp"
args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
- args.GlobFile = globFile
+ // The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
+ // Building soong_build does not require a glob file
+ // Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
+ args.GlobFile = ""
args.GeneratingPrimaryBuilder = true
args.EmptyNinjaFile = config.EmptyNinjaFile()