Merge "Do not dump BOARD_VNDK_VERSION" into main
diff --git a/README.md b/README.md
index f471c47..93260e6 100644
--- a/README.md
+++ b/README.md
@@ -314,6 +314,9 @@
* `["//visibility:override"]`: Discards any rules inherited from defaults or a
creating module. Can only be used at the beginning of a list of visibility
rules.
+* `["//visibility:any_partition"]`: Any modules of type android_filesystem
+or android_system_image can use this module. Intended for modules that no one
+should link against, but should still be included in soong-built partitions.
* `["//some/package:__pkg__", "//other/package:__pkg__"]`: Only modules in
`some/package` and `other/package` (defined in `some/package/*.bp` and
`other/package/*.bp`) have access to this module. Note that sub-packages do not
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index 78f506a..392e819 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -40,6 +40,9 @@
// Container(system/vendor/apex) that this module belongs to
Container string
+
+ // The flags will only be repackaged if this prop is true.
+ Exportable bool
}
intermediatePath android.WritablePath
@@ -159,6 +162,7 @@
android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{
Package: module.properties.Package,
Container: module.properties.Container,
+ Exportable: module.properties.Exportable,
IntermediateCacheOutputPath: intermediateCacheFilePath,
IntermediateDumpOutputPath: intermediateDumpFilePath,
})
diff --git a/aconfig/aconfig_declarations_test.go b/aconfig/aconfig_declarations_test.go
index d508af7..1fe3c86 100644
--- a/aconfig/aconfig_declarations_test.go
+++ b/aconfig/aconfig_declarations_test.go
@@ -27,6 +27,7 @@
name: "module_name",
package: "com.example.package",
container: "com.android.foo",
+ exportable: true,
srcs: [
"foo.aconfig",
"bar.aconfig",
@@ -41,6 +42,7 @@
depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
android.AssertStringEquals(t, "package", depData.Package, "com.example.package")
android.AssertStringEquals(t, "container", depData.Container, "com.android.foo")
+ android.AssertBoolEquals(t, "exportable", depData.Exportable, true)
if !strings.HasSuffix(depData.IntermediateCacheOutputPath.String(), "/intermediate.pb") {
t.Errorf("Missing intermediates proto path in provider: %s", depData.IntermediateCacheOutputPath.String())
}
@@ -48,3 +50,22 @@
t.Errorf("Missing intermediates text path in provider: %s", depData.IntermediateDumpOutputPath.String())
}
}
+
+func TestAconfigDeclarationsWithExportableUnset(t *testing.T) {
+ bp := `
+ aconfig_declarations {
+ name: "module_name",
+ package: "com.example.package",
+ container: "com.android.foo",
+ srcs: [
+ "foo.aconfig",
+ "bar.aconfig",
+ ],
+ }
+ `
+ result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+ module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
+ depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
+ android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
+}
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index e6817e0..d4c6da5 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -91,6 +91,12 @@
if !isModeSupported(mode) {
ctx.PropertyErrorf("mode", "%q is not a supported mode", mode)
}
+ // TODO: uncomment this part after internal clean up
+ //if mode == "exported" && !declarations.Exportable {
+ // // if mode is exported, the corresponding aconfig_declaration must mark its
+ // // exportable property true
+ // ctx.PropertyErrorf("mode", "exported mode requires its aconfig_declaration has exportable prop true")
+ //}
ctx.Build(pctx, android.BuildParams{
Rule: javaRule,
@@ -102,6 +108,16 @@
},
})
+ if declarations.Exportable {
+ // Mark our generated code as possibly needing jarjar repackaging
+ // The repackaging only happens when the corresponding aconfig_declaration
+ // has property exportable true
+ module.AddJarJarRenameRule(declarations.Package+".Flags", "")
+ module.AddJarJarRenameRule(declarations.Package+".FeatureFlags", "")
+ module.AddJarJarRenameRule(declarations.Package+".FeatureFlagsImpl", "")
+ module.AddJarJarRenameRule(declarations.Package+".FakeFeatureFlagsImpl", "")
+ }
+
return srcJarPath
}
diff --git a/aconfig/codegen/java_aconfig_library_test.go b/aconfig/codegen/java_aconfig_library_test.go
index 85d2675..de45b5c 100644
--- a/aconfig/codegen/java_aconfig_library_test.go
+++ b/aconfig/codegen/java_aconfig_library_test.go
@@ -176,6 +176,7 @@
name: "my_aconfig_declarations",
package: "com.example.package",
srcs: ["foo.aconfig"],
+ exportable: true,
}
java_aconfig_library {
diff --git a/aconfig/init.go b/aconfig/init.go
index 3e9d297..77f5ed3 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -43,7 +43,7 @@
// For create-device-config-sysprops: Generate aconfig flag value map text file
aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
blueprint.RuleParams{
- Command: `${aconfig} dump-cache --format='{fully_qualified_name}={state:bool}'` +
+ Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` +
` --cache ${in}` +
` --out ${out}.tmp` +
` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
@@ -56,7 +56,7 @@
// For all_aconfig_declarations: Combine all parsed_flags proto files
AllDeclarationsRule = pctx.AndroidStaticRule("All_aconfig_declarations_dump",
blueprint.RuleParams{
- Command: `${aconfig} dump-cache --format protobuf --out ${out} ${cache_files}`,
+ Command: `${aconfig} dump-cache --dedup --format protobuf --out ${out} ${cache_files}`,
CommandDeps: []string{
"${aconfig}",
},
@@ -73,7 +73,7 @@
blueprint.RuleParams{
Command: `rm -rf ${out}.tmp` +
`&& for cache in ${cache_files}; do ` +
- ` if [ -n "$$(${aconfig} dump-cache --cache $$cache --filter=is_exported:true --format='{fully_qualified_name}')" ]; then ` +
+ ` if [ -n "$$(${aconfig} dump-cache --dedup --cache $$cache --filter=is_exported:true --format='{fully_qualified_name}')" ]; then ` +
` ${aconfig} create-java-lib --cache $$cache --mode=exported --out ${out}.tmp; ` +
` fi ` +
`done` +
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index be9beb1..74c1a5e 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -35,6 +35,7 @@
type AconfigDeclarationsProviderData struct {
Package string
Container string
+ Exportable bool
IntermediateCacheOutputPath WritablePath
IntermediateDumpOutputPath WritablePath
}
@@ -171,7 +172,7 @@
}
func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
- inputs = LastUniquePaths(inputs)
+ inputs = SortedUniquePaths(inputs)
if len(inputs) == 1 {
return Paths{inputs[0]}
}
diff --git a/android/all_teams.go b/android/all_teams.go
index 6c3a219..dd7d2db 100644
--- a/android/all_teams.go
+++ b/android/all_teams.go
@@ -118,8 +118,8 @@
// either the declared team data for that module or the package default team data for that module.
func (this *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
teamsProto := make([]*team_proto.Team, len(this.teams_for_mods))
- i := 0
- for moduleName, m := range this.teams_for_mods {
+ for i, moduleName := range SortedKeys(this.teams_for_mods) {
+ m, _ := this.teams_for_mods[moduleName]
teamName := m.teamName
var teamProperties teamProperties
found := false
@@ -152,7 +152,6 @@
}
}
teamsProto[i] = teamData
- i++
}
return &team_proto.AllTeams{Teams: teamsProto}
}
diff --git a/android/arch.go b/android/arch.go
index c39db02..4fe4345 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -426,6 +426,7 @@
// filters out non-Soong modules. Now that we've handled them, create a
// normal android.BottomUpMutatorContext.
mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+ defer bottomUpMutatorContextPool.Put(mctx)
base := module.base()
@@ -572,6 +573,7 @@
// filters out non-Soong modules. Now that we've handled them, create a
// normal android.BottomUpMutatorContext.
mctx := bottomUpMutatorContextFactory(bpctx, module, false)
+ defer bottomUpMutatorContextPool.Put(mctx)
base := module.base()
@@ -1058,9 +1060,7 @@
// order checks the `android:"variant_prepend"` tag to handle properties where the
// arch-specific value needs to come before the generic value, for example for lists of
// include directories.
- order := func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (proptools.Order, error) {
+ order := func(dstField, srcField reflect.StructField) (proptools.Order, error) {
if proptools.HasTag(dstField, "android", "variant_prepend") {
return proptools.Prepend, nil
} else {
diff --git a/android/config.go b/android/config.go
index d94a86f..407aeb7 100644
--- a/android/config.go
+++ b/android/config.go
@@ -28,6 +28,7 @@
"strconv"
"strings"
"sync"
+ "unicode"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
@@ -209,6 +210,12 @@
Bool(c.config.productVariables.ReleaseDefaultModuleBuildFromSource)
}
+// Enables flagged apis annotated with READ_WRITE aconfig flags to be included in the stubs
+// and hiddenapi flags so that they are accessible at runtime
+func (c Config) ReleaseExportRuntimeApis() bool {
+ return c.config.productVariables.GetBuildFlagBool("RELEASE_EXPORT_RUNTIME_APIS")
+}
+
// Enables ABI monitoring of NDK libraries
func (c Config) ReleaseNdkAbiMonitored() bool {
return c.config.productVariables.GetBuildFlagBool("RELEASE_NDK_ABI_MONITORED")
@@ -320,6 +327,18 @@
return loadFromConfigFile(&config.productVariables, absolutePath(config.ProductVariablesFileName))
}
+// Checks if the string is a valid go identifier. This is equivalent to blueprint's definition
+// of an identifier, so it will match the same identifiers as those that can be used in bp files.
+func isGoIdentifier(ident string) bool {
+ for i, r := range ident {
+ valid := r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) && i > 0
+ if !valid {
+ return false
+ }
+ }
+ return len(ident) > 0
+}
+
// loadFromConfigFile loads and decodes configuration options from a JSON file
// in the current working directory.
func loadFromConfigFile(configurable *ProductVariables, filename string) error {
@@ -355,6 +374,20 @@
Bool(configurable.GcovCoverage) ||
Bool(configurable.ClangCoverage))
+ // The go scanner's definition of identifiers is c-style identifiers, but allowing unicode's
+ // definition of letters and digits. This is the same scanner that blueprint uses, so it
+ // will allow the same identifiers as are valid in bp files.
+ for namespace := range configurable.VendorVars {
+ if !isGoIdentifier(namespace) {
+ return fmt.Errorf("soong config namespaces must be valid identifiers: %q", namespace)
+ }
+ for variable := range configurable.VendorVars[namespace] {
+ if !isGoIdentifier(variable) {
+ return fmt.Errorf("soong config variables must be valid identifiers: %q", variable)
+ }
+ }
+ }
+
// when Platform_sdk_final is true (or PLATFORM_VERSION_CODENAME is REL), use Platform_sdk_version;
// if false (pre-released version, for example), use Platform_sdk_codename.
if Bool(configurable.Platform_sdk_final) {
@@ -1466,18 +1499,18 @@
}
// AfdoProfile returns fully qualified path associated to the given module name
-func (c *deviceConfig) AfdoProfile(name string) (*string, error) {
+func (c *deviceConfig) AfdoProfile(name string) (string, error) {
for _, afdoProfile := range c.config.productVariables.AfdoProfiles {
split := strings.Split(afdoProfile, ":")
if len(split) != 3 {
- return nil, fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
+ return "", fmt.Errorf("AFDO_PROFILES has invalid value: %s. "+
"The expected format is <module>:<fully-qualified-path-to-fdo_profile>", afdoProfile)
}
if split[0] == name {
- return proptools.StringPtr(strings.Join([]string{split[1], split[2]}, ":")), nil
+ return strings.Join([]string{split[1], split[2]}, ":"), nil
}
}
- return nil, nil
+ return "", nil
}
func (c *deviceConfig) VendorSepolicyDirs() []string {
diff --git a/android/module.go b/android/module.go
index 5c7bbbf..b615ff5 100644
--- a/android/module.go
+++ b/android/module.go
@@ -34,6 +34,7 @@
var (
DeviceSharedLibrary = "shared_library"
DeviceStaticLibrary = "static_library"
+ jarJarPrefixHandler func(ctx ModuleContext)
)
type Module interface {
@@ -1772,6 +1773,13 @@
return
}
+ if jarJarPrefixHandler != nil {
+ jarJarPrefixHandler(ctx)
+ if ctx.Failed() {
+ return
+ }
+ }
+
m.module.GenerateAndroidBuildActions(ctx)
if ctx.Failed() {
return
@@ -1865,6 +1873,13 @@
m.variables = ctx.variables
}
+func SetJarJarPrefixHandler(handler func(ModuleContext)) {
+ if jarJarPrefixHandler != nil {
+ panic("jarJarPrefixHandler already set")
+ }
+ jarJarPrefixHandler = handler
+}
+
func (m *ModuleBase) moduleInfoRegisterName(ctx ModuleContext, subName string) string {
name := m.BaseModuleName()
diff --git a/android/mutator.go b/android/mutator.go
index 22e9160..0ff4f48 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,6 +15,8 @@
package android
import (
+ "sync"
+
"github.com/google/blueprint"
)
@@ -328,29 +330,52 @@
SetVariationProvider(module blueprint.Module, provider blueprint.AnyProviderKey, value interface{})
}
+// An outgoingTransitionContextImpl and incomingTransitionContextImpl is created for every dependency of every module
+// for each transition mutator. bottomUpMutatorContext and topDownMutatorContext are created once for every module
+// for every BottomUp or TopDown mutator. Use a global pool for each to avoid reallocating every time.
+var (
+ outgoingTransitionContextPool = sync.Pool{
+ New: func() any { return &outgoingTransitionContextImpl{} },
+ }
+ incomingTransitionContextPool = sync.Pool{
+ New: func() any { return &incomingTransitionContextImpl{} },
+ }
+ bottomUpMutatorContextPool = sync.Pool{
+ New: func() any { return &bottomUpMutatorContext{} },
+ }
+
+ topDownMutatorContextPool = sync.Pool{
+ New: func() any { return &topDownMutatorContext{} },
+ }
+)
+
type bottomUpMutatorContext struct {
bp blueprint.BottomUpMutatorContext
baseModuleContext
finalPhase bool
}
+// callers must immediately follow the call to this function with defer bottomUpMutatorContextPool.Put(mctx).
func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
finalPhase bool) BottomUpMutatorContext {
moduleContext := a.base().baseModuleContextFactory(ctx)
-
- return &bottomUpMutatorContext{
+ mctx := bottomUpMutatorContextPool.Get().(*bottomUpMutatorContext)
+ *mctx = bottomUpMutatorContext{
bp: ctx,
baseModuleContext: moduleContext,
finalPhase: finalPhase,
}
+ return mctx
}
func (x *registerMutatorsContext) BottomUp(name string, m BottomUpMutator) MutatorHandle {
finalPhase := x.finalPhase
f := func(ctx blueprint.BottomUpMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
- m(bottomUpMutatorContextFactory(ctx, a, finalPhase))
+ mctx := bottomUpMutatorContextFactory(ctx, a, finalPhase)
+ defer bottomUpMutatorContextPool.Put(mctx)
+ m(mctx)
}
}
mutator := &mutator{name: x.mutatorName(name), bottomUpMutator: f}
@@ -514,7 +539,9 @@
func (a *androidTransitionMutator) OutgoingTransition(bpctx blueprint.OutgoingTransitionContext, sourceVariation string) string {
if m, ok := bpctx.Module().(Module); ok {
- ctx := &outgoingTransitionContextImpl{
+ ctx := outgoingTransitionContextPool.Get().(*outgoingTransitionContextImpl)
+ defer outgoingTransitionContextPool.Put(ctx)
+ *ctx = outgoingTransitionContextImpl{
archModuleContext: m.base().archModuleContextFactory(bpctx),
bp: bpctx,
}
@@ -543,7 +570,9 @@
func (a *androidTransitionMutator) IncomingTransition(bpctx blueprint.IncomingTransitionContext, incomingVariation string) string {
if m, ok := bpctx.Module().(Module); ok {
- ctx := &incomingTransitionContextImpl{
+ ctx := incomingTransitionContextPool.Get().(*incomingTransitionContextImpl)
+ defer incomingTransitionContextPool.Put(ctx)
+ *ctx = incomingTransitionContextImpl{
archModuleContext: m.base().archModuleContextFactory(bpctx),
bp: bpctx,
}
@@ -555,7 +584,9 @@
func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, variation string) {
if am, ok := ctx.Module().(Module); ok {
- a.mutator.Mutate(bottomUpMutatorContextFactory(ctx, am, a.finalPhase), variation)
+ mctx := bottomUpMutatorContextFactory(ctx, am, a.finalPhase)
+ defer bottomUpMutatorContextPool.Put(mctx)
+ a.mutator.Mutate(mctx, variation)
}
}
@@ -578,7 +609,9 @@
f := func(ctx blueprint.TopDownMutatorContext) {
if a, ok := ctx.Module().(Module); ok {
moduleContext := a.base().baseModuleContextFactory(ctx)
- actx := &topDownMutatorContext{
+ actx := topDownMutatorContextPool.Get().(*topDownMutatorContext)
+ defer topDownMutatorContextPool.Put(actx)
+ *actx = topDownMutatorContext{
bp: ctx,
baseModuleContext: moduleContext,
}
diff --git a/android/packaging.go b/android/packaging.go
index 8873540..a8fb28d 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -85,6 +85,7 @@
// GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
+ GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec
// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
@@ -221,14 +222,18 @@
}
}
-// See PackageModule.GatherPackagingSpecs
-func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
+func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
m := make(map[string]PackagingSpec)
ctx.VisitDirectDeps(func(child Module) {
if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
return
}
for _, ps := range child.TransitivePackagingSpecs() {
+ if filter != nil {
+ if !filter(ps) {
+ continue
+ }
+ }
if _, ok := m[ps.relPathInPackage]; !ok {
m[ps.relPathInPackage] = ps
}
@@ -237,6 +242,11 @@
return m
}
+// See PackageModule.GatherPackagingSpecs
+func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
+ return p.GatherPackagingSpecsWithFilter(ctx, nil)
+}
+
// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
// entries into the specified directory.
func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) {
diff --git a/android/paths.go b/android/paths.go
index 95f53ea..61c1258 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1661,6 +1661,8 @@
// makePath indicates whether this path is for Soong (false) or Make (true).
makePath bool
+
+ fullPath string
}
// Will panic if called from outside a test environment.
@@ -1673,7 +1675,12 @@
func (p InstallPath) RelativeToTop() Path {
ensureTestOnly()
- p.soongOutDir = OutSoongDir
+ if p.makePath {
+ p.soongOutDir = OutDir
+ } else {
+ p.soongOutDir = OutSoongDir
+ }
+ p.fullPath = filepath.Join(p.soongOutDir, p.path)
return p
}
@@ -1691,12 +1698,7 @@
func (p InstallPath) writablePath() {}
func (p InstallPath) String() string {
- if p.makePath {
- // Make path starts with out/ instead of out/soong.
- return filepath.Join(p.soongOutDir, "../", p.path)
- } else {
- return filepath.Join(p.soongOutDir, p.path)
- }
+ return p.fullPath
}
// PartitionDir returns the path to the partition where the install path is rooted at. It is
@@ -1726,6 +1728,7 @@
func (p InstallPath) withRel(rel string) InstallPath {
p.basePath = p.basePath.withRel(rel)
+ p.fullPath = filepath.Join(p.fullPath, rel)
return p
}
@@ -1769,6 +1772,25 @@
return os, arch
}
+func pathForPartitionInstallDir(ctx PathContext, partition, partitionPath string, makePath bool) InstallPath {
+ fullPath := ctx.Config().SoongOutDir()
+ if makePath {
+ // Make path starts with out/ instead of out/soong.
+ fullPath = filepath.Join(fullPath, "../", partitionPath)
+ } else {
+ fullPath = filepath.Join(fullPath, partitionPath)
+ }
+
+ return InstallPath{
+ basePath: basePath{partitionPath, ""},
+ soongOutDir: ctx.Config().soongOutDir,
+ partitionDir: partitionPath,
+ partition: partition,
+ makePath: makePath,
+ fullPath: fullPath,
+ }
+}
+
func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string,
pathComponents ...string) InstallPath {
@@ -1805,27 +1827,12 @@
reportPathError(ctx, err)
}
- base := InstallPath{
- basePath: basePath{partitionPath, ""},
- soongOutDir: ctx.Config().soongOutDir,
- partitionDir: partitionPath,
- partition: partition,
- }
-
- if ctx.Config().KatiEnabled() {
- base.makePath = true
- }
-
+ base := pathForPartitionInstallDir(ctx, partition, partitionPath, ctx.Config().KatiEnabled())
return base.Join(ctx, pathComponents...)
}
func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
- base := InstallPath{
- basePath: basePath{prefix, ""},
- soongOutDir: ctx.Config().soongOutDir,
- partitionDir: prefix,
- makePath: false,
- }
+ base := pathForPartitionInstallDir(ctx, "", prefix, false)
return base.Join(ctx, paths...)
}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index a94f5b7..13cda9d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -518,7 +518,7 @@
// query all_apex_contributions to see if any module in this family has been selected
for _, moduleInFamily := range allModulesInFamily {
// validate that are no duplicates
- if psi.IsSelected(moduleInFamily.Name()) {
+ if isSelected(psi, moduleInFamily) {
if selectedModuleInFamily == nil {
// Store this so we can validate that there are no duplicates
selectedModuleInFamily = moduleInFamily
@@ -547,13 +547,29 @@
if p := GetEmbeddedPrebuilt(m); p != nil {
bmn, _ := m.(baseModuleName)
name := bmn.BaseModuleName()
+ psi := PrebuiltSelectionInfoMap{}
+ ctx.VisitDirectDepsWithTag(acDepTag, func(am Module) {
+ psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
+ })
+
if p.properties.UsePrebuilt {
if p.properties.SourceExists {
ctx.ReplaceDependenciesIf(name, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
+ if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
+ // Do not replace deps to the top-level prebuilt java_sdk_library hook.
+ // This hook has been special-cased in #isSelected to be _always_ active, even in next builds
+ // for dexpreopt and hiddenapi processing.
+ // If we do not special-case this here, rdeps referring to a java_sdk_library in next builds via libs
+ // will get prebuilt stubs
+ // TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
+ if psi.IsSelected(*sdkLibrary.SdkLibraryName()) {
+ return false
+ }
+ }
+
if t, ok := tag.(ReplaceSourceWithPrebuilt); ok {
return t.ReplaceSourceWithPrebuilt()
}
-
return true
})
}
@@ -582,15 +598,20 @@
func isSelected(psi PrebuiltSelectionInfoMap, m Module) bool {
if sdkLibrary, ok := m.(interface{ SdkLibraryName() *string }); ok && sdkLibrary.SdkLibraryName() != nil {
sln := proptools.String(sdkLibrary.SdkLibraryName())
+
// This is the top-level library
// Do not supersede the existing prebuilts vs source selection mechanisms
- if sln == m.base().BaseModuleName() {
+ // TODO (b/308187268): Remove this after the apexes have been added to apex_contributions
+ if bmn, ok := m.(baseModuleName); ok && sln == bmn.BaseModuleName() {
return false
}
// Stub library created by java_sdk_library_import
- if p := GetEmbeddedPrebuilt(m); p != nil {
- return psi.IsSelected(PrebuiltNameFromSource(sln))
+ // java_sdk_library creates several child modules (java_import + prebuilt_stubs_sources) dynamically.
+ // This code block ensures that these child modules are selected if the top-level java_sdk_library_import is listed
+ // in the selected apex_contributions.
+ if javaImport, ok := m.(createdByJavaSdkLibraryName); ok && javaImport.CreatedByJavaSdkLibraryName() != nil {
+ return psi.IsSelected(PrebuiltNameFromSource(proptools.String(javaImport.CreatedByJavaSdkLibraryName())))
}
// Stub library created by java_sdk_library
@@ -599,6 +620,11 @@
return psi.IsSelected(m.Name())
}
+// implemented by child modules of java_sdk_library_import
+type createdByJavaSdkLibraryName interface {
+ CreatedByJavaSdkLibraryName() *string
+}
+
// usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt
// will be used if it is marked "prefer" or if the source module is disabled.
func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool {
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e8dbd48..85e29bd 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -1157,11 +1157,15 @@
// OutputDir adds the output directory to the command line. This is only available when used with RuleBuilder.Sbox,
// and will be the temporary output directory managed by sbox, not the final one.
-func (c *RuleBuilderCommand) OutputDir() *RuleBuilderCommand {
+func (c *RuleBuilderCommand) OutputDir(subPathComponents ...string) *RuleBuilderCommand {
if !c.rule.sbox {
panic("OutputDir only valid with Sbox")
}
- return c.Text(sboxOutDir)
+ path := sboxOutDir
+ if len(subPathComponents) > 0 {
+ path = filepath.Join(append([]string{sboxOutDir}, subPathComponents...)...)
+ }
+ return c.Text(path)
}
// DepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles and adds it to the command
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 9355667..b2ff960 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -393,6 +393,7 @@
// Export the name of the soong modules representing the various Java API surfaces.
func javaSdkMakeVars(ctx MakeVarsContext) {
ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.DefaultJavaLibraryName())
+ ctx.Strict("ANDROID_PUBLIC_EXPORTABLE_STUBS", SdkPublic.DefaultExportableJavaLibraryName())
ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.DefaultJavaLibraryName())
ctx.Strict("ANDROID_TEST_STUBS", SdkTest.DefaultJavaLibraryName())
ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.DefaultJavaLibraryName())
diff --git a/android/singleton.go b/android/singleton.go
index e0e552e..ccddeaf 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -250,8 +250,8 @@
}
func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
- // get qualified module name for visibility enforcement
- qualified := createQualifiedModuleName(s.ModuleName(referer), s.ModuleDir(referer))
+ // get module reference for visibility enforcement
+ qualified := createVisibilityModuleReference(s.ModuleName(referer), s.ModuleDir(referer), s.ModuleType(referer))
modules := s.SingletonContext.ModuleVariantsFromName(referer, name)
result := make([]Module, 0, len(modules))
@@ -262,7 +262,7 @@
depDir := s.ModuleDir(module)
depQualified := qualifiedModuleName{depDir, depName}
// Targets are always visible to other targets in their own package.
- if depQualified.pkg != qualified.pkg {
+ if depQualified.pkg != qualified.name.pkg {
rule := effectiveVisibilityRules(s.Config(), depQualified)
if !rule.matches(qualified) {
s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility",
diff --git a/android/test_suites.go b/android/test_suites.go
index 9ded998..adcc15a 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -24,6 +24,7 @@
type testSuiteFiles struct {
robolectric WritablePath
+ ravenwood WritablePath
}
type TestSuiteModule interface {
@@ -47,12 +48,15 @@
})
t.robolectric = robolectricTestSuite(ctx, files["robolectric-tests"])
-
ctx.Phony("robolectric-tests", t.robolectric)
+
+ t.ravenwood = ravenwoodTestSuite(ctx, files["ravenwood-tests"])
+ ctx.Phony("ravenwood-tests", t.ravenwood)
}
func (t *testSuiteFiles) MakeVars(ctx MakeVarsContext) {
ctx.DistForGoal("robolectric-tests", t.robolectric)
+ ctx.DistForGoal("ravenwood-tests", t.ravenwood)
}
func robolectricTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
@@ -74,3 +78,23 @@
return outputFile
}
+
+func ravenwoodTestSuite(ctx SingletonContext, files map[string]InstallPaths) WritablePath {
+ var installedPaths InstallPaths
+ for _, module := range SortedKeys(files) {
+ installedPaths = append(installedPaths, files[module]...)
+ }
+ testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases")
+
+ outputFile := PathForOutput(ctx, "packaging", "ravenwood-tests.zip")
+ rule := NewRuleBuilder(pctx, ctx)
+ rule.Command().BuiltTool("soong_zip").
+ FlagWithOutput("-o ", outputFile).
+ FlagWithArg("-P ", "host/testcases").
+ FlagWithArg("-C ", testCasesDir.String()).
+ FlagWithRspFileInputList("-r ", outputFile.ReplaceExtension(ctx, "rsp"), installedPaths.Paths()).
+ Flag("-sha256")
+ rule.Build("ravenwood_tests_zip", "ravenwood-tests.zip")
+
+ return outputFile
+}
diff --git a/android/visibility.go b/android/visibility.go
index 3130135..b387562 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -57,12 +57,29 @@
var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
+type visibilityModuleReference struct {
+ name qualifiedModuleName
+ isPartitionModule bool
+}
+
+func createVisibilityModuleReference(name, dir, typ string) visibilityModuleReference {
+ isPartitionModule := false
+ switch typ {
+ case "android_filesystem", "android_system_image":
+ isPartitionModule = true
+ }
+ return visibilityModuleReference{
+ name: createQualifiedModuleName(name, dir),
+ isPartitionModule: isPartitionModule,
+ }
+}
+
// A visibility rule is associated with a module and determines which other modules it is visible
// to, i.e. which other modules can depend on the rule's module.
type visibilityRule interface {
// Check to see whether this rules matches m.
// Returns true if it does, false otherwise.
- matches(m qualifiedModuleName) bool
+ matches(m visibilityModuleReference) bool
String() string
}
@@ -108,8 +125,10 @@
// ["//visibility:private"].
type compositeRule []visibilityRule
+var _ visibilityRule = compositeRule{}
+
// A compositeRule matches if and only if any of its rules matches.
-func (c compositeRule) matches(m qualifiedModuleName) bool {
+func (c compositeRule) matches(m visibilityModuleReference) bool {
for _, r := range c {
if r.matches(m) {
return true
@@ -135,8 +154,10 @@
pkg string
}
-func (r packageRule) matches(m qualifiedModuleName) bool {
- return m.pkg == r.pkg
+var _ visibilityRule = packageRule{}
+
+func (r packageRule) matches(m visibilityModuleReference) bool {
+ return m.name.pkg == r.pkg
}
func (r packageRule) String() string {
@@ -149,8 +170,10 @@
pkgPrefix string
}
-func (r subpackagesRule) matches(m qualifiedModuleName) bool {
- return isAncestor(r.pkgPrefix, m.pkg)
+var _ visibilityRule = subpackagesRule{}
+
+func (r subpackagesRule) matches(m visibilityModuleReference) bool {
+ return isAncestor(r.pkgPrefix, m.name.pkg)
}
func isAncestor(p1 string, p2 string) bool {
@@ -168,7 +191,9 @@
// visibilityRule for //visibility:public
type publicRule struct{}
-func (r publicRule) matches(_ qualifiedModuleName) bool {
+var _ visibilityRule = publicRule{}
+
+func (r publicRule) matches(_ visibilityModuleReference) bool {
return true
}
@@ -179,7 +204,9 @@
// visibilityRule for //visibility:private
type privateRule struct{}
-func (r privateRule) matches(_ qualifiedModuleName) bool {
+var _ visibilityRule = privateRule{}
+
+func (r privateRule) matches(_ visibilityModuleReference) bool {
return false
}
@@ -187,6 +214,19 @@
return "//visibility:private"
}
+// visibilityRule for //visibility:any_partition
+type anyPartitionRule struct{}
+
+var _ visibilityRule = anyPartitionRule{}
+
+func (r anyPartitionRule) matches(m visibilityModuleReference) bool {
+ return m.isPartitionModule
+}
+
+func (r anyPartitionRule) String() string {
+ return "//visibility:any_partition"
+}
+
var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
// The map from qualifiedModuleName to visibilityRule.
@@ -237,13 +277,10 @@
// Checks the per-module visibility rule lists before defaults expansion.
func visibilityRuleChecker(ctx BottomUpMutatorContext) {
- qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
- if m, ok := ctx.Module().(Module); ok {
- visibilityProperties := m.visibilityProperties()
- for _, p := range visibilityProperties {
- if visibility := p.getStrings(); visibility != nil {
- checkRules(ctx, qualified.pkg, p.getName(), visibility)
- }
+ visibilityProperties := ctx.Module().visibilityProperties()
+ for _, p := range visibilityProperties {
+ if visibility := p.getStrings(); visibility != nil {
+ checkRules(ctx, ctx.ModuleDir(), p.getName(), visibility)
}
}
}
@@ -266,7 +303,7 @@
if pkg == "visibility" {
switch name {
- case "private", "public":
+ case "private", "public", "any_partition":
case "legacy_public":
ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
continue
@@ -305,10 +342,7 @@
//
// See ../README.md#Visibility for information on the format of the visibility rules.
func visibilityRuleGatherer(ctx BottomUpMutatorContext) {
- m, ok := ctx.Module().(Module)
- if !ok {
- return
- }
+ m := ctx.Module()
qualifiedModuleId := m.qualifiedModuleId(ctx)
currentPkg := qualifiedModuleId.pkg
@@ -355,6 +389,8 @@
hasNonPrivateRule = false
// This does not actually create a rule so continue onto the next rule.
continue
+ case "any_partition":
+ r = anyPartitionRule{}
}
} else {
switch name {
@@ -395,10 +431,7 @@
func isAllowedFromOutsideVendor(pkg string, name string) bool {
if pkg == "vendor" {
- if name == "__subpackages__" {
- return true
- }
- return false
+ return name == "__subpackages__"
}
return !isAncestor("vendor", pkg)
@@ -434,11 +467,7 @@
}
func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
- if _, ok := ctx.Module().(Module); !ok {
- return
- }
-
- qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir())
+ qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.ModuleType())
// Visit all the dependencies making sure that this module has access to them all.
ctx.VisitDirectDeps(func(dep Module) {
@@ -453,7 +482,7 @@
depQualified := qualifiedModuleName{depDir, depName}
// Targets are always visible to other targets in their own package.
- if depQualified.pkg == qualified.pkg {
+ if depQualified.pkg == qualified.name.pkg {
return
}
@@ -478,7 +507,7 @@
if ok {
rule = value.(compositeRule)
} else {
- rule = packageDefaultVisibility(config, qualified)
+ rule = packageDefaultVisibility(moduleToVisibilityRule, qualified)
}
// If no rule is specified then return the default visibility rule to avoid
@@ -494,8 +523,7 @@
return qualified
}
-func packageDefaultVisibility(config Config, moduleId qualifiedModuleName) compositeRule {
- moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
+func packageDefaultVisibility(moduleToVisibilityRule *sync.Map, moduleId qualifiedModuleName) compositeRule {
packageQualifiedId := moduleId.getContainingPackageId()
for {
value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
@@ -574,10 +602,12 @@
rule := effectiveVisibilityRules(ctx.Config(), qualified)
+ currentModule := createVisibilityModuleReference(moduleName, dir, ctx.OtherModuleType(module))
+
// Modules are implicitly visible to other modules in the same package,
// without checking the visibility rules. Here we need to add that visibility
// explicitly.
- if !rule.matches(qualified) {
+ if !rule.matches(currentModule) {
if len(rule) == 1 {
if _, ok := rule[0].(privateRule); ok {
// If the rule is //visibility:private we can't append another
diff --git a/android/visibility_test.go b/android/visibility_test.go
index a66f0b6..d4add7d 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1904,6 +1904,38 @@
}`),
},
},
+ {
+ name: "any_partition visibility works",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ android_filesystem {
+ name: "foo",
+ deps: ["bar"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ package(default_visibility=["//visibility:private"])
+ mock_library {
+ name: "bar",
+ visibility: ["//visibility:any_partition"],
+ }`),
+ },
+ },
+ {
+ name: "any_partition visibility doesn't work for non-partitions",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ mock_library {
+ name: "foo",
+ deps: ["bar"],
+ }`),
+ "top/nested/Android.bp": []byte(`
+ mock_library {
+ name: "bar",
+ visibility: ["//visibility:any_partition"],
+ }`),
+ },
+ expectedErrors: []string{`module "foo" variant "android_common": depends on //top/nested:bar which is not visible to this module`},
+ },
}
func TestVisibility(t *testing.T) {
@@ -1925,6 +1957,8 @@
ctx.RegisterModuleType("mock_library", newMockLibraryModule)
ctx.RegisterModuleType("mock_parent", newMockParentFactory)
ctx.RegisterModuleType("mock_defaults", defaultsFactory)
+ // For testing //visibility:any_partition. The module type doesn't matter, just that it's registered under the name "android_filesystem"
+ ctx.RegisterModuleType("android_filesystem", newMockLibraryModule)
}),
prepareForTestWithFakePrebuiltModules,
// Add additional files to the mock filesystem
diff --git a/apex/builder.go b/apex/builder.go
index 3078863..40ccd2c 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -910,7 +910,7 @@
var validations android.Paths
validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile.OutputPath, imageDir.OutputPath))
// TODO(b/279688635) deapexer supports [ext4]
- if suffix == imageApexSuffix && ext4 == a.payloadFsType {
+ if !a.testApex && suffix == imageApexSuffix && ext4 == a.payloadFsType {
validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
}
if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 399d9b9..cebbae9 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -639,7 +639,7 @@
return false
}
- name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(child))
+ name := java.ModuleStemForDeapexing(child)
if _, ok := tag.(android.RequiresFilesFromPrebuiltApexTag); ok {
commonModules = append(commonModules, name)
diff --git a/bin/soongdbg b/bin/soongdbg
index 132a0f0..bfdbbde 100755
--- a/bin/soongdbg
+++ b/bin/soongdbg
@@ -2,9 +2,12 @@
import argparse
import fnmatch
+import html
+import io
import json
import os
import pathlib
+import subprocess
import types
import sys
@@ -27,6 +30,7 @@
dep = get_or_make_node(self.nodes, d.id, None)
node.deps.add(dep)
dep.rdeps.add(node)
+ node.dep_tags.setdefault(dep, list()).append(d)
def find_paths(self, id1, id2):
# Throws KeyError if one of the names isn't found
@@ -60,6 +64,7 @@
self.module = module
self.deps = set()
self.rdeps = set()
+ self.dep_tags = {}
PROVIDERS = [
@@ -68,21 +73,35 @@
]
-def format_node_label(node):
- if not node.module:
- return node.id
- if node.module.debug:
- module_debug = f"<tr><td>{node.module.debug}</td></tr>"
- else:
- module_debug = ""
+def format_dep_label(node, dep):
+ tags = node.dep_tags.get(dep)
+ labels = []
+ if tags:
+ labels = [tag.tag_type.split("/")[-1] for tag in tags]
+ labels = sorted(set(labels))
+ if labels:
+ result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
+ for label in labels:
+ result += f"<tr><td>{label}</td></tr>"
+ result += "</table>>"
+ return result
- result = (f"<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
- + f"<tr><td><b>{node.module.name}</b></td></tr>"
- + module_debug
- + f"<tr><td>{node.module.type}</td></tr>")
- for p in node.module.providers:
- if p.type in PROVIDERS:
- result += "<tr><td><font color=\"#666666\">" + format_provider(p) + "</font></td></tr>"
+
+def format_node_label(node, module_formatter):
+ result = "<<table border=\"0\" cellborder=\"0\" cellspacing=\"0\" cellpadding=\"0\">"
+
+ # node name
+ result += f"<tr><td><b>{node.module.name if node.module else node.id}</b></td></tr>"
+
+ if node.module:
+ # node_type
+ result += f"<tr><td>{node.module.type}</td></tr>"
+
+ # module_formatter will return a list of rows
+ for row in module_formatter(node.module):
+ row = html.escape(row)
+ result += f"<tr><td><font color=\"#666666\">{row}</font></td></tr>"
+
result += "</table>>"
return result
@@ -190,22 +209,87 @@
yield m
-def print_nodes(nodes):
- print("digraph {")
+def print_args(parser):
+ parser.add_argument("--label", action="append", metavar="JQ_FILTER",
+ help="jq query for each module metadata")
+ parser.add_argument("--deptags", action="store_true",
+ help="show dependency tags (makes the graph much more complex)")
+
+ group = parser.add_argument_group("output formats",
+ "If no format is provided, a dot file will be written to"
+ + " stdout.")
+ output = group.add_mutually_exclusive_group()
+ output.add_argument("--dot", type=str, metavar="FILENAME",
+ help="Write the graph to this file as dot (graphviz format)")
+ output.add_argument("--svg", type=str, metavar="FILENAME",
+ help="Write the graph to this file as svg")
+
+
+def print_nodes(args, nodes, module_formatter):
+ # Generate the graphviz
+ dep_tag_id = 0
+ dot = io.StringIO()
+ dot.write("digraph {\n")
+ dot.write("node [shape=box];")
+
for node in nodes:
- print(f"\"{node.id}\"[label={format_node_label(node)}];")
+ dot.write(f"\"{node.id}\" [label={format_node_label(node, module_formatter)}];\n")
for dep in node.deps:
if dep in nodes:
- print(f"\"{node.id}\" -> \"{dep.id}\";")
- print("}")
+ if args.deptags:
+ dot.write(f"\"{node.id}\" -> \"__dep_tag_{dep_tag_id}\" [ arrowhead=none ];\n")
+ dot.write(f"\"__dep_tag_{dep_tag_id}\" -> \"{dep.id}\";\n")
+ dot.write(f"\"__dep_tag_{dep_tag_id}\""
+ + f"[label={format_dep_label(node, dep)} shape=ellipse"
+ + " color=\"#666666\" fontcolor=\"#666666\"];\n")
+ else:
+ dot.write(f"\"{node.id}\" -> \"{dep.id}\";\n")
+ dep_tag_id += 1
+ dot.write("}\n")
+ text = dot.getvalue()
+
+ # Write it somewhere
+ if args.dot:
+ with open(args.dot, "w") as f:
+ f.write(text)
+ elif args.svg:
+ subprocess.run(["dot", "-Tsvg", "-o", args.svg],
+ input=text, text=True, check=True)
+ else:
+ sys.stdout.write(text)
-def get_deps(nodes, root):
+def get_deps(nodes, root, maxdepth, reverse):
if root in nodes:
return
nodes.add(root)
- for dep in root.deps:
- get_deps(nodes, dep)
+ if maxdepth != 0:
+ for dep in (root.rdeps if reverse else root.deps):
+ get_deps(nodes, dep, maxdepth-1, reverse)
+
+
+def new_module_formatter(args):
+ def module_formatter(module):
+ if not args.label:
+ return []
+ result = []
+ text = json.dumps(module, default=lambda o: o.__dict__)
+ for jq_filter in args.label:
+ proc = subprocess.run(["jq", jq_filter],
+ input=text, text=True, check=True, stdout=subprocess.PIPE)
+ if proc.stdout:
+ o = json.loads(proc.stdout)
+ if type(o) == list:
+ for row in o:
+ if row:
+ result.append(row)
+ elif type(o) == dict:
+ result.append(str(proc.stdout).strip())
+ else:
+ if o:
+ result.append(str(o).strip())
+ return result
+ return module_formatter
class BetweenCommand:
@@ -213,11 +297,13 @@
def args(self, parser):
parser.add_argument("module", nargs=2,
- help="The two modules")
+ help="the two modules")
+ print_args(parser)
def run(self, args):
graph = load_graph()
- print_nodes(graph.find_paths(args.module[0], args.module[1]))
+ print_nodes(args, graph.find_paths(args.module[0], args.module[1]),
+ new_module_formatter(args))
class DepsCommand:
@@ -226,21 +312,26 @@
def args(self, parser):
parser.add_argument("module", nargs="+",
help="Module to print dependencies of")
+ parser.add_argument("--reverse", action="store_true",
+ help="traverse reverse dependencies")
+ parser.add_argument("--depth", type=int, default=-1,
+ help="max depth of dependencies (can keep the graph size reasonable)")
+ print_args(parser)
def run(self, args):
graph = load_graph()
nodes = set()
err = False
- for id in sys.argv[3:]:
+ for id in args.module:
root = graph.nodes.get(id)
if not root:
sys.stderr.write(f"error: Can't find root: {id}\n")
err = True
continue
- get_deps(nodes, root)
+ get_deps(nodes, root, args.depth, args.reverse)
if err:
sys.exit(1)
- print_nodes(nodes)
+ print_nodes(args, nodes, new_module_formatter(args))
class IdCommand:
@@ -254,6 +345,25 @@
print(m.id)
+class JsonCommand:
+ help = "Print metadata about modules in json format"
+
+ def args(self, parser):
+ module_selection_args(parser)
+ parser.add_argument("--list", action="store_true",
+ help="Print the results in a json list. If not set and multiple"
+ + " modules are matched, the output won't be valid json.")
+
+ def run(self, args):
+ modules = load_and_filter_modules(args)
+ if args.list:
+ json.dump([m for m in modules], sys.stdout, indent=4, default=lambda o: o.__dict__)
+ else:
+ for m in modules:
+ json.dump(m, sys.stdout, indent=4, default=lambda o: o.__dict__)
+ print()
+
+
class QueryCommand:
help = "Query details about modules"
@@ -275,6 +385,7 @@
"between": BetweenCommand(),
"deps": DepsCommand(),
"id": IdCommand(),
+ "json": JsonCommand(),
"query": QueryCommand(),
}
diff --git a/cc/afdo.go b/cc/afdo.go
index 79fbae1..00b2245 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -21,29 +21,17 @@
"android/soong/android"
"github.com/google/blueprint"
- "github.com/google/blueprint/proptools"
)
// This flag needs to be in both CFlags and LdFlags to ensure correct symbol ordering
const afdoFlagsFormat = "-fprofile-sample-use=%s -fprofile-sample-accurate"
-func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
- getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
-}
-
-type afdoRdep struct {
- VariationName *string
- ProfilePath *string
-}
-
type AfdoProperties struct {
// Afdo allows developers self-service enroll for
// automatic feedback-directed optimization using profile data.
Afdo bool
- FdoProfilePath *string `blueprint:"mutated"`
-
- AfdoRDeps []afdoRdep `blueprint:"mutated"`
+ AfdoDep bool `blueprint:"mutated"`
}
type afdo struct {
@@ -62,13 +50,32 @@
}
// afdoEnabled returns true for binaries and shared libraries
-// that set afdo prop to True and there is a profile available
+// that set afdo prop to True.
func (afdo *afdo) afdoEnabled() bool {
return afdo != nil && afdo.Properties.Afdo
}
+func (afdo *afdo) isAfdoCompile(ctx ModuleContext) bool {
+ fdoProfilePath := getFdoProfilePathFromDep(ctx)
+ return !ctx.Host() && (afdo.Properties.Afdo || afdo.Properties.AfdoDep) && (fdoProfilePath != "")
+}
+
+func getFdoProfilePathFromDep(ctx ModuleContext) string {
+ fdoProfileDeps := ctx.GetDirectDepsWithTag(FdoProfileTag)
+ if len(fdoProfileDeps) > 0 && fdoProfileDeps[0] != nil {
+ if info, ok := android.OtherModuleProvider(ctx, fdoProfileDeps[0], FdoProfileProvider); ok {
+ return info.Path.String()
+ }
+ }
+ return ""
+}
+
func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
- if afdo.Properties.Afdo {
+ if ctx.Host() {
+ return flags
+ }
+
+ if afdo.Properties.Afdo || afdo.Properties.AfdoDep {
// We use `-funique-internal-linkage-names` to associate profiles to the right internal
// functions. This option should be used before generating a profile. Because a profile
// generated for a binary without unique names doesn't work well building a binary with
@@ -86,15 +93,15 @@
// TODO(b/266595187): Remove the following feature once it is enabled in LLVM by default.
flags.Local.CFlags = append([]string{"-mllvm", "-improved-fs-discriminator=true"}, flags.Local.CFlags...)
}
- if path := afdo.Properties.FdoProfilePath; path != nil {
+ if fdoProfilePath := getFdoProfilePathFromDep(ctx); fdoProfilePath != "" {
// The flags are prepended to allow overriding.
- profileUseFlag := fmt.Sprintf(afdoFlagsFormat, *path)
+ profileUseFlag := fmt.Sprintf(afdoFlagsFormat, fdoProfilePath)
flags.Local.CFlags = append([]string{profileUseFlag}, flags.Local.CFlags...)
flags.Local.LdFlags = append([]string{profileUseFlag, "-Wl,-mllvm,-no-warn-sample-unused=true"}, flags.Local.LdFlags...)
// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
// if profileFile gets updated
- pathForSrc := android.PathForSource(ctx, *path)
+ pathForSrc := android.PathForSource(ctx, fdoProfilePath)
flags.CFlagsDeps = append(flags.CFlagsDeps, pathForSrc)
flags.LdFlagsDeps = append(flags.LdFlagsDeps, pathForSrc)
}
@@ -102,122 +109,86 @@
return flags
}
-func (afdo *afdo) addDep(ctx BaseModuleContext, actx android.BottomUpMutatorContext) {
+func (a *afdo) addDep(ctx android.BottomUpMutatorContext, fdoProfileTarget string) {
+ if fdoProfileName, err := ctx.DeviceConfig().AfdoProfile(fdoProfileTarget); fdoProfileName != "" && err == nil {
+ ctx.AddFarVariationDependencies(
+ []blueprint.Variation{
+ {Mutator: "arch", Variation: ctx.Target().ArchVariation()},
+ {Mutator: "os", Variation: "android"},
+ },
+ FdoProfileTag,
+ fdoProfileName)
+ }
+}
+
+func afdoPropagateViaDepTag(tag blueprint.DependencyTag) bool {
+ libTag, isLibTag := tag.(libraryDependencyTag)
+ // Do not recurse down non-static dependencies
+ if isLibTag {
+ return libTag.static()
+ } else {
+ return tag == objDepTag || tag == reuseObjTag || tag == staticVariantTag
+ }
+}
+
+// afdoTransitionMutator creates afdo variants of cc modules.
+type afdoTransitionMutator struct{}
+
+func (a *afdoTransitionMutator) Split(ctx android.BaseModuleContext) []string {
+ return []string{""}
+}
+
+func (a *afdoTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
if ctx.Host() {
- return
+ return ""
}
- if ctx.static() && !ctx.staticBinary() {
- return
- }
-
- if c, ok := ctx.Module().(*Module); ok && c.Enabled() {
- if fdoProfileName, err := actx.DeviceConfig().AfdoProfile(actx.ModuleName()); fdoProfileName != nil && err == nil {
- actx.AddFarVariationDependencies(
- []blueprint.Variation{
- {Mutator: "arch", Variation: actx.Target().ArchVariation()},
- {Mutator: "os", Variation: "android"},
- },
- FdoProfileTag,
- []string{*fdoProfileName}...,
- )
+ if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+ if !afdoPropagateViaDepTag(ctx.DepTag()) {
+ return ""
}
+
+ if sourceVariation != "" {
+ return sourceVariation
+ }
+
+ if !m.afdo.afdoEnabled() {
+ return ""
+ }
+
+ // TODO(b/324141705): this is designed to prevent propagating AFDO from static libraries that have afdo: true set, but
+ // it should be m.static() && !m.staticBinary() so that static binaries use AFDO variants of dependencies.
+ if m.static() {
+ return ""
+ }
+
+ return encodeTarget(ctx.Module().Name())
}
+ return ""
}
-// FdoProfileMutator reads the FdoProfileProvider from a direct dep with FdoProfileTag
-// assigns FdoProfileInfo.Path to the FdoProfilePath mutated property
-func (c *Module) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
- if !c.Enabled() {
- return
+func (a *afdoTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
+ if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+ return incomingVariation
}
-
- if !c.afdo.afdoEnabled() {
- return
- }
-
- ctx.VisitDirectDepsWithTag(FdoProfileTag, func(m android.Module) {
- if info, ok := android.OtherModuleProvider(ctx, m, FdoProfileProvider); ok {
- c.afdo.Properties.FdoProfilePath = proptools.StringPtr(info.Path.String())
- }
- })
+ return ""
}
-var _ FdoProfileMutatorInterface = (*Module)(nil)
-
-// Propagate afdo requirements down from binaries and shared libraries
-func afdoDepsMutator(mctx android.TopDownMutatorContext) {
- if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
- path := m.afdo.Properties.FdoProfilePath
- mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
- tag := mctx.OtherModuleDependencyTag(dep)
- libTag, isLibTag := tag.(libraryDependencyTag)
-
- // Do not recurse down non-static dependencies
- if isLibTag {
- if !libTag.static() {
- return false
- }
- } else {
- if tag != objDepTag && tag != reuseObjTag {
- return false
- }
+func (a *afdoTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
+ if m, ok := ctx.Module().(*Module); ok && m.afdo != nil {
+ if variation == "" {
+ // The empty variation is either a module that has enabled AFDO for itself, or the non-AFDO
+ // variant of a dependency.
+ if m.afdo.afdoEnabled() && !(m.static() && !m.staticBinary()) && !m.Host() {
+ m.afdo.addDep(ctx, ctx.ModuleName())
}
-
- if dep, ok := dep.(*Module); ok {
- dep.afdo.Properties.AfdoRDeps = append(
- dep.afdo.Properties.AfdoRDeps,
- afdoRdep{
- VariationName: proptools.StringPtr(encodeTarget(m.Name())),
- ProfilePath: path,
- },
- )
- }
-
- return true
- })
- }
-}
-
-// Create afdo variants for modules that need them
-func afdoMutator(mctx android.BottomUpMutatorContext) {
- if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
- if !m.static() && m.afdo.Properties.Afdo {
- mctx.SetDependencyVariation(encodeTarget(m.Name()))
- return
- }
-
- variationNames := []string{""}
-
- variantNameToProfilePath := make(map[string]*string)
-
- for _, afdoRDep := range m.afdo.Properties.AfdoRDeps {
- variantName := *afdoRDep.VariationName
- // An rdep can be set twice in AfdoRDeps because there can be
- // more than one path from an afdo-enabled module to
- // a static dep such as
- // afdo_enabled_foo -> static_bar ----> static_baz
- // \ ^
- // ----------------------|
- // We only need to create one variant per unique rdep
- if _, exists := variantNameToProfilePath[variantName]; !exists {
- variationNames = append(variationNames, variantName)
- variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
- }
- }
-
- if len(variationNames) > 1 {
- modules := mctx.CreateVariations(variationNames...)
- for i, name := range variationNames {
- if name == "" {
- continue
- }
- variation := modules[i].(*Module)
- variation.Properties.PreventInstall = true
- variation.Properties.HideFromMake = true
- variation.afdo.Properties.Afdo = true
- variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
- }
+ } else {
+ // The non-empty variation is the AFDO variant of a dependency of a module that enabled AFDO
+ // for itself.
+ m.Properties.PreventInstall = true
+ m.Properties.HideFromMake = true
+ m.afdo.Properties.AfdoDep = true
+ m.afdo.addDep(ctx, decodeTarget(variation))
}
}
}
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
index b250ad1..0679d13 100644
--- a/cc/afdo_test.go
+++ b/cc/afdo_test.go
@@ -15,6 +15,7 @@
package cc
import (
+ "runtime"
"strings"
"testing"
@@ -42,19 +43,25 @@
bp := `
cc_library_shared {
name: "libTest",
+ host_supported: true,
srcs: ["test.c"],
static_libs: ["libFoo"],
afdo: true,
+ lto: {
+ thin: true,
+ },
}
cc_library_static {
name: "libFoo",
+ host_supported: true,
srcs: ["foo.c"],
static_libs: ["libBar"],
}
cc_library_static {
name: "libBar",
+ host_supported: true,
srcs: ["bar.c"],
}
`
@@ -72,13 +79,20 @@
"afdo_profiles_package/Android.bp": []byte(`
fdo_profile {
name: "libTest_afdo",
- profile: "libTest.afdo",
+ arch: {
+ arm64: {
+ profile: "libTest.afdo",
+ },
+ },
}
`),
}.AddToFixture(),
).RunTestWithBp(t, bp)
- expectedCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+ profileSampleCFlag := "-fprofile-sample-use=afdo_profiles_package/libTest.afdo"
+ uniqueInternalLinkageNamesCFlag := "-funique-internal-linkage-names"
+ afdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=40"
+ noAfdoLtoLdFlag := "-Wl,-plugin-opt,-import-instr-limit=5"
libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
@@ -86,18 +100,32 @@
// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
cFlags := libTest.Rule("cc").Args["cFlags"]
- if !strings.Contains(cFlags, expectedCFlag) {
- t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ if !strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected 'libTest' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+
+ ldFlags := libTest.Rule("ld").Args["ldFlags"]
+ if !strings.Contains(ldFlags, afdoLtoLdFlag) {
+ t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in ldflags %q", afdoLtoLdFlag, ldFlags)
}
cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
- if !strings.Contains(cFlags, expectedCFlag) {
- t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ if !strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected 'libFooAfdoVariant' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
}
cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
- if !strings.Contains(cFlags, expectedCFlag) {
- t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+ if !strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected 'libBarAfdoVariant' to enable afdo profile, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", profileSampleCFlag, cFlags)
}
// Check dependency edge from afdo-enabled module to static deps
@@ -114,12 +142,18 @@
libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
cFlags = libFoo.Rule("cc").Args["cFlags"]
- if strings.Contains(cFlags, expectedCFlag) {
- t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected 'libFoo' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+ if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
}
cFlags = libBar.Rule("cc").Args["cFlags"]
- if strings.Contains(cFlags, expectedCFlag) {
- t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected 'libBar' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+ if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
}
// Check dependency edges of static deps
@@ -130,6 +164,104 @@
if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
}
+
+ // Verify that the arm variant does not have FDO since the fdo_profile module only has a profile for arm64
+ libTest32 := result.ModuleForTests("libTest", "android_arm_armv7-a-neon_shared")
+ libFooAfdoVariant32 := result.ModuleForTests("libFoo", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+ libBarAfdoVariant32 := result.ModuleForTests("libBar", "android_arm_armv7-a-neon_static_afdo-libTest_lto-thin")
+
+ cFlags = libTest32.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected arm32 'libTest' not to enable afdo, but found %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+
+ // TODO(b/324141705): when the fdo_profile module doesn't provide a source file the dependencies don't get
+ // -funique-internal-linkage-names but the module does.
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected arm32 'libTest' to enable -funique-internal-linkage-names but did not find %q in cflags %q",
+ uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+
+ ldFlags = libTest32.Rule("ld").Args["ldFlags"]
+ if !strings.Contains(ldFlags, noAfdoLtoLdFlag) {
+ t.Errorf("Expected arm32 'libTest' to not enable afdo, but did not find %q in ldflags %q", noAfdoLtoLdFlag, ldFlags)
+ }
+ if strings.Contains(ldFlags, afdoLtoLdFlag) {
+ t.Errorf("Expected arm32 'libTest' to not enable afdo, but found %q in ldflags %q", afdoLtoLdFlag, ldFlags)
+ }
+
+ // Check dependency edge from afdo-enabled module to static deps
+ if !hasDirectDep(result, libTest32.Module(), libFooAfdoVariant32.Module()) {
+ t.Errorf("arm32 libTest missing dependency on afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFooAfdoVariant32.Module(), libBarAfdoVariant32.Module()) {
+ t.Errorf("arm32 libTest missing dependency on afdo variant of libBar")
+ }
+
+ cFlags = libFooAfdoVariant32.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected arm32 'libFoo' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected arm32 'libFoo' to enable afdo, but did not find %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ cFlags = libBarAfdoVariant32.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected arm32 'libBar' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ if !strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected arm32 'libBar' to enable afdo, but did not find %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+
+ // Verify that the host variants don't enable afdo
+ libTestHost := result.ModuleForTests("libTest", result.Config.BuildOSTarget.String()+"_shared")
+ libFooHost := result.ModuleForTests("libFoo", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+ libBarHost := result.ModuleForTests("libBar", result.Config.BuildOSTarget.String()+"_static_lto-thin")
+
+ cFlags = libTestHost.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected host 'libTest' to not enable afdo profile, but found %q in cflags %q", profileSampleCFlag, cFlags)
+ }
+
+ if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected host 'libTest' to not enable afdo but found %q in cflags %q",
+ uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+
+ if runtime.GOOS != "darwin" {
+ ldFlags := libTestHost.Rule("ld").Args["ldFlags"]
+ if !strings.Contains(ldFlags, noAfdoLtoLdFlag) {
+ t.Errorf("Expected host 'libTest' to not enable afdo, but did not find %q in ldflags %q", noAfdoLtoLdFlag, ldFlags)
+ }
+ if strings.Contains(ldFlags, afdoLtoLdFlag) {
+ t.Errorf("Expected host 'libTest' to not enable afdo, but found %q in ldflags %q", afdoLtoLdFlag, ldFlags)
+ }
+ }
+
+ // Check dependency edge from afdo-enabled module to static deps
+ if !hasDirectDep(result, libTestHost.Module(), libFooHost.Module()) {
+ t.Errorf("host libTest missing dependency on non-afdo variant of libFoo")
+ }
+
+ if !hasDirectDep(result, libFooHost.Module(), libBarHost.Module()) {
+ t.Errorf("host libTest missing dependency on non-afdo variant of libBar")
+ }
+
+ cFlags = libFooHost.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected host 'libFoo' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected host 'libFoo' to not enable afdo, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ cFlags = libBarHost.Rule("cc").Args["cFlags"]
+ if strings.Contains(cFlags, profileSampleCFlag) {
+ t.Errorf("Expected host 'libBar' to not enable afdo profile, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
+ if strings.Contains(cFlags, uniqueInternalLinkageNamesCFlag) {
+ t.Errorf("Expected host 'libBar' to not enable afdo, but found %q in cflags %q", uniqueInternalLinkageNamesCFlag, cFlags)
+ }
}
func TestAfdoEnabledOnStaticDepNoAfdo(t *testing.T) {
@@ -174,11 +306,11 @@
libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static").Module()
if !hasDirectDep(result, libTest, libFoo.Module()) {
- t.Errorf("libTest missing dependency on afdo variant of libFoo")
+ t.Errorf("libTest missing dependency on non-afdo variant of libFoo")
}
if !hasDirectDep(result, libFoo.Module(), libBar) {
- t.Errorf("libFoo missing dependency on afdo variant of libBar")
+ t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
}
fooVariants := result.ModuleVariantsForTests("foo")
diff --git a/cc/cc.go b/cc/cc.go
index 449d38f..39024aa 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -55,7 +55,6 @@
ctx.BottomUp("test_per_src", TestPerSrcMutator).Parallel()
ctx.BottomUp("version", versionMutator).Parallel()
ctx.BottomUp("begin", BeginMutator).Parallel()
- ctx.BottomUp("fdo_profile", fdoProfileMutator)
})
ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -70,8 +69,7 @@
ctx.Transition("coverage", &coverageTransitionMutator{})
- ctx.TopDown("afdo_deps", afdoDepsMutator)
- ctx.BottomUp("afdo", afdoMutator).Parallel()
+ ctx.Transition("afdo", &afdoTransitionMutator{})
ctx.Transition("orderfile", &orderfileTransitionMutator{})
@@ -527,7 +525,7 @@
selectedStl() string
baseModuleName() string
getVndkExtendsModuleName() string
- isAfdoCompile() bool
+ isAfdoCompile(ctx ModuleContext) bool
isOrderfileCompile() bool
isCfi() bool
isFuzzer() bool
@@ -1381,9 +1379,9 @@
return false
}
-func (c *Module) isAfdoCompile() bool {
+func (c *Module) isAfdoCompile(ctx ModuleContext) bool {
if afdo := c.afdo; afdo != nil {
- return afdo.Properties.FdoProfilePath != nil
+ return afdo.isAfdoCompile(ctx)
}
return false
}
@@ -1706,8 +1704,8 @@
return ctx.mod.IsVndk()
}
-func (ctx *moduleContextImpl) isAfdoCompile() bool {
- return ctx.mod.isAfdoCompile()
+func (ctx *moduleContextImpl) isAfdoCompile(mctx ModuleContext) bool {
+ return ctx.mod.isAfdoCompile(mctx)
}
func (ctx *moduleContextImpl) isOrderfileCompile() bool {
@@ -2351,10 +2349,6 @@
}
ctx.ctx = ctx
- if !actx.Host() || !ctx.static() || ctx.staticBinary() {
- c.afdo.addDep(ctx, actx)
- }
-
c.begin(ctx)
}
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index b789590..47c61b0 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -29,11 +29,6 @@
"-fPIC",
"-funwind-tables",
- // Workaround differences in inttypes.h between host and target.
- //See bug 12708004.
- "-D__STDC_FORMAT_MACROS",
- "-D__STDC_CONSTANT_MACROS",
-
"-isysroot ${macSdkRoot}",
"-mmacosx-version-min=${macMinVersion}",
"-DMACOSX_DEPLOYMENT_TARGET=${macMinVersion}",
diff --git a/cc/config/global.go b/cc/config/global.go
index c562614..d3c2658 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -254,7 +254,7 @@
"-Wno-pointer-to-int-cast",
"-Werror=fortify-source",
// http://b/315246135 temporarily disabled
- "-Wno-error=unused-variable",
+ "-Wno-unused-variable",
// http://b/315250603 temporarily disabled
"-Wno-error=format",
// Disabled because it produces many false positives. http://b/323050926
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index f497bf9..9bc54d6 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -30,11 +30,6 @@
"-D_FORTIFY_SOURCE=2",
"-fstack-protector",
- // Workaround differences in inttypes.h between host and target.
- //See bug 12708004.
- "-D__STDC_FORMAT_MACROS",
- "-D__STDC_CONSTANT_MACROS",
-
"--gcc-toolchain=${LinuxGccRoot}",
"-fstack-protector-strong",
}
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 561c500..1e61b01 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -27,9 +27,8 @@
"-DWIN32_LEAN_AND_MEAN",
"-Wno-unused-parameter",
- // Workaround differences in inttypes.h between host and target.
- //See bug 12708004.
- "-D__STDC_FORMAT_MACROS",
+ // Workaround differences in <stdint.h> between host and target.
+ // Context: http://b/12708004
"-D__STDC_CONSTANT_MACROS",
// Use C99-compliant printf functions (%zd).
diff --git a/cc/coverage.go b/cc/coverage.go
index 43f5e07..f6092e4 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -91,7 +91,7 @@
}
func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps {
- if cov.Properties.NeedCoverageVariant {
+ if cov.Properties.NeedCoverageVariant && ctx.Device() {
ctx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, CoverageDepTag, getGcovProfileLibraryName(ctx))
@@ -184,19 +184,22 @@
if gcovCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
- coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
- deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
-
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+ if ctx.Device() {
+ coverage := ctx.GetDirectDepWithTag(getGcovProfileLibraryName(ctx), CoverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
+ }
} else if clangCoverage {
flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
if EnableContinuousCoverage(ctx) {
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
}
- coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
- deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
- flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
+ if ctx.Device() {
+ coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
+ deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
+ flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,open")
+ }
}
}
@@ -204,7 +207,7 @@
}
func (cov *coverage) begin(ctx BaseModuleContext) {
- if ctx.Host() {
+ if ctx.Host() && !ctx.Os().Linux() {
// TODO(dwillemsen): because of -nodefaultlibs, we must depend on libclang_rt.profile-*.a
// Just turn off for now.
} else {
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
index 0893da5..1a33957 100644
--- a/cc/fdo_profile.go
+++ b/cc/fdo_profile.go
@@ -43,23 +43,10 @@
}
// FdoProfileProvider is used to provide path to an fdo profile
-var FdoProfileProvider = blueprint.NewMutatorProvider[FdoProfileInfo]("fdo_profile")
-
-// FdoProfileMutatorInterface is the interface implemented by fdo_profile module type
-// module types that can depend on an fdo_profile module
-type FdoProfileMutatorInterface interface {
- // FdoProfileMutator eithers set or get FdoProfileProvider
- fdoProfileMutator(ctx android.BottomUpMutatorContext)
-}
-
-var _ FdoProfileMutatorInterface = (*fdoProfile)(nil)
+var FdoProfileProvider = blueprint.NewProvider[FdoProfileInfo]()
// GenerateAndroidBuildActions of fdo_profile does not have any build actions
-func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
-// FdoProfileMutator sets FdoProfileProvider to fdo_profile module
-// or sets afdo.Properties.FdoProfilePath to path in FdoProfileProvider of the depended fdo_profile
-func (fp *fdoProfile) fdoProfileMutator(ctx android.BottomUpMutatorContext) {
+func (fp *fdoProfile) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if fp.properties.Profile != nil {
path := android.PathForModuleSrc(ctx, *fp.properties.Profile)
android.SetProvider(ctx, FdoProfileProvider, FdoProfileInfo{
@@ -68,14 +55,6 @@
}
}
-// fdoProfileMutator calls the generic fdoProfileMutator function of fdoProfileMutator
-// which is implemented by cc and cc.FdoProfile
-func fdoProfileMutator(ctx android.BottomUpMutatorContext) {
- if f, ok := ctx.Module().(FdoProfileMutatorInterface); ok {
- f.fdoProfileMutator(ctx)
- }
-}
-
func FdoProfileFactory() android.Module {
m := &fdoProfile{}
m.AddProperties(&m.properties)
diff --git a/cc/lto.go b/cc/lto.go
index b2b4570..05fa8ee 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -100,7 +100,7 @@
lto.Properties.LtoEnabled = ltoEnabled
}
-func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
+func (lto *lto) flags(ctx ModuleContext, flags Flags) Flags {
// TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves.
// This has be checked late because these properties can be mutated.
if ctx.isCfi() || ctx.isFuzzer() {
@@ -139,7 +139,7 @@
// Reduce the inlining threshold for a better balance of binary size and
// performance.
if !ctx.Darwin() {
- if ctx.isAfdoCompile() {
+ if ctx.isAfdoCompile(ctx) {
ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=40")
} else {
ltoLdFlags = append(ltoLdFlags, "-Wl,-plugin-opt,-import-instr-limit=5")
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 2f6476c..6612a6f 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -50,8 +50,8 @@
// Function that builds extra files under the root directory and returns the files
buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
- // Function that filters PackagingSpecs returned by PackagingBase.GatherPackagingSpecs()
- filterPackagingSpecs func(specs map[string]android.PackagingSpec)
+ // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
+ filterPackagingSpec func(spec android.PackagingSpec) bool
output android.OutputPath
installDir android.InstallPath
@@ -493,10 +493,7 @@
// Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
- specs := f.PackagingBase.GatherPackagingSpecs(ctx)
- if f.filterPackagingSpecs != nil {
- f.filterPackagingSpecs(specs)
- }
+ specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filterPackagingSpec)
return specs
}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 75abf70..34f4ffb 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -37,7 +37,7 @@
module := &systemImage{}
module.AddProperties(&module.properties)
module.filesystem.buildExtraFiles = module.buildExtraFiles
- module.filesystem.filterPackagingSpecs = module.filterPackagingSpecs
+ module.filesystem.filterPackagingSpec = module.filterPackagingSpec
initFilesystemModule(&module.filesystem)
return module
}
@@ -73,10 +73,6 @@
// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
// Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
-func (s *systemImage) filterPackagingSpecs(specs map[string]android.PackagingSpec) {
- for k, ps := range specs {
- if ps.Partition() != "system" {
- delete(specs, k)
- }
- }
+func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool {
+ return ps.Partition() == "system"
}
diff --git a/java/Android.bp b/java/Android.bp
index 2585cd2..54b36ab 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -67,6 +67,7 @@
"plugin.go",
"prebuilt_apis.go",
"proto.go",
+ "ravenwood.go",
"robolectric.go",
"rro.go",
"sdk.go",
@@ -107,6 +108,7 @@
"plugin_test.go",
"prebuilt_apis_test.go",
"proto_test.go",
+ "ravenwood_test.go",
"rro_test.go",
"sdk_library_test.go",
"sdk_test.go",
diff --git a/java/app.go b/java/app.go
index 0c56d81..9be3f6d 100755
--- a/java/app.go
+++ b/java/app.go
@@ -920,15 +920,39 @@
shouldCollectRecursiveNativeDeps bool,
checkNativeSdkVersion bool) ([]jniLib, android.Paths, []Certificate) {
- var jniLibs []jniLib
- var prebuiltJniPackages android.Paths
- var certificates []Certificate
- seenModulePaths := make(map[string]bool)
-
if checkNativeSdkVersion {
checkNativeSdkVersion = app.SdkVersion(ctx).Specified() &&
app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
}
+ jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
+ checkNativeSdkVersion, func(dep cc.LinkableInterface) bool {
+ return !dep.IsNdk(ctx.Config()) && !dep.IsStubs()
+ })
+
+ var certificates []Certificate
+
+ ctx.VisitDirectDeps(func(module android.Module) {
+ otherName := ctx.OtherModuleName(module)
+ tag := ctx.OtherModuleDependencyTag(module)
+
+ if tag == certificateTag {
+ if dep, ok := module.(*AndroidAppCertificate); ok {
+ certificates = append(certificates, dep.Certificate)
+ } else {
+ ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
+ }
+ }
+ })
+ return jniLib, prebuiltJniPackages, certificates
+}
+
+func collectJniDeps(ctx android.ModuleContext,
+ shouldCollectRecursiveNativeDeps bool,
+ checkNativeSdkVersion bool,
+ filter func(cc.LinkableInterface) bool) ([]jniLib, android.Paths) {
+ var jniLibs []jniLib
+ var prebuiltJniPackages android.Paths
+ seenModulePaths := make(map[string]bool)
ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
otherName := ctx.OtherModuleName(module)
@@ -936,7 +960,7 @@
if IsJniDepTag(tag) || cc.IsSharedDepTag(tag) {
if dep, ok := module.(cc.LinkableInterface); ok {
- if dep.IsNdk(ctx.Config()) || dep.IsStubs() {
+ if filter != nil && !filter(dep) {
return false
}
@@ -977,18 +1001,10 @@
prebuiltJniPackages = append(prebuiltJniPackages, info.JniPackages...)
}
- if tag == certificateTag {
- if dep, ok := module.(*AndroidAppCertificate); ok {
- certificates = append(certificates, dep.Certificate)
- } else {
- ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
- }
- }
-
return false
})
- return jniLibs, prebuiltJniPackages, certificates
+ return jniLibs, prebuiltJniPackages
}
func (a *AndroidApp) WalkPayloadDeps(ctx android.ModuleContext, do android.PayloadDepsCallback) {
diff --git a/java/base.go b/java/base.go
index e52cedd..4e2366f 100644
--- a/java/base.go
+++ b/java/base.go
@@ -17,6 +17,8 @@
import (
"fmt"
"path/filepath"
+ "reflect"
+ "slices"
"strconv"
"strings"
@@ -89,6 +91,9 @@
// if not blank, run jarjar using the specified rules file
Jarjar_rules *string `android:"path,arch_variant"`
+ // if not blank, used as prefix to generate repackage rule
+ Jarjar_prefix *string
+
// If not blank, set the java version passed to javac as -source and -target
Java_version *string
@@ -425,6 +430,8 @@
// inserting into the bootclasspath/classpath of another compile
headerJarFile android.Path
+ repackagedHeaderJarFile android.Path
+
// jar file containing implementation classes including static library dependencies but no
// resources
implementationJarFile android.Path
@@ -489,6 +496,9 @@
// expanded Jarjar_rules
expandJarjarRules android.Path
+ // jarjar rule for inherited jarjar rules
+ repackageJarjarRules android.Path
+
// Extra files generated by the module type to be added as java resources.
extraResources android.Paths
@@ -518,6 +528,10 @@
// Single aconfig "cache file" merged from this module and all dependencies.
mergedAconfigFiles map[string]android.Paths
+
+ // Values that will be set in the JarJarProvider data for jarjar repackaging,
+ // and merged with our dependencies' rules.
+ jarjarRenameRules map[string]string
}
func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error {
@@ -1072,6 +1086,19 @@
}
func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
+
+ // Auto-propagating jarjar rules
+ jarjarProviderData := j.collectJarJarRules(ctx)
+ if jarjarProviderData != nil {
+ android.SetProvider(ctx, JarJarProvider, *jarjarProviderData)
+ text := getJarJarRuleText(jarjarProviderData)
+ if text != "" {
+ ruleTextFile := android.PathForModuleOut(ctx, "repackaged-jarjar", "repackaging.txt")
+ android.WriteFileRule(ctx, ruleTextFile, text)
+ j.repackageJarjarRules = ruleTextFile
+ }
+ }
+
j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
deps := j.collectDeps(ctx)
@@ -1170,7 +1197,7 @@
ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.")
}
- _, j.headerJarFile =
+ _, j.headerJarFile, _ =
j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName,
extraCombinedJars)
if ctx.Failed() {
@@ -1285,7 +1312,7 @@
// with sharding enabled. See: b/77284273.
}
extraJars := append(android.CopyOf(extraCombinedJars), kotlinHeaderJars...)
- headerJarFileWithoutDepsOrJarjar, j.headerJarFile =
+ headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile =
j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars)
if ctx.Failed() {
return
@@ -1509,6 +1536,16 @@
}
}
+ // Automatic jarjar rules propagation
+ if j.repackageJarjarRules != nil {
+ repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", jarName).OutputPath
+ TransformJarJar(ctx, repackagedJarjarFile, outputFile, j.repackageJarjarRules)
+ outputFile = repackagedJarjarFile
+ if ctx.Failed() {
+ return
+ }
+ }
+
// Check package restrictions if necessary.
if len(j.properties.Permitted_packages) > 0 {
// Time stamp file created by the package check rule.
@@ -1678,6 +1715,7 @@
android.SetProvider(ctx, JavaInfoProvider, JavaInfo{
HeaderJars: android.PathsIfNonNil(j.headerJarFile),
+ RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile),
TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars,
TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars,
ImplementationAndResourcesJars: android.PathsIfNonNil(j.implementationAndResourcesJar),
@@ -1813,7 +1851,7 @@
func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
deps deps, flags javaBuilderFlags, jarName string,
- extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar android.Path) {
+ extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) {
var jars android.Paths
if len(srcFiles) > 0 || len(srcJars) > 0 {
@@ -1821,7 +1859,7 @@
turbineJar := android.PathForModuleOut(ctx, "turbine", jarName)
TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags)
if ctx.Failed() {
- return nil, nil
+ return nil, nil, nil
}
jars = append(jars, turbineJar)
headerJar = turbineJar
@@ -1846,11 +1884,22 @@
TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules)
jarjarAndDepsHeaderJar = jarjarFile
if ctx.Failed() {
- return nil, nil
+ return nil, nil, nil
}
}
- return headerJar, jarjarAndDepsHeaderJar
+ if j.repackageJarjarRules != nil {
+ repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName)
+ TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules)
+ jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile
+ if ctx.Failed() {
+ return nil, nil, nil
+ }
+ } else {
+ jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar
+ }
+
+ return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar
}
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
@@ -2207,6 +2256,10 @@
}
deps.classpath = append(deps.classpath, dep.HeaderJars...)
deps.dexClasspath = append(deps.dexClasspath, dep.HeaderJars...)
+ if len(dep.RepackagedHeaderJars) == 1 && !slices.Contains(dep.HeaderJars, dep.RepackagedHeaderJars[0]) {
+ deps.classpath = append(deps.classpath, dep.RepackagedHeaderJars...)
+ deps.dexClasspath = append(deps.dexClasspath, dep.RepackagedHeaderJars...)
+ }
deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs...)
addPlugins(&deps, dep.ExportedPlugins, dep.ExportedPluginClasses...)
deps.disableTurbine = deps.disableTurbine || dep.ExportedPluginDisableTurbine
@@ -2311,6 +2364,190 @@
return deps
}
+// Provider for jarjar renaming rules.
+//
+// Modules can set their jarjar renaming rules with addJarJarRenameRule, and those renamings will be
+// passed to all rdeps. The typical way that these renamings will NOT be inherited is when a module
+// links against stubs -- these are not passed through stubs. The classes will remain unrenamed on
+// classes until a module with jarjar_prefix is reached, and all as yet unrenamed classes will then
+// be renamed from that module.
+// TODO: Add another property to suppress the forwarding of
+type JarJarProviderData struct {
+ // Mapping of class names: original --> renamed. If the value is "", the class will be
+ // renamed by the next rdep that has the jarjar_prefix attribute (or this module if it has
+ // attribute). Rdeps of that module will inherit the renaming.
+ Rename map[string]string
+}
+
+func (this JarJarProviderData) GetDebugString() string {
+ result := ""
+ for k, v := range this.Rename {
+ if strings.Contains(k, "android.companion.virtual.flags.FakeFeatureFlagsImpl") {
+ result += k + "-->" + v + ";"
+ }
+ }
+ return result
+}
+
+var JarJarProvider = blueprint.NewProvider[JarJarProviderData]()
+
+var overridableJarJarPrefix = "com.android.internal.hidden_from_bootclasspath"
+
+func init() {
+ android.SetJarJarPrefixHandler(mergeJarJarPrefixes)
+}
+
+// BaseJarJarProviderData contains information that will propagate across dependencies regardless of
+// whether they are java modules or not.
+type BaseJarJarProviderData struct {
+ JarJarProviderData JarJarProviderData
+}
+
+func (this BaseJarJarProviderData) GetDebugString() string {
+ return this.JarJarProviderData.GetDebugString()
+}
+
+var BaseJarJarProvider = blueprint.NewProvider[BaseJarJarProviderData]()
+
+// mergeJarJarPrefixes is called immediately before module.GenerateAndroidBuildActions is called.
+// Since there won't be a JarJarProvider, we create the BaseJarJarProvider if any of our deps have
+// either JarJarProvider or BaseJarJarProvider.
+func mergeJarJarPrefixes(ctx android.ModuleContext) {
+ mod := ctx.Module()
+ // Explicitly avoid propagating into some module types.
+ switch reflect.TypeOf(mod).String() {
+ case "*java.Droidstubs":
+ return
+ }
+ jarJarData := collectDirectDepsProviders(ctx)
+ if jarJarData != nil {
+ providerData := BaseJarJarProviderData{
+ JarJarProviderData: *jarJarData,
+ }
+ android.SetProvider(ctx, BaseJarJarProvider, providerData)
+ }
+
+}
+
+// Add a jarjar renaming rule to this module, to be inherited to all dependent modules.
+func (module *Module) addJarJarRenameRule(original string, renamed string) {
+ if module.jarjarRenameRules == nil {
+ module.jarjarRenameRules = make(map[string]string)
+ }
+ module.jarjarRenameRules[original] = renamed
+}
+
+func collectDirectDepsProviders(ctx android.ModuleContext) (result *JarJarProviderData) {
+ // Gather repackage information from deps
+ // If the dep jas a JarJarProvider, it is used. Otherwise, any BaseJarJarProvider is used.
+ ctx.VisitDirectDepsIgnoreBlueprint(func(m android.Module) {
+ if ctx.OtherModuleDependencyTag(m) == proguardRaiseTag {
+ return
+ }
+ merge := func(theirs *JarJarProviderData) {
+ for orig, renamed := range theirs.Rename {
+ if result == nil {
+ result = &JarJarProviderData{
+ Rename: make(map[string]string),
+ }
+ }
+ if preexisting, exists := (*result).Rename[orig]; !exists || preexisting == "" {
+ result.Rename[orig] = renamed
+ } else if preexisting != "" && renamed != "" && preexisting != renamed {
+ if strings.HasPrefix(preexisting, overridableJarJarPrefix) {
+ result.Rename[orig] = renamed
+ } else if !strings.HasPrefix(renamed, overridableJarJarPrefix) {
+ ctx.ModuleErrorf("1. Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting, ctx.ModuleName(), m.Name())
+ continue
+ }
+ }
+ }
+ }
+ if theirs, ok := android.OtherModuleProvider(ctx, m, JarJarProvider); ok {
+ merge(&theirs)
+ } else if theirs, ok := android.OtherModuleProvider(ctx, m, BaseJarJarProvider); ok {
+ // TODO: if every java.Module should have a JarJarProvider, and we find only the
+ // BaseJarJarProvider, then there is a bug. Consider seeing if m can be cast
+ // to java.Module.
+ merge(&theirs.JarJarProviderData)
+ }
+ })
+ return
+}
+
+func (this Module) GetDebugString() string {
+ return "sdk_version=" + proptools.String(this.deviceProperties.Sdk_version)
+}
+
+// Merge the jarjar rules we inherit from our dependencies, any that have been added directly to
+// us, and if it's been set, apply the jarjar_prefix property to rename them.
+func (module *Module) collectJarJarRules(ctx android.ModuleContext) *JarJarProviderData {
+ // Gather repackage information from deps
+ result := collectDirectDepsProviders(ctx)
+
+ // Update that with entries we've stored for ourself
+ for orig, renamed := range module.jarjarRenameRules {
+ if result == nil {
+ result = &JarJarProviderData{
+ Rename: make(map[string]string),
+ }
+ }
+ if renamed != "" {
+ if preexisting, exists := (*result).Rename[orig]; exists && preexisting != renamed {
+ ctx.ModuleErrorf("Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting)
+ continue
+ }
+ }
+ (*result).Rename[orig] = renamed
+ }
+
+ // If there are no renamings, then jarjar_prefix does nothing, so skip the extra work.
+ if result == nil {
+ return nil
+ }
+
+ // If they've given us a jarjar_prefix property, then we will use that to rename any classes
+ // that have not yet been renamed.
+ prefix := proptools.String(module.properties.Jarjar_prefix)
+ if prefix != "" {
+ if prefix[0] == '.' {
+ ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not start with '.'")
+ return nil
+ }
+ if prefix[len(prefix)-1] == '.' {
+ ctx.PropertyErrorf("jarjar_prefix", "jarjar_prefix can not end with '.'")
+ return nil
+ }
+
+ var updated map[string]string
+ for orig, renamed := range (*result).Rename {
+ if renamed == "" {
+ if updated == nil {
+ updated = make(map[string]string)
+ }
+ updated[orig] = prefix + "." + orig
+ }
+ }
+ for orig, renamed := range updated {
+ (*result).Rename[orig] = renamed
+ }
+ }
+
+ return result
+}
+
+// Get the jarjar rule text for a given provider for the fully resolved rules. Classes that map
+// to "" won't be in this list because they shouldn't be renamed yet.
+func getJarJarRuleText(provider *JarJarProviderData) string {
+ result := ""
+ for orig, renamed := range provider.Rename {
+ if renamed != "" {
+ result += "rule " + orig + " " + renamed + "\n"
+ }
+ }
+ return result
+}
+
func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) {
deps.processorPath = append(deps.processorPath, pluginJars...)
deps.processorClasses = append(deps.processorClasses, pluginClasses...)
diff --git a/java/builder.go b/java/builder.go
index 085e7a1..74a05f2 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -277,7 +277,7 @@
gatherReleasedFlaggedApisRule = pctx.AndroidStaticRule("gatherReleasedFlaggedApisRule",
blueprint.RuleParams{
- Command: `${aconfig} dump-cache --format='{fully_qualified_name}={state:bool}' ` +
+ Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}' ` +
`--out ${out} ` +
`${flags_path} ` +
`${filter_args} `,
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 2017801..0ebab4d 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -103,8 +103,8 @@
func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
set := map[string]struct{}{}
for _, name := range contents {
- dep := ctx.GetDirectDepWithTag(name, tag)
- set[name] = struct{}{}
+ dep, _ := ctx.GetDirectDepWithTag(name, tag).(android.Module)
+ set[ModuleStemForDeapexing(dep)] = struct{}{}
if m, ok := dep.(ModuleWithStem); ok {
set[m.Stem()] = struct{}{}
} else {
diff --git a/java/dex.go b/java/dex.go
index cdae0a2..4474c63 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -262,7 +262,7 @@
var proguardRaiseDeps classpath
ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) {
dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider)
- proguardRaiseDeps = append(proguardRaiseDeps, dep.HeaderJars...)
+ proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...)
})
r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 01f60d4..f7e3cb9 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -726,13 +726,19 @@
return nil, false
}
+// Returns the stem of an artifact inside a prebuilt apex
+func ModuleStemForDeapexing(m android.Module) string {
+ bmn, _ := m.(interface{ BaseModuleName() string })
+ return bmn.BaseModuleName()
+}
+
// Returns the java libraries exported by the apex for hiddenapi and dexpreopt
// This information can come from two mechanisms
// 1. New: Direct deps to _selected_ apexes. The apexes return a ApexExportsInfo
// 2. Legacy: An edge to java_library or java_import (java_sdk_library) module. For prebuilt apexes, this serves as a hook and is populated by deapexers of prebuilt apxes
// TODO: b/308174306 - Once all mainline modules have been flagged, drop (2)
func getDexJarForApex(ctx android.ModuleContext, pair apexJarModulePair, apexNameToApexExportsInfoMap apexNameToApexExportsInfoMap) android.Path {
- if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, android.RemoveOptionalPrebuiltPrefix(pair.jarModule.Name())); found {
+ if dex, found := apexNameToApexExportsInfoMap.javaLibraryDexPathOnHost(ctx, pair.apex, ModuleStemForDeapexing(pair.jarModule)); found {
return dex
}
// TODO: b/308174306 - Remove the legacy mechanism
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 4267545..51503f2 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -744,8 +744,14 @@
filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
case Exportable:
- filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'"
-
+ // When the build flag RELEASE_EXPORT_RUNTIME_APIS is set to true, apis marked with
+ // the flagged apis that have read_write permissions are exposed on top of the enabled
+ // and read_only apis. This is to support local override of flag values at runtime.
+ if ctx.Config().ReleaseExportRuntimeApis() {
+ filterArgs = "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
+ } else {
+ filterArgs = "--filter='state:ENABLED+permission:READ_ONLY'"
+ }
}
ctx.Build(pctx, android.BuildParams{
@@ -1320,6 +1326,24 @@
type PrebuiltStubsSourcesProperties struct {
Srcs []string `android:"path"`
+
+ // Name of the source soong module that gets shadowed by this prebuilt
+ // If unspecified, follows the naming convention that the source module of
+ // the prebuilt is Name() without "prebuilt_" prefix
+ Source_module_name *string
+
+ // Non-nil if this prebuilt stub srcs module was dynamically created by a java_sdk_library_import
+ // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+ // (without any prebuilt_ prefix)
+ Created_by_java_sdk_library_name *string `blueprint:"mutated"`
+}
+
+func (j *PrebuiltStubsSources) BaseModuleName() string {
+ return proptools.StringDefault(j.properties.Source_module_name, j.ModuleBase.Name())
+}
+
+func (j *PrebuiltStubsSources) CreatedByJavaSdkLibraryName() *string {
+ return j.properties.Created_by_java_sdk_library_name
}
type PrebuiltStubsSources struct {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 52cd1c5..caa8345 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -412,3 +412,48 @@
android.AssertStringDoesContain(t, "foo generates exportable stubs jar",
strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar")
}
+
+func TestReleaseExportRuntimeApis(t *testing.T) {
+ result := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildFlags = map[string]string{
+ "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
+ "RELEASE_EXPORT_RUNTIME_APIS": "true",
+ }
+ }),
+ android.FixtureMergeMockFs(map[string][]byte{
+ "a/A.java": nil,
+ "a/current.txt": nil,
+ "a/removed.txt": nil,
+ }),
+ ).RunTestWithBp(t, `
+ aconfig_declarations {
+ name: "bar",
+ package: "com.example.package",
+ srcs: [
+ "bar.aconfig",
+ ],
+ }
+ droidstubs {
+ name: "foo",
+ srcs: ["a/A.java"],
+ api_surface: "public",
+ check_api: {
+ current: {
+ api_file: "a/current.txt",
+ removed_api_file: "a/removed.txt",
+ }
+ },
+ aconfig_declarations: [
+ "bar",
+ ],
+ }
+ `)
+
+ m := result.ModuleForTests("foo", "android_common")
+
+ rule := m.Output("released-flagged-apis-exportable.txt")
+ exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'"
+ android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"])
+}
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
index 40f780c..e8316cc 100644
--- a/java/generated_java_library.go
+++ b/java/generated_java_library.go
@@ -107,3 +107,8 @@
module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
module.Library.GenerateAndroidBuildActions(ctx)
}
+
+// Add a rule to the jarjar renaming rules. See RepackageProviderData.
+func (module *GeneratedJavaLibraryModule) AddJarJarRenameRule(original string, renamed string) {
+ module.addJarJarRenameRule(original, renamed)
+}
diff --git a/java/java.go b/java/java.go
index cd249ed..d7d271c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -248,6 +248,8 @@
// against this module. If empty, ImplementationJars should be used instead.
HeaderJars android.Paths
+ RepackagedHeaderJars android.Paths
+
// set of header jars for all transitive libs deps
TransitiveLibsHeaderJars *android.DepSet[android.Path]
@@ -2095,6 +2097,11 @@
// If unspecified, follows the naming convention that the source module of
// the prebuilt is Name() without "prebuilt_" prefix
Source_module_name *string
+
+ // Non-nil if this java_import module was dynamically created by a java_sdk_library_import
+ // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+ // (without any prebuilt_ prefix)
+ Created_by_java_sdk_library_name *string `blueprint:"mutated"`
}
type Import struct {
@@ -2180,6 +2187,10 @@
return proptools.StringDefault(j.properties.Stem, j.BaseModuleName())
}
+func (j *Import) CreatedByJavaSdkLibraryName() *string {
+ return j.properties.Created_by_java_sdk_library_name
+}
+
func (a *Import) JacocoReportClassesFile() android.Path {
return nil
}
@@ -2831,7 +2842,20 @@
type JavaApiContributionImport struct {
JavaApiContribution
- prebuilt android.Prebuilt
+ prebuilt android.Prebuilt
+ prebuiltProperties javaApiContributionImportProperties
+}
+
+type javaApiContributionImportProperties struct {
+ // Name of the source soong module that gets shadowed by this prebuilt
+ // If unspecified, follows the naming convention that the source module of
+ // the prebuilt is Name() without "prebuilt_" prefix
+ Source_module_name *string
+
+ // Non-nil if this java_import module was dynamically created by a java_sdk_library_import
+ // The name is the undecorated name of the java_sdk_library as it appears in the blueprint file
+ // (without any prebuilt_ prefix)
+ Created_by_java_sdk_library_name *string `blueprint:"mutated"`
}
func ApiContributionImportFactory() android.Module {
@@ -2839,7 +2863,7 @@
android.InitAndroidModule(module)
android.InitDefaultableModule(module)
android.InitPrebuiltModule(module, &[]string{""})
- module.AddProperties(&module.properties)
+ module.AddProperties(&module.properties, &module.prebuiltProperties)
module.AddProperties(&module.sdkLibraryComponentProperties)
return module
}
@@ -2852,6 +2876,14 @@
return module.prebuilt.Name(module.ModuleBase.Name())
}
+func (j *JavaApiContributionImport) BaseModuleName() string {
+ return proptools.StringDefault(j.prebuiltProperties.Source_module_name, j.ModuleBase.Name())
+}
+
+func (j *JavaApiContributionImport) CreatedByJavaSdkLibraryName() *string {
+ return j.prebuiltProperties.Created_by_java_sdk_library_name
+}
+
func (ap *JavaApiContributionImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ap.JavaApiContribution.GenerateAndroidBuildActions(ctx)
}
diff --git a/java/ravenwood.go b/java/ravenwood.go
new file mode 100644
index 0000000..908619d
--- /dev/null
+++ b/java/ravenwood.go
@@ -0,0 +1,278 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package java
+
+import (
+ "android/soong/android"
+ "android/soong/tradefed"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+func init() {
+ RegisterRavenwoodBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("android_ravenwood_test", ravenwoodTestFactory)
+ ctx.RegisterModuleType("android_ravenwood_libgroup", ravenwoodLibgroupFactory)
+}
+
+var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"}
+var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"}
+var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"}
+
+const ravenwoodUtilsName = "ravenwood-utils"
+const ravenwoodRuntimeName = "ravenwood-runtime"
+
+type ravenwoodLibgroupJniDepProviderInfo struct {
+ // All the jni_libs module names with transient dependencies.
+ names map[string]bool
+}
+
+var ravenwoodLibgroupJniDepProvider = blueprint.NewProvider[ravenwoodLibgroupJniDepProviderInfo]()
+
+func getLibPath(archType android.ArchType) string {
+ if archType.Multilib == "lib64" {
+ return "lib64"
+ }
+ return "lib"
+}
+
+type ravenwoodTestProperties struct {
+ Jni_libs []string
+}
+
+type ravenwoodTest struct {
+ Library
+
+ ravenwoodTestProperties ravenwoodTestProperties
+
+ testProperties testProperties
+ testConfig android.Path
+
+ forceOSType android.OsType
+ forceArchType android.ArchType
+}
+
+func ravenwoodTestFactory() android.Module {
+ module := &ravenwoodTest{}
+
+ module.addHostAndDeviceProperties()
+ module.AddProperties(&module.testProperties, &module.ravenwoodTestProperties)
+
+ module.Module.dexpreopter.isTest = true
+ module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+
+ module.testProperties.Test_suites = []string{
+ "general-tests",
+ "ravenwood-tests",
+ }
+ module.testProperties.Test_options.Unit_test = proptools.BoolPtr(false)
+
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitDefaultableModule(module)
+
+ return module
+}
+
+func (r *ravenwoodTest) InstallInTestcases() bool { return true }
+func (r *ravenwoodTest) InstallForceOS() (*android.OsType, *android.ArchType) {
+ return &r.forceOSType, &r.forceArchType
+}
+func (r *ravenwoodTest) TestSuites() []string {
+ return r.testProperties.Test_suites
+}
+
+func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+ r.Library.DepsMutator(ctx)
+
+ // Generically depend on the runtime so that it's installed together with us
+ ctx.AddVariationDependencies(nil, ravenwoodRuntimeTag, ravenwoodRuntimeName)
+
+ // Directly depend on any utils so that we link against them
+ utils := ctx.AddVariationDependencies(nil, ravenwoodUtilsTag, ravenwoodUtilsName)[0]
+ if utils != nil {
+ for _, lib := range utils.(*ravenwoodLibgroup).ravenwoodLibgroupProperties.Libs {
+ ctx.AddVariationDependencies(nil, libTag, lib)
+ }
+ }
+
+ // Add jni libs
+ for _, lib := range r.ravenwoodTestProperties.Jni_libs {
+ ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
+ }
+}
+
+func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ r.forceOSType = ctx.Config().BuildOS
+ r.forceArchType = ctx.Config().BuildArch
+
+ r.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: r.testProperties.Test_config,
+ TestConfigTemplateProp: r.testProperties.Test_config_template,
+ TestSuites: r.testProperties.Test_suites,
+ AutoGenConfig: r.testProperties.Auto_gen_config,
+ DeviceTemplate: "${RavenwoodTestConfigTemplate}",
+ HostTemplate: "${RavenwoodTestConfigTemplate}",
+ })
+
+ r.Library.GenerateAndroidBuildActions(ctx)
+
+ // Start by depending on all files installed by dependencies
+ var installDeps android.InstallPaths
+
+ // All JNI libraries included in the runtime
+ var runtimeJniModuleNames map[string]bool
+
+ if utils := ctx.GetDirectDepsWithTag(ravenwoodUtilsTag)[0]; utils != nil {
+ for _, installFile := range utils.FilesToInstall() {
+ installDeps = append(installDeps, installFile)
+ }
+ jniDeps, ok := android.OtherModuleProvider(ctx, utils, ravenwoodLibgroupJniDepProvider)
+ if ok {
+ runtimeJniModuleNames = jniDeps.names
+ }
+ }
+
+ if runtime := ctx.GetDirectDepsWithTag(ravenwoodRuntimeTag)[0]; runtime != nil {
+ for _, installFile := range runtime.FilesToInstall() {
+ installDeps = append(installDeps, installFile)
+ }
+ jniDeps, ok := android.OtherModuleProvider(ctx, runtime, ravenwoodLibgroupJniDepProvider)
+ if ok {
+ runtimeJniModuleNames = jniDeps.names
+ }
+ }
+
+ // Also remember what JNI libs are in the runtime.
+
+ // Also depend on our config
+ installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
+ installConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
+ installDeps = append(installDeps, installConfig)
+
+ // Depend on the JNI libraries, but don't install the ones that the runtime already
+ // contains.
+ soInstallPath := installPath.Join(ctx, getLibPath(r.forceArchType))
+ for _, jniLib := range collectTransitiveJniDeps(ctx) {
+ if _, ok := runtimeJniModuleNames[jniLib.name]; ok {
+ continue // Runtime already includes it.
+ }
+ installJni := ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+ installDeps = append(installDeps, installJni)
+ }
+
+ // Install our JAR with all dependencies
+ ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...)
+}
+
+func (r *ravenwoodTest) AndroidMkEntries() []android.AndroidMkEntries {
+ entriesList := r.Library.AndroidMkEntries()
+ entries := &entriesList[0]
+ entries.ExtraEntries = append(entries.ExtraEntries,
+ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+ entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE",
+ "general-tests", "ravenwood-tests")
+ if r.testConfig != nil {
+ entries.SetPath("LOCAL_FULL_TEST_CONFIG", r.testConfig)
+ }
+ })
+ return entriesList
+}
+
+type ravenwoodLibgroupProperties struct {
+ Libs []string
+
+ Jni_libs []string
+}
+
+type ravenwoodLibgroup struct {
+ android.ModuleBase
+
+ ravenwoodLibgroupProperties ravenwoodLibgroupProperties
+
+ forceOSType android.OsType
+ forceArchType android.ArchType
+}
+
+func ravenwoodLibgroupFactory() android.Module {
+ module := &ravenwoodLibgroup{}
+ module.AddProperties(&module.ravenwoodLibgroupProperties)
+
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ return module
+}
+
+func (r *ravenwoodLibgroup) InstallInTestcases() bool { return true }
+func (r *ravenwoodLibgroup) InstallForceOS() (*android.OsType, *android.ArchType) {
+ return &r.forceOSType, &r.forceArchType
+}
+func (r *ravenwoodLibgroup) TestSuites() []string {
+ return []string{
+ "general-tests",
+ "ravenwood-tests",
+ }
+}
+
+func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // Always depends on our underlying libs
+ for _, lib := range r.ravenwoodLibgroupProperties.Libs {
+ ctx.AddVariationDependencies(nil, ravenwoodLibContentTag, lib)
+ }
+ for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs {
+ ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib)
+ }
+}
+
+func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ r.forceOSType = ctx.Config().BuildOS
+ r.forceArchType = ctx.Config().BuildArch
+
+ // Collect the JNI dependencies, including the transitive deps.
+ jniDepNames := make(map[string]bool)
+ jniLibs := collectTransitiveJniDeps(ctx)
+
+ for _, jni := range jniLibs {
+ jniDepNames[jni.name] = true
+ }
+ android.SetProvider(ctx, ravenwoodLibgroupJniDepProvider, ravenwoodLibgroupJniDepProviderInfo{
+ names: jniDepNames,
+ })
+
+ // Install our runtime into expected location for packaging
+ installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
+ for _, lib := range r.ravenwoodLibgroupProperties.Libs {
+ libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag)
+ libJar := android.OutputFileForModule(ctx, libModule, "")
+ ctx.InstallFile(installPath, lib+".jar", libJar)
+ }
+ soInstallPath := android.PathForModuleInstall(ctx, r.BaseModuleName()).Join(ctx, getLibPath(r.forceArchType))
+
+ for _, jniLib := range jniLibs {
+ ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path)
+ }
+
+ // Normal build should perform install steps
+ ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install"))
+}
+
+// collectTransitiveJniDeps returns all JNI dependencies, including transitive
+// ones, including NDK / stub libs. (Because Ravenwood has no "preinstalled" libraries)
+func collectTransitiveJniDeps(ctx android.ModuleContext) []jniLib {
+ libs, _ := collectJniDeps(ctx, true, false, nil)
+ return libs
+}
diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go
new file mode 100644
index 0000000..5961264
--- /dev/null
+++ b/java/ravenwood_test.go
@@ -0,0 +1,186 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+ "runtime"
+ "testing"
+
+ "android/soong/android"
+)
+
+var prepareRavenwoodRuntime = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ RegisterRavenwoodBuildComponents(ctx)
+ }),
+ android.FixtureAddTextFile("ravenwood/Android.bp", `
+ cc_library_shared {
+ name: "ravenwood-runtime-jni1",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ }
+ cc_library_shared {
+ name: "ravenwood-runtime-jni2",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ stem: "libred",
+ shared_libs: [
+ "ravenwood-runtime-jni3",
+ ],
+ }
+ cc_library_shared {
+ name: "ravenwood-runtime-jni3",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ }
+ java_library_static {
+ name: "framework-minus-apex.ravenwood",
+ srcs: ["Framework.java"],
+ }
+ java_library_static {
+ name: "framework-services.ravenwood",
+ srcs: ["Services.java"],
+ }
+ java_library_static {
+ name: "framework-rules.ravenwood",
+ srcs: ["Rules.java"],
+ }
+ android_ravenwood_libgroup {
+ name: "ravenwood-runtime",
+ libs: [
+ "framework-minus-apex.ravenwood",
+ "framework-services.ravenwood",
+ ],
+ jni_libs: [
+ "ravenwood-runtime-jni1",
+ "ravenwood-runtime-jni2",
+ ],
+ }
+ android_ravenwood_libgroup {
+ name: "ravenwood-utils",
+ libs: [
+ "framework-rules.ravenwood",
+ ],
+ }
+ `),
+)
+
+var installPathPrefix = "out/soong/host/linux-x86/testcases"
+
+func TestRavenwoodRuntime(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ prepareRavenwoodRuntime,
+ ).RunTest(t)
+
+ // Verify that our runtime depends on underlying libs
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-minus-apex.ravenwood")
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "framework-services.ravenwood")
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-runtime", "android_common", "ravenwood-runtime-jni")
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-utils", "android_common", "framework-rules.ravenwood")
+
+ // Verify that we've emitted artifacts in expected location
+ runtime := ctx.ModuleForTests("ravenwood-runtime", "android_common")
+ runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
+ runtime.Output(installPathPrefix + "/ravenwood-runtime/framework-services.ravenwood.jar")
+ runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
+ runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so")
+ runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
+ utils := ctx.ModuleForTests("ravenwood-utils", "android_common")
+ utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar")
+}
+
+func TestRavenwoodTest(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip("requires linux")
+ }
+
+ ctx := android.GroupFixturePreparers(
+ PrepareForIntegrationTestWithJava,
+ prepareRavenwoodRuntime,
+ ).RunTestWithBp(t, `
+ cc_library_shared {
+ name: "jni-lib1",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ }
+ cc_library_shared {
+ name: "jni-lib2",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ stem: "libblue",
+ shared_libs: [
+ "jni-lib3",
+ ],
+ }
+ cc_library_shared {
+ name: "jni-lib3",
+ host_supported: true,
+ srcs: ["jni.cpp"],
+ stem: "libpink",
+ }
+ android_ravenwood_test {
+ name: "ravenwood-test",
+ srcs: ["Test.java"],
+ jni_libs: [
+ "jni-lib1",
+ "jni-lib2",
+ "ravenwood-runtime-jni2",
+ ],
+ sdk_version: "test_current",
+ }
+ `)
+
+ // Verify that our test depends on underlying libs
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-buildtime")
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "ravenwood-utils")
+ CheckModuleHasDependency(t, ctx.TestContext, "ravenwood-test", "android_common", "jni-lib")
+
+ module := ctx.ModuleForTests("ravenwood-test", "android_common")
+ classpath := module.Rule("javac").Args["classpath"]
+
+ // Verify that we're linking against test_current
+ android.AssertStringDoesContain(t, "classpath", classpath, "android_test_stubs_current.jar")
+ // Verify that we're linking against utils
+ android.AssertStringDoesContain(t, "classpath", classpath, "framework-rules.ravenwood.jar")
+ // Verify that we're *NOT* linking against runtime
+ android.AssertStringDoesNotContain(t, "classpath", classpath, "framework-minus-apex.ravenwood.jar")
+ android.AssertStringDoesNotContain(t, "classpath", classpath, "framework-services.ravenwood.jar")
+
+ // Verify that we've emitted test artifacts in expected location
+ outputJar := module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.jar")
+ module.Output(installPathPrefix + "/ravenwood-test/ravenwood-test.config")
+ module.Output(installPathPrefix + "/ravenwood-test/lib64/jni-lib1.so")
+ module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so")
+ module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so")
+
+ // ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted.
+ for _, o := range module.AllOutputs() {
+ android.AssertStringDoesNotContain(t, "runtime libs shouldn't be included", o, "/ravenwood-test/lib64/ravenwood-runtime")
+ }
+
+ // Verify that we're going to install underlying libs
+ orderOnly := outputJar.OrderOnly.Strings()
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-minus-apex.ravenwood.jar")
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/framework-services.ravenwood.jar")
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so")
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/libred.so")
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so")
+ android.AssertStringListContains(t, "orderOnly", orderOnly, installPathPrefix+"/ravenwood-utils/framework-rules.ravenwood.jar")
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 49e6727..fbde042 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -907,7 +907,30 @@
type commonSdkLibraryAndImportModule interface {
android.Module
- BaseModuleName() string
+ // Returns the name of the root java_sdk_library that creates the child stub libraries
+ // This is the `name` as it appears in Android.bp, and not the name in Soong's build graph
+ // (with the prebuilt_ prefix)
+ //
+ // e.g. in the following java_sdk_library_import
+ // java_sdk_library_import {
+ // name: "framework-foo.v1",
+ // source_module_name: "framework-foo",
+ // }
+ // the values returned by
+ // 1. Name(): prebuilt_framework-foo.v1 # unique
+ // 2. BaseModuleName(): framework-foo # the source
+ // 3. RootLibraryName: framework-foo.v1 # the undecordated `name` from Android.bp
+ RootLibraryName() string
+}
+
+func (m *SdkLibrary) RootLibraryName() string {
+ return m.BaseModuleName()
+}
+
+func (m *SdkLibraryImport) RootLibraryName() string {
+ // m.BaseModuleName refers to the source of the import
+ // use moduleBase.Name to get the name of the module as it appears in the .bp file
+ return m.ModuleBase.Name()
}
// Common code between sdk library and sdk library import
@@ -946,7 +969,7 @@
return false
}
- namePtr := proptools.StringPtr(c.module.BaseModuleName())
+ namePtr := proptools.StringPtr(c.module.RootLibraryName())
c.sdkLibraryComponentProperties.SdkLibraryName = namePtr
// Only track this sdk library if this can be used as a shared library.
@@ -973,51 +996,51 @@
// Module name of the runtime implementation library
func (c *commonToSdkLibraryAndImport) implLibraryModuleName() string {
- return c.module.BaseModuleName() + ".impl"
+ return c.module.RootLibraryName() + ".impl"
}
// Module name of the XML file for the lib
func (c *commonToSdkLibraryAndImport) xmlPermissionsModuleName() string {
- return c.module.BaseModuleName() + sdkXmlFileSuffix
+ return c.module.RootLibraryName() + sdkXmlFileSuffix
}
// Name of the java_library module that compiles the stubs source.
func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.stubsLibraryModuleName(apiScope, baseName)
}
// Name of the java_library module that compiles the exportable stubs source.
func (c *commonToSdkLibraryAndImport) exportableStubsLibraryModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.exportableStubsLibraryModuleName(apiScope, baseName)
}
// Name of the droidstubs module that generates the stubs source and may also
// generate/check the API.
func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
}
// Name of the java_api_library module that generates the from-text stubs source
// and compiles to a jar file.
func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
}
// Name of the java_library module that compiles the stubs
// generated from source Java files.
func (c *commonToSdkLibraryAndImport) sourceStubsLibraryModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.sourceStubsLibraryModuleName(apiScope, baseName)
}
// Name of the java_library module that compiles the exportable stubs
// generated from source Java files.
func (c *commonToSdkLibraryAndImport) exportableSourceStubsLibraryModuleName(apiScope *apiScope) string {
- baseName := c.module.BaseModuleName()
+ baseName := c.module.RootLibraryName()
return c.namingScheme.exportableSourceStubsLibraryModuleName(apiScope, baseName)
}
@@ -1067,7 +1090,7 @@
if scope, ok := scopeByName[scopeName]; ok {
paths := c.findScopePaths(scope)
if paths == nil {
- return nil, fmt.Errorf("%q does not provide api scope %s", c.module.BaseModuleName(), scopeName)
+ return nil, fmt.Errorf("%q does not provide api scope %s", c.module.RootLibraryName(), scopeName)
}
switch component {
@@ -1103,7 +1126,7 @@
if c.doctagPaths != nil {
return c.doctagPaths, nil
} else {
- return nil, fmt.Errorf("no doctag_files specified on %s", c.module.BaseModuleName())
+ return nil, fmt.Errorf("no doctag_files specified on %s", c.module.RootLibraryName())
}
}
return nil, nil
@@ -1149,7 +1172,7 @@
// If a specific numeric version has been requested then use prebuilt versions of the sdk.
if !sdkVersion.ApiLevel.IsPreview() {
- return PrebuiltJars(ctx, c.module.BaseModuleName(), sdkVersion)
+ return PrebuiltJars(ctx, c.module.RootLibraryName(), sdkVersion)
}
paths := c.selectScopePaths(ctx, sdkVersion.Kind)
@@ -1176,7 +1199,7 @@
scopes = append(scopes, s.name)
}
}
- ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.BaseModuleName(), scopes)
+ ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.module.RootLibraryName(), scopes)
return nil
}
@@ -1238,7 +1261,7 @@
SdkLibraryToImplicitlyTrack *string
}{}
- namePtr := proptools.StringPtr(c.module.BaseModuleName())
+ namePtr := proptools.StringPtr(c.module.RootLibraryName())
componentProps.SdkLibraryName = namePtr
if c.sharedLibrary() {
@@ -2525,6 +2548,11 @@
// If not empty, classes are restricted to the specified packages and their sub-packages.
Permitted_packages []string
+
+ // Name of the source soong module that gets shadowed by this prebuilt
+ // If unspecified, follows the naming convention that the source module of
+ // the prebuilt is Name() without "prebuilt_" prefix
+ Source_module_name *string
}
type SdkLibraryImport struct {
@@ -2638,6 +2666,10 @@
return module.prebuilt.Name(module.ModuleBase.Name())
}
+func (module *SdkLibraryImport) BaseModuleName() string {
+ return proptools.StringDefault(module.properties.Source_module_name, module.ModuleBase.Name())
+}
+
func (module *SdkLibraryImport) createInternalModules(mctx android.DefaultableHookContext) {
// If the build is configured to use prebuilts then force this to be preferred.
@@ -2670,15 +2702,19 @@
func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
// Creates a java import for the jar with ".stubs" suffix
props := struct {
- Name *string
- Sdk_version *string
- Libs []string
- Jars []string
- Compile_dex *bool
+ Name *string
+ Source_module_name *string
+ Created_by_java_sdk_library_name *string
+ Sdk_version *string
+ Libs []string
+ Jars []string
+ Compile_dex *bool
android.UserSuppliedPrebuiltProperties
}{}
props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
+ props.Source_module_name = proptools.StringPtr(apiScope.stubsLibraryModuleName(module.BaseModuleName()))
+ props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Sdk_version = scopeProperties.Sdk_version
// Prepend any of the libs from the legacy public properties to the libs for each of the
// scopes to avoid having to duplicate them in each scope.
@@ -2700,12 +2736,16 @@
func (module *SdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
props := struct {
- Name *string
- Srcs []string
+ Name *string
+ Source_module_name *string
+ Created_by_java_sdk_library_name *string
+ Srcs []string
android.UserSuppliedPrebuiltProperties
}{}
props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
+ props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
+ props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Srcs = scopeProperties.Stub_srcs
// The stubs source is preferred if the java_sdk_library_import is preferred.
@@ -2719,13 +2759,17 @@
api_surface := &apiScope.name
props := struct {
- Name *string
- Api_surface *string
- Api_file *string
- Visibility []string
+ Name *string
+ Source_module_name *string
+ Created_by_java_sdk_library_name *string
+ Api_surface *string
+ Api_file *string
+ Visibility []string
}{}
props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope) + ".api.contribution")
+ props.Source_module_name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()) + ".api.contribution")
+ props.Created_by_java_sdk_library_name = proptools.StringPtr(module.RootLibraryName())
props.Api_surface = api_surface
props.Api_file = api_file
props.Visibility = []string{"//visibility:override", "//visibility:public"}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 3326ec5..93ef408 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -1772,3 +1772,151 @@
"top level exportable stubs library", []string{exportableSourceStubsLibraryModuleName},
topLevelModule.Module().(*Library).properties.Static_libs)
}
+
+// For java libraries depending on java_sdk_library(_import) via libs, assert that
+// rdep gets stubs of source if source is listed in apex_contributions and prebuilt has prefer (legacy mechanism)
+func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) {
+ bp := `
+ apex_contributions {
+ name: "my_mainline_module_contributions",
+ api_domain: "my_mainline_module",
+ contents: ["sdklib"], // source is selected using apex_contributions, but prebuilt is selected using prefer
+ }
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ public: {
+ enabled: true,
+ },
+ }
+ java_sdk_library_import {
+ name: "sdklib",
+ public: {
+ jars: ["a.jar"],
+ stub_srcs: ["a.java"],
+ current_api: "current.txt",
+ removed_api: "removed.txt",
+ annotations: "annotations.zip",
+ },
+ prefer: true, // Set prefer explicitly on the prebuilt. We will assert that rdep gets source in a test case.
+ }
+ // rdeps
+ java_library {
+ name: "mymodule",
+ srcs: ["a.java"],
+ sdk_version: "current",
+ libs: ["sdklib",], // this should be dynamically resolved to sdklib.stubs (source) or prebuilt_sdklib.stubs (prebuilt)
+ }
+ `
+
+ fixture := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib"),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildFlags = map[string]string{
+ // We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here
+ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
+ }
+ }),
+ )
+
+ result := fixture.RunTestWithBp(t, bp)
+ // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
+ public := result.ModuleForTests("mymodule", "android_common")
+ rule := public.Output("javac/mymodule.jar")
+ inputs := rule.Implicits.Strings()
+ android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar")
+}
+
+// test that rdep gets resolved to the correct version of a java_sdk_library (source or a specific prebuilt)
+func TestMultipleSdkLibraryPrebuilts(t *testing.T) {
+ bp := `
+ apex_contributions {
+ name: "my_mainline_module_contributions",
+ api_domain: "my_mainline_module",
+ contents: ["%s"],
+ }
+ java_sdk_library {
+ name: "sdklib",
+ srcs: ["a.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ public: {
+ enabled: true,
+ },
+ }
+ java_sdk_library_import {
+ name: "sdklib.v1", //prebuilt
+ source_module_name: "sdklib",
+ public: {
+ jars: ["a.jar"],
+ stub_srcs: ["a.java"],
+ current_api: "current.txt",
+ removed_api: "removed.txt",
+ annotations: "annotations.zip",
+ },
+ }
+ java_sdk_library_import {
+ name: "sdklib.v2", //prebuilt
+ source_module_name: "sdklib",
+ public: {
+ jars: ["a.jar"],
+ stub_srcs: ["a.java"],
+ current_api: "current.txt",
+ removed_api: "removed.txt",
+ annotations: "annotations.zip",
+ },
+ }
+ // rdeps
+ java_library {
+ name: "mymodule",
+ srcs: ["a.java"],
+ libs: ["sdklib.stubs",],
+ }
+ `
+ testCases := []struct {
+ desc string
+ selectedDependencyName string
+ expectedStubPath string
+ }{
+ {
+ desc: "Source library is selected using apex_contributions",
+ selectedDependencyName: "sdklib",
+ expectedStubPath: "out/soong/.intermediates/sdklib.stubs/android_common/turbine-combined/sdklib.stubs.jar",
+ },
+ {
+ desc: "Prebuilt library v1 is selected using apex_contributions",
+ selectedDependencyName: "prebuilt_sdklib.v1",
+ expectedStubPath: "out/soong/.intermediates/prebuilt_sdklib.v1.stubs/android_common/combined/sdklib.stubs.jar",
+ },
+ {
+ desc: "Prebuilt library v2 is selected using apex_contributions",
+ selectedDependencyName: "prebuilt_sdklib.v2",
+ expectedStubPath: "out/soong/.intermediates/prebuilt_sdklib.v2.stubs/android_common/combined/sdklib.stubs.jar",
+ },
+ }
+
+ fixture := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ PrepareForTestWithJavaSdkLibraryFiles,
+ FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"),
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildFlags = map[string]string{
+ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions",
+ }
+ }),
+ )
+
+ for _, tc := range testCases {
+ result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+ // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions
+ public := result.ModuleForTests("mymodule", "android_common")
+ rule := public.Output("javac/mymodule.jar")
+ inputs := rule.Implicits.Strings()
+ android.AssertStringListContains(t, "Could not find the expected stub on classpath", inputs, tc.expectedStubPath)
+ }
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index f344648..92e31cd 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -19,6 +19,7 @@
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
"android/soong/android"
)
@@ -210,7 +211,7 @@
// type and the one to use is selected at runtime.
func systemModulesImportFactory() android.Module {
module := &systemModulesImport{}
- module.AddProperties(&module.properties)
+ module.AddProperties(&module.properties, &module.prebuiltProperties)
android.InitPrebuiltModule(module, &module.properties.Libs)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
@@ -219,13 +220,39 @@
type systemModulesImport struct {
SystemModules
- prebuilt android.Prebuilt
+ prebuilt android.Prebuilt
+ prebuiltProperties prebuiltSystemModulesProperties
+}
+
+type prebuiltSystemModulesProperties struct {
+ // Name of the source soong module that gets shadowed by this prebuilt
+ // If unspecified, follows the naming convention that the source module of
+ // the prebuilt is Name() without "prebuilt_" prefix
+ Source_module_name *string
}
func (system *systemModulesImport) Name() string {
return system.prebuilt.Name(system.ModuleBase.Name())
}
+// BaseModuleName returns the source module that will get shadowed by this prebuilt
+// e.g.
+//
+// java_system_modules_import {
+// name: "my_system_modules.v1",
+// source_module_name: "my_system_modules",
+// }
+//
+// java_system_modules_import {
+// name: "my_system_modules.v2",
+// source_module_name: "my_system_modules",
+// }
+//
+// `BaseModuleName` for both will return `my_system_modules`
+func (system *systemModulesImport) BaseModuleName() string {
+ return proptools.StringDefault(system.prebuiltProperties.Source_module_name, system.ModuleBase.Name())
+}
+
func (system *systemModulesImport) Prebuilt() *android.Prebuilt {
return &system.prebuilt
}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 2ceca5d..336dd21 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -15,6 +15,7 @@
package java
import (
+ "fmt"
"testing"
"android/soong/android"
@@ -111,3 +112,85 @@
expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
}
+
+func TestMultipleSystemModulesPrebuilts(t *testing.T) {
+ bp := `
+ // an rdep
+ java_library {
+ name: "foo",
+ sdk_version: "none",
+ system_modules: "my_system_modules",
+ }
+
+ // multiple variations of java_system_modules
+ // source
+ java_system_modules {
+ name: "my_system_modules",
+ libs: ["bar"],
+ }
+ java_library {
+ name: "bar",
+ srcs: ["bar.java"],
+ }
+ // prebuilt "v1"
+ java_system_modules_import {
+ name: "my_system_modules.v1",
+ source_module_name: "my_system_modules",
+ libs: ["bar.v1"],
+ }
+ java_import {
+ name: "bar.v1",
+ source_module_name: "bar",
+ jars: ["bar.v1.jar"],
+ }
+ // prebuilt "v2"
+ java_system_modules_import {
+ name: "my_system_modules.v2",
+ source_module_name: "my_system_modules",
+ libs: ["bar.v2"],
+ }
+ java_import {
+ name: "bar.v2",
+ source_module_name: "bar",
+ jars: ["bar.v2.jar"],
+ }
+
+ // selectors
+ apex_contributions {
+ name: "myapex_contributions",
+ contents: ["%v"],
+ }
+ `
+ testCases := []struct {
+ desc string
+ selectedDependencyName string
+ }{
+ {
+ desc: "Source system_modules is selected using apex_contributions",
+ selectedDependencyName: "my_system_modules",
+ },
+ {
+ desc: "Prebuilt system_modules v1 is selected using apex_contributions",
+ selectedDependencyName: "prebuilt_my_system_modules.v1",
+ },
+ {
+ desc: "Prebuilt system_modules v2 is selected using apex_contributions",
+ selectedDependencyName: "prebuilt_my_system_modules.v2",
+ },
+ }
+
+ for _, tc := range testCases {
+ res := android.GroupFixturePreparers(
+ prepareForJavaTest,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildFlags = map[string]string{
+ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions",
+ }
+ }),
+ ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName))
+
+ // check that rdep gets the correct variation of system_modules
+ hasDep := CheckModuleHasDependency(t, res.TestContext, "foo", "android_common", tc.selectedDependencyName)
+ android.AssertBoolEquals(t, fmt.Sprintf("expected dependency from foo to %s\n", tc.selectedDependencyName), true, hasDep)
+ }
+}
diff --git a/python/scripts/precompile_python.py b/python/scripts/precompile_python.py
index 80e7c76..aa1a5df 100644
--- a/python/scripts/precompile_python.py
+++ b/python/scripts/precompile_python.py
@@ -24,7 +24,12 @@
# This file needs to support both python 2 and 3.
-def process_one_file(info, infile, outzip):
+def process_one_file(name, infile, outzip):
+ # Create a ZipInfo instance with a fixed date to ensure a deterministic output.
+ # Date was chosen to be the same as
+ # https://cs.android.com/android/platform/superproject/main/+/main:build/soong/jar/jar.go;l=36;drc=2863e4535eb65e15f955dc8ed48fa99b1d2a1db5
+ info = zipfile.ZipInfo(filename=name, date_time=(2008, 1, 1, 0, 0, 0))
+
if not info.filename.endswith('.py'):
outzip.writestr(info, infile.read())
return
@@ -37,17 +42,15 @@
with tempfile.NamedTemporaryFile(prefix="Soong_precompile_", delete=False) as tmp:
out_name = tmp.name
try:
- # Ensure deterministic pyc by using the hash rather than timestamp.
- # This is required to improve caching in accelerated builds.
- # Only works on Python 3.7+ (see https://docs.python.org/3/library/py_compile.html#py_compile.PycInvalidationMode)
- # which should cover most updated branches and developer machines.
+ # Ensure a deterministic .pyc output by using the hash rather than the timestamp.
+ # Only works on Python 3.7+
+ # See https://docs.python.org/3/library/py_compile.html#py_compile.PycInvalidationMode
if sys.version_info >= (3, 7):
py_compile.compile(in_name, out_name, info.filename, doraise=True, invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH)
else:
py_compile.compile(in_name, out_name, info.filename, doraise=True)
with open(out_name, 'rb') as f:
info.filename = info.filename + 'c'
- # Use ZipInfo rather than str to reuse timestamps for deterministic zip files.
outzip.writestr(info, f.read())
finally:
os.remove(in_name)
@@ -62,9 +65,9 @@
with open(args.dst_zip, 'wb') as outf, open(args.src_zip, 'rb') as inf:
with zipfile.ZipFile(outf, mode='w') as outzip, zipfile.ZipFile(inf, mode='r') as inzip:
- for info in inzip.infolist():
- with inzip.open(info.filename, mode='r') as inzipf:
- process_one_file(info, inzipf, outzip)
+ for name in inzip.namelist():
+ with inzip.open(name, mode='r') as inzipf:
+ process_one_file(name, inzipf, outzip)
if __name__ == "__main__":
diff --git a/python/test.go b/python/test.go
index 7eb9136..826f353 100644
--- a/python/test.go
+++ b/python/test.go
@@ -158,35 +158,25 @@
}
runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed")
- if runner == "tradefed" {
- p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
- TestConfigProp: p.testProperties.Test_config,
- TestConfigTemplateProp: p.testProperties.Test_config_template,
- TestSuites: p.binaryProperties.Test_suites,
- OptionsForAutogenerated: configs,
- AutoGenConfig: p.binaryProperties.Auto_gen_config,
- DeviceTemplate: "${PythonBinaryHostTestConfigTemplate}",
- HostTemplate: "${PythonBinaryHostTestConfigTemplate}",
- })
- } else if runner == "mobly" {
- if p.testProperties.Test_config != nil || p.testProperties.Test_config_template != nil || p.binaryProperties.Auto_gen_config != nil {
- panic(fmt.Errorf("cannot set test_config, test_config_template or auto_gen_config for mobly test"))
+ template := "${PythonBinaryHostTestConfigTemplate}"
+ if runner == "mobly" {
+ // Add tag to enable Atest mobly runner
+ if !android.InList("mobly", p.testProperties.Test_options.Tags) {
+ p.testProperties.Test_options.Tags = append(p.testProperties.Test_options.Tags, "mobly")
}
-
- for _, testSuite := range p.binaryProperties.Test_suites {
- if testSuite == "cts" {
- configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: "cts"})
- break
- }
- }
- p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
- OptionsForAutogenerated: configs,
- DeviceTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}",
- HostTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}",
- })
- } else {
+ template = "${PythonBinaryHostMoblyTestConfigTemplate}"
+ } else if runner != "tradefed" {
panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner))
}
+ p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{
+ TestConfigProp: p.testProperties.Test_config,
+ TestConfigTemplateProp: p.testProperties.Test_config_template,
+ TestSuites: p.binaryProperties.Test_suites,
+ OptionsForAutogenerated: configs,
+ AutoGenConfig: p.binaryProperties.Auto_gen_config,
+ DeviceTemplate: template,
+ HostTemplate: template,
+ })
for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) {
p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
@@ -228,6 +218,12 @@
entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
}
+ // ATS 2.0 is the test harness for mobly tests and the test config is for ATS 2.0.
+ // Add "v2" suffix to test config name to distinguish it from the config for TF.
+ if proptools.String(p.testProperties.Test_options.Runner) == "mobly" {
+ entries.SetString("LOCAL_TEST_CONFIG_SUFFIX", "v2")
+ }
+
entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))
android.SetAconfigFileMkEntries(&p.ModuleBase, entries, p.mergedAconfigFiles)
diff --git a/rust/afdo.go b/rust/afdo.go
index 323ee36..6116c5e 100644
--- a/rust/afdo.go
+++ b/rust/afdo.go
@@ -44,14 +44,14 @@
if err != nil {
ctx.ModuleErrorf("%s", err.Error())
}
- if fdoProfileName != nil {
+ if fdoProfileName != "" {
actx.AddFarVariationDependencies(
[]blueprint.Variation{
{Mutator: "arch", Variation: actx.Target().ArchVariation()},
{Mutator: "os", Variation: "android"},
},
cc.FdoProfileTag,
- []string{*fdoProfileName}...,
+ []string{fdoProfileName}...,
)
}
}
diff --git a/rust/config/lints.go b/rust/config/lints.go
index 9322981..7770af0 100644
--- a/rust/config/lints.go
+++ b/rust/config/lints.go
@@ -44,6 +44,7 @@
// Default Rust lints that applies to Google-authored modules.
defaultRustcLints = []string{
"-A deprecated",
+ "-A unknown_lints",
"-D missing-docs",
"-D warnings",
"-D unsafe_op_in_unsafe_fn",
@@ -53,6 +54,7 @@
// deny.
defaultClippyLints = []string{
"-A clippy::type-complexity",
+ "-A clippy::unnecessary_fallible_conversions",
"-A clippy::unnecessary-wraps",
"-A clippy::unusual-byte-groupings",
"-A clippy::upper-case-acronyms",
diff --git a/tradefed/config.go b/tradefed/config.go
index 326a006..b015034 100644
--- a/tradefed/config.go
+++ b/tradefed/config.go
@@ -33,6 +33,7 @@
pctx.SourcePathVariable("NativeTestConfigTemplate", "build/make/core/native_test_config_template.xml")
pctx.SourcePathVariable("PythonBinaryHostMoblyTestConfigTemplate", "build/make/core/python_binary_host_mobly_test_config_template.xml")
pctx.SourcePathVariable("PythonBinaryHostTestConfigTemplate", "build/make/core/python_binary_host_test_config_template.xml")
+ pctx.SourcePathVariable("RavenwoodTestConfigTemplate", "build/make/core/ravenwood_test_config_template.xml")
pctx.SourcePathVariable("RustDeviceTestConfigTemplate", "build/make/core/rust_device_test_config_template.xml")
pctx.SourcePathVariable("RustHostTestConfigTemplate", "build/make/core/rust_host_test_config_template.xml")
pctx.SourcePathVariable("RustDeviceBenchmarkConfigTemplate", "build/make/core/rust_device_benchmark_config_template.xml")
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index e004cdc..e7896ab 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -16,8 +16,6 @@
import (
"strings"
-
- "android/soong/android"
)
var androidmk_denylist []string = []string{
@@ -32,16 +30,17 @@
"libcore/",
"libnativehelper/",
"pdk/",
- "toolchain/",
+ // Add back toolchain/ once defensive Android.mk files are removed
+ //"toolchain/",
}
-func blockAndroidMks(androidMks []string) []string {
- return android.FilterListPred(androidMks, func(s string) bool {
+func blockAndroidMks(ctx Context, androidMks []string) {
+ for _, mkFile := range androidMks {
for _, d := range androidmk_denylist {
- if strings.HasPrefix(s, d) {
- return false
+ if strings.HasPrefix(mkFile, d) {
+ ctx.Fatalf("Found blocked Android.mk file: %s. "+
+ "Please see androidmk_denylist.go for the blocked directories and contact build system team if the file should not be blocked.", mkFile)
}
}
- return true
- })
+ }
}
diff --git a/ui/build/finder.go b/ui/build/finder.go
index a114079..573df21 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -128,7 +128,7 @@
// Stop searching a subdirectory recursively after finding an Android.mk.
androidMks := f.FindFirstNamedAt(".", "Android.mk")
- androidMks = blockAndroidMks(androidMks)
+ blockAndroidMks(ctx, androidMks)
err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
if err != nil {
ctx.Fatalf("Could not export module list: %v", err)
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 65e2c8e..2f25a8c 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -93,6 +93,7 @@
"fuser": Allowed,
"gcert": Allowed,
"gcertstatus": Allowed,
+ "gcloud": Allowed,
"getopt": Allowed,
"git": Allowed,
"hexdump": Allowed,
@@ -101,7 +102,6 @@
"javap": Allowed,
"lsof": Allowed,
"openssl": Allowed,
- "prodcertstatus": Allowed,
"pstree": Allowed,
"rsync": Allowed,
"sh": Allowed,