Android 15.0.0 Release 6 (AP4A.241205.013)

-----BEGIN PGP SIGNATURE-----
 
 iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ1IssQAKCRDorT+BmrEO
 eICmAJ4ucooPI46WmQPpM0P59O1mMg6tTQCeN/GI0UvgsQkpgB52QWP9VGXeRg8=
 =mAzQ
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQJLBAABCgA1FiEEHrBYPudH862glXQBzJUERRm+ZmkFAmdYixgXHG1rYmVzdGFz
 QGxpbmVhZ2Vvcy5vcmcACgkQzJUERRm+ZmntQQ/+OQD/4KTpBaQOM+hYmZv2al28
 wZ2EDbuGZcdUIj9o6kgl2x5DJGv7AJ3tZZ5/WKTvAsxVkZ4oISMrR6WkE/kysIaV
 HGiX3JaM8t1bdAbD6HGq7I4KwS2UUTdUJMc9TUSQkT74QwAfhwAwxVM6Yw9CxQUG
 V5nLtYhDj3un9Xb1amiJaV3Mof+Nw/q0d1A3kpc1qH2Ul56f4COnAysa1GPiDrB3
 eHssxIrAhf5+E3Vk5rN+sdoWTVLa9JdOGx4pSQNBnkBwT2HD8FpysVVN4L9DvwwN
 pG6U7pNN5uzPZ1HyQT8D71Y0tDRaX/mT38doPoa0u1shmiBBbMU87vl2woiEAATQ
 0UHI/ZcY9Q8yX1iA08a6raK7VAedxmierfNMTtp2nyMJmoTe818u/WVAYfFYrjYZ
 i5bM0PtAANnQ/6elWC4C57usyLuPcUg4C7cKqPfXerfopEsi8e1v/wHjjyeQhCIK
 xSk11uwD26CgBxkitktn6dySmVhapnqEM53/1bQ8OWcxhlRQjE5019/SpNYysVzD
 Q8ef4Xu2on6NlwfNmicKymvOvIbugnKhl6i4iK9UKMaKmZ/cArpk0/OWu3yHy5OK
 9AZozQHwbF2kwZLHakK9QSxtwgiRBhqLGttu3tsf1+4NDuuCTlj25BwcJgXoF5+7
 Wn7EaVThxf3pkDrYNcE=
 =6ce+
 -----END PGP SIGNATURE-----

Merge tag 'android-15.0.0_r6' into staging/lineage-22.0_merge-android-15.0.0_r6

Android 15.0.0 Release 6 (AP4A.241205.013)

# -----BEGIN PGP SIGNATURE-----
#
# iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ1IssQAKCRDorT+BmrEO
# eICmAJ4ucooPI46WmQPpM0P59O1mMg6tTQCeN/GI0UvgsQkpgB52QWP9VGXeRg8=
# =mAzQ
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri Dec  6 00:44:01 2024 EET
# gpg:                using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78
# gpg: Good signature from "The Android Open Source Project <initial-contribution@android.com>" [marginal]
# gpg: initial-contribution@android.com: Verified 2457 signatures in the past
#      3 years.  Encrypted 4 messages in the past 2 years.
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 4340 D135 70EF 945E 8381  0964 E8AD 3F81 9AB1 0E78

# By Spandan Das (55) and others
# Via Automerger Merge Worker (1031) and others
* tag 'android-15.0.0_r6': (554 commits)
  Revert^3 "Use -target-feature for MTE"
  Enforce exclusive release config component directories
  Possible fix for stat error during globbing
  Remove `prebuilt_apex_module_creator` mutator
  Revert "pass read new storage parameter to java codegen"
  Temporarily bypass apex availability check for /product apexes with a specific prefix
  Remove the internal extractor module created by apex_set
  Remove the internal selector module created by prebuilt_apex
  pass read new storage parameter to java codegen
  Add an incremental parity test.
  Remove internal deapexer module
  Remove deapex support from java_*_import
  Revert^2 "Remove java_sdk_library "magic""
  Revert^2 "Restrict java_sdk_library in libs of java_import and droidstubs modules"
  Revert^2 "Restrict java_sdk_library in libs"
  Revert "Remove java_sdk_library "magic""
  Revert "Restrict java_sdk_library in libs of java_import and dro..."
  Revert "Restrict java_sdk_library in libs"
  Fixes host_required_modules for test_module_config
  Add some log to the build action caching test.
  ...

 Conflicts:
	android/Android.bp
	android/androidmk.go
	android/config.go
	android/defaults.go
	android/module.go
	android/module_context.go
	android/neverallow.go
	android/product_config.go
	android/sdk.go
	android/variable.go
	apex/apex.go
	cc/binary_sdk_member.go
	cc/cc.go
	cc/compiler.go
	cc/library.go
	cc/library_sdk_member.go
	cc/linker.go
	cc/object.go
	java/java.go
	scripts/gen_build_prop.py
	snapshot/host_fake_snapshot.go
	snapshot/host_snapshot.go

Change-Id: I036e0ef3652f680f194dcf11e5a36844bd527221
This commit is contained in:
Michael Bestas 2024-12-10 20:40:20 +02:00
commit fc5575a537
393 changed files with 17208 additions and 13622 deletions

View file

@ -1,5 +1,8 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
default_visibility: [
"//build/soong:__subpackages__",
],
}
subdirs = [
@ -23,6 +26,8 @@ bootstrap_go_package {
srcs: [
"doc.go",
],
// Used by plugins, though probably shouldn't be.
visibility: ["//visibility:public"],
}
//
@ -40,6 +45,7 @@ cc_defaults {
enabled: true,
},
},
defaults_visibility: ["//visibility:public"],
}
//
@ -51,6 +57,7 @@ kernel_headers {
vendor: true,
recovery_available: true,
min_sdk_version: "apex_inherit",
visibility: ["//visibility:public"],
}
cc_genrule {
@ -75,6 +82,7 @@ cc_genrule {
cmd: "$(location) -s $(out) $(in)",
srcs: [":linker"],
out: ["linker.s"],
visibility: ["//bionic/libc"],
}
cc_genrule {
@ -99,12 +107,13 @@ cc_genrule {
cmd: "$(location) -T $(out) $(in)",
srcs: [":linker"],
out: ["linker.script"],
visibility: ["//visibility:public"],
}
// Instantiate the dex_bootjars singleton module.
dex_bootjars {
name: "dex_bootjars",
no_full_install: true,
visibility: ["//visibility:public"],
}
// Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@ -124,6 +133,7 @@ dexpreopt_systemserver_check {
// container for apex_contributions selected using build flags
all_apex_contributions {
name: "all_apex_contributions",
visibility: ["//visibility:public"],
}
product_config {
@ -138,7 +148,7 @@ build_prop {
// Currently, only microdroid and cf system image can refer to system-build.prop
visibility: [
"//device/google/cuttlefish/system_image",
"//packages/modules/Virtualization/microdroid",
"//packages/modules/Virtualization/build/microdroid",
],
}

View file

@ -594,19 +594,13 @@ modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
by all of the vendor's other modules using the normal namespace and visibility
rules.
`soongConfigTraceMutator` enables modules affected by soong config variables to
write outputs into a hashed directory path. It does this by recording accesses
to soong config variables on each module, and then accumulating records of each
module's all dependencies. `m soong_config_trace` builds information about
hashes to `$OUT_DIR/soong/soong_config_trace.json`.
## Build logic
The build logic is written in Go using the
[blueprint](http://godoc.org/github.com/google/blueprint) framework. Build
logic receives module definitions parsed into Go structures using reflection
and produces build rules. The build rules are collected by blueprint and
written to a [ninja](http://ninja-build.org) build file.
[blueprint](https://android.googlesource.com/platform/build/blueprint)
framework. Build logic receives module definitions parsed into Go structures
using reflection and produces build rules. The build rules are collected by
blueprint and written to a [ninja](http://ninja-build.org) build file.
## Environment Variables Config File

View file

@ -15,6 +15,8 @@
package aconfig
import (
"path/filepath"
"slices"
"strings"
"android/soong/android"
@ -22,9 +24,15 @@ import (
"github.com/google/blueprint"
)
type AconfigReleaseConfigValue struct {
ReleaseConfig string
Values []string `blueprint:"mutated"`
}
type DeclarationsModule struct {
android.ModuleBase
android.DefaultableModuleBase
blueprint.IncrementalModule
// Properties for "aconfig_declarations"
properties struct {
@ -34,8 +42,10 @@ type DeclarationsModule struct {
// Release config flag package
Package string
// Values from TARGET_RELEASE / RELEASE_ACONFIG_VALUE_SETS
Values []string `blueprint:"mutated"`
// Values for release configs / RELEASE_ACONFIG_VALUE_SETS
// The current release config is `ReleaseConfig: ""`, others
// are from RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS.
ReleaseConfigValues []AconfigReleaseConfigValue
// Container(system/vendor/apex) that this module belongs to
Container string
@ -57,6 +67,10 @@ func DeclarationsFactory() android.Module {
type implicitValuesTagType struct {
blueprint.BaseDependencyTag
// The release config name for these values.
// Empty string for the actual current release config.
ReleaseConfig string
}
var implicitValuesTag = implicitValuesTagType{}
@ -74,6 +88,13 @@ func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext
ctx.PropertyErrorf("container", "missing container property")
}
// treating system_ext as system partition as we are combining them as one container
// TODO remove this logic once we start enforcing that system_ext cannot be specified as
// container in the container field.
if module.properties.Container == "system_ext" {
module.properties.Container = "system"
}
// Add a dependency on the aconfig_value_sets defined in
// RELEASE_ACONFIG_VALUE_SETS, and add any aconfig_values that
// match our package.
@ -81,6 +102,11 @@ func (module *DeclarationsModule) DepsMutator(ctx android.BottomUpMutatorContext
if len(valuesFromConfig) > 0 {
ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
}
for rcName, valueSets := range ctx.Config().ReleaseAconfigExtraReleaseConfigsValueSets() {
if len(valueSets) > 0 {
ctx.AddDependency(ctx.Module(), implicitValuesTagType{ReleaseConfig: rcName}, valueSets...)
}
}
}
func joinAndPrefix(prefix string, values []string) string {
@ -101,59 +127,103 @@ func optionalVariable(prefix string, value string) string {
return sb.String()
}
// Assemble the actual filename.
// If `rcName` is not empty, then insert "-{rcName}" into the path before the
// file extension.
func assembleFileName(rcName, path string) string {
if rcName == "" {
return path
}
dir, file := filepath.Split(path)
rcName = "-" + rcName
ext := filepath.Ext(file)
base := file[:len(file)-len(ext)]
return dir + base + rcName + ext
}
func (module *DeclarationsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Get the values that came from the global RELEASE_ACONFIG_VALUE_SETS flag
valuesFiles := make([]android.Path, 0)
// Determine which release configs we are processing.
//
// We always process the current release config (empty string).
// We may have been told to also create artifacts for some others.
configs := append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...)
slices.Sort(configs)
values := make(map[string][]string)
valuesFiles := make(map[string][]android.Path, 0)
providerData := android.AconfigReleaseDeclarationsProviderData{}
ctx.VisitDirectDeps(func(dep android.Module) {
if depData, ok := android.OtherModuleProvider(ctx, dep, valueSetProviderKey); ok {
paths, ok := depData.AvailablePackages[module.properties.Package]
if ok {
valuesFiles = append(valuesFiles, paths...)
for _, path := range paths {
module.properties.Values = append(module.properties.Values, path.String())
depTag := ctx.OtherModuleDependencyTag(dep)
for _, config := range configs {
tag := implicitValuesTagType{ReleaseConfig: config}
if depTag == tag {
paths, ok := depData.AvailablePackages[module.properties.Package]
if ok {
valuesFiles[config] = append(valuesFiles[config], paths...)
for _, path := range paths {
values[config] = append(values[config], path.String())
}
}
}
}
}
})
for _, config := range configs {
module.properties.ReleaseConfigValues = append(module.properties.ReleaseConfigValues, AconfigReleaseConfigValue{
ReleaseConfig: config,
Values: values[config],
})
// Intermediate format
declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
intermediateCacheFilePath := android.PathForModuleOut(ctx, "intermediate.pb")
defaultPermission := ctx.Config().ReleaseAconfigFlagDefaultPermission()
inputFiles := make([]android.Path, len(declarationFiles))
copy(inputFiles, declarationFiles)
inputFiles = append(inputFiles, valuesFiles...)
args := map[string]string{
"release_version": ctx.Config().ReleaseVersion(),
"package": module.properties.Package,
"declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
"values": joinAndPrefix(" --values ", module.properties.Values),
"default-permission": optionalVariable(" --default-permission ", defaultPermission),
// Intermediate format
declarationFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
intermediateCacheFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.pb"))
var defaultPermission string
defaultPermission = ctx.Config().ReleaseAconfigFlagDefaultPermission()
if config != "" {
if confPerm, ok := ctx.Config().GetBuildFlag("RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION_" + config); ok {
defaultPermission = confPerm
}
}
inputFiles := make([]android.Path, len(declarationFiles))
copy(inputFiles, declarationFiles)
inputFiles = append(inputFiles, valuesFiles[config]...)
args := map[string]string{
"release_version": ctx.Config().ReleaseVersion(),
"package": module.properties.Package,
"declarations": android.JoinPathsWithPrefix(declarationFiles, "--declarations "),
"values": joinAndPrefix(" --values ", values[config]),
"default-permission": optionalVariable(" --default-permission ", defaultPermission),
}
if len(module.properties.Container) > 0 {
args["container"] = "--container " + module.properties.Container
}
ctx.Build(pctx, android.BuildParams{
Rule: aconfigRule,
Output: intermediateCacheFilePath,
Inputs: inputFiles,
Description: "aconfig_declarations",
Args: args,
})
intermediateDumpFilePath := android.PathForModuleOut(ctx, assembleFileName(config, "intermediate.txt"))
ctx.Build(pctx, android.BuildParams{
Rule: aconfigTextRule,
Output: intermediateDumpFilePath,
Inputs: android.Paths{intermediateCacheFilePath},
Description: "aconfig_text",
})
providerData[config] = android.AconfigDeclarationsProviderData{
Package: module.properties.Package,
Container: module.properties.Container,
Exportable: module.properties.Exportable,
IntermediateCacheOutputPath: intermediateCacheFilePath,
IntermediateDumpOutputPath: intermediateDumpFilePath,
}
}
if len(module.properties.Container) > 0 {
args["container"] = "--container " + module.properties.Container
}
ctx.Build(pctx, android.BuildParams{
Rule: aconfigRule,
Output: intermediateCacheFilePath,
Inputs: inputFiles,
Description: "aconfig_declarations",
Args: args,
})
intermediateDumpFilePath := android.PathForModuleOut(ctx, "intermediate.txt")
ctx.Build(pctx, android.BuildParams{
Rule: aconfigTextRule,
Output: intermediateDumpFilePath,
Inputs: android.Paths{intermediateCacheFilePath},
Description: "aconfig_text",
})
android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, android.AconfigDeclarationsProviderData{
Package: module.properties.Package,
Container: module.properties.Container,
Exportable: module.properties.Exportable,
IntermediateCacheOutputPath: intermediateCacheFilePath,
IntermediateDumpOutputPath: intermediateDumpFilePath,
})
android.SetProvider(ctx, android.AconfigDeclarationsProviderKey, providerData[""])
android.SetProvider(ctx, android.AconfigReleaseDeclarationsProviderKey, providerData)
}
var _ blueprint.Incremental = &DeclarationsModule{}

View file

@ -15,6 +15,7 @@
package aconfig
import (
"slices"
"strings"
"testing"
@ -39,7 +40,7 @@ func TestAconfigDeclarations(t *testing.T) {
module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
// Check that the provider has the right contents
depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
depData, _ := android.OtherModuleProvider(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)
@ -66,7 +67,7 @@ func TestAconfigDeclarationsWithExportableUnset(t *testing.T) {
result := runTest(t, android.FixtureExpectsNoErrors, bp)
module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
depData, _ := android.SingletonModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
depData, _ := android.OtherModuleProvider(result, module, android.AconfigDeclarationsProviderKey)
android.AssertBoolEquals(t, "exportable", depData.Exportable, false)
}
@ -134,3 +135,95 @@ func TestMandatoryProperties(t *testing.T) {
})
}
}
func TestAssembleFileName(t *testing.T) {
testCases := []struct {
name string
releaseConfig string
path string
expectedValue string
}{
{
name: "active release config",
path: "file.path",
expectedValue: "file.path",
},
{
name: "release config FOO",
releaseConfig: "FOO",
path: "file.path",
expectedValue: "file-FOO.path",
},
}
for _, test := range testCases {
actualValue := assembleFileName(test.releaseConfig, test.path)
if actualValue != test.expectedValue {
t.Errorf("Expected %q found %q", test.expectedValue, actualValue)
}
}
}
func TestGenerateAndroidBuildActions(t *testing.T) {
testCases := []struct {
name string
buildFlags map[string]string
bp string
errorHandler android.FixtureErrorHandler
}{
{
name: "generate extra",
buildFlags: map[string]string{
"RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS": "config2",
"RELEASE_ACONFIG_VALUE_SETS": "aconfig_value_set-config1",
"RELEASE_ACONFIG_VALUE_SETS_config2": "aconfig_value_set-config2",
},
bp: `
aconfig_declarations {
name: "module_name",
package: "com.example.package",
container: "com.android.foo",
srcs: [
"foo.aconfig",
"bar.aconfig",
],
}
aconfig_value_set {
name: "aconfig_value_set-config1",
values: []
}
aconfig_value_set {
name: "aconfig_value_set-config2",
values: []
}
`,
},
}
for _, test := range testCases {
fixture := PrepareForTest(t, addBuildFlagsForTest(test.buildFlags))
if test.errorHandler != nil {
fixture = fixture.ExtendWithErrorHandler(test.errorHandler)
}
result := fixture.RunTestWithBp(t, test.bp)
module := result.ModuleForTests("module_name", "").Module().(*DeclarationsModule)
depData, _ := android.OtherModuleProvider(result, module, android.AconfigReleaseDeclarationsProviderKey)
expectedKeys := []string{""}
for _, rc := range strings.Split(test.buildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"], " ") {
expectedKeys = append(expectedKeys, rc)
}
slices.Sort(expectedKeys)
actualKeys := []string{}
for rc := range depData {
actualKeys = append(actualKeys, rc)
}
slices.Sort(actualKeys)
android.AssertStringEquals(t, "provider keys", strings.Join(expectedKeys, " "), strings.Join(actualKeys, " "))
for _, rc := range actualKeys {
if !strings.HasSuffix(depData[rc].IntermediateCacheOutputPath.String(), assembleFileName(rc, "/intermediate.pb")) {
t.Errorf("Incorrect intermediates proto path in provider for release config %s: %s", rc, depData[rc].IntermediateCacheOutputPath.String())
}
if !strings.HasSuffix(depData[rc].IntermediateDumpOutputPath.String(), assembleFileName(rc, "/intermediate.txt")) {
t.Errorf("Incorrect intermediates text path in provider for release config %s: %s", rc, depData[rc].IntermediateDumpOutputPath.String())
}
}
}
}

View file

@ -16,6 +16,9 @@ package aconfig
import (
"android/soong/android"
"fmt"
"strings"
"github.com/google/blueprint"
)
@ -27,6 +30,9 @@ type ValueSetModule struct {
properties struct {
// aconfig_values modules
Values []string
// Paths to the Android.bp files where the aconfig_values modules are defined.
Srcs []string
}
}
@ -56,7 +62,35 @@ type valueSetProviderData struct {
var valueSetProviderKey = blueprint.NewProvider[valueSetProviderData]()
func (module *ValueSetModule) FindAconfigValuesFromSrc(ctx android.BottomUpMutatorContext) map[string]android.Path {
moduleDir := ctx.ModuleDir()
srcs := android.PathsForModuleSrcExcludes(ctx, module.properties.Srcs, []string{ctx.BlueprintsFile()})
aconfigValuesPrefix := strings.Replace(module.Name(), "aconfig_value_set", "aconfig-values", 1)
moduleNamesSrcMap := make(map[string]android.Path)
for _, src := range srcs {
subDir := strings.TrimPrefix(src.String(), moduleDir+"/")
packageName, _, found := strings.Cut(subDir, "/")
if found {
moduleName := fmt.Sprintf("%s-%s-all", aconfigValuesPrefix, packageName)
moduleNamesSrcMap[moduleName] = src
}
}
return moduleNamesSrcMap
}
func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
// TODO: b/366285733 - Replace the file path based solution with more robust solution.
aconfigValuesMap := module.FindAconfigValuesFromSrc(ctx)
for _, moduleName := range android.SortedKeys(aconfigValuesMap) {
if ctx.OtherModuleExists(moduleName) {
ctx.AddDependency(ctx.Module(), valueSetTag, moduleName)
} else {
ctx.ModuleErrorf("module %q not found. Rename the aconfig_values module defined in %q to %q", moduleName, aconfigValuesMap[moduleName], moduleName)
}
}
deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
for _, dep := range deps {
_, ok := dep.(*ValuesModule)

View file

@ -18,6 +18,8 @@ import (
"testing"
"android/soong/android"
"github.com/google/blueprint"
)
func TestAconfigValueSet(t *testing.T) {
@ -38,6 +40,115 @@ func TestAconfigValueSet(t *testing.T) {
module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
// Check that the provider has the right contents
depData, _ := android.SingletonModuleProvider(result, module, valueSetProviderKey)
depData, _ := android.OtherModuleProvider(result, module, valueSetProviderKey)
android.AssertStringEquals(t, "AvailablePackages", "blah.aconfig_values", depData.AvailablePackages["foo.package"][0].String())
}
func TestAconfigValueSetBpGlob(t *testing.T) {
result := android.GroupFixturePreparers(
PrepareForTestWithAconfigBuildComponents,
android.FixtureMergeMockFs(
map[string][]byte{
// .../some_release/android.foo/
"some_release/android.foo/Android.bp": []byte(`
aconfig_values {
name: "aconfig-values-platform_build_release-some_release-android.foo-all",
package: "android.foo",
srcs: [
"*.textproto",
],
}
`),
"some_release/android.foo/flag.textproto": nil,
// .../some_release/android.bar/
"some_release/android.bar/Android.bp": []byte(`
aconfig_values {
name: "aconfig-values-platform_build_release-some_release-android.bar-all",
package: "android.bar",
srcs: [
"*.textproto",
],
}
`),
"some_release/android.bar/flag.textproto": nil,
// .../some_release/
"some_release/Android.bp": []byte(`
aconfig_value_set {
name: "aconfig_value_set-platform_build_release-some_release",
srcs: [
"*/Android.bp",
],
}
`),
},
),
).RunTest(t)
checkModuleHasDependency := func(name, variant, dep string) bool {
t.Helper()
module := result.ModuleForTests(name, variant).Module()
depFound := false
result.VisitDirectDeps(module, func(m blueprint.Module) {
if m.Name() == dep {
depFound = true
}
})
return depFound
}
android.AssertBoolEquals(t,
"aconfig_value_set expected to depend on aconfig_value via srcs",
true,
checkModuleHasDependency(
"aconfig_value_set-platform_build_release-some_release",
"",
"aconfig-values-platform_build_release-some_release-android.foo-all",
),
)
android.AssertBoolEquals(t,
"aconfig_value_set expected to depend on aconfig_value via srcs",
true,
checkModuleHasDependency(
"aconfig_value_set-platform_build_release-some_release",
"",
"aconfig-values-platform_build_release-some_release-android.bar-all",
),
)
}
func TestAconfigValueSetBpGlobError(t *testing.T) {
android.GroupFixturePreparers(
PrepareForTestWithAconfigBuildComponents,
android.FixtureMergeMockFs(
map[string][]byte{
// .../some_release/android.bar/
"some_release/android.bar/Android.bp": []byte(`
aconfig_values {
name: "aconfig-values-platform_build_release-some_release-android_bar-all",
package: "android.bar",
srcs: [
"*.textproto",
],
}
`),
"some_release/android.bar/flag.textproto": nil,
// .../some_release/
"some_release/Android.bp": []byte(`
aconfig_value_set {
name: "aconfig_value_set-platform_build_release-some_release",
srcs: [
"*/Android.bp",
],
}
`),
},
),
).ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
`module "aconfig_value_set-platform_build_release-some_release": module ` +
`"aconfig-values-platform_build_release-some_release-android.bar-all" not found. ` +
`Rename the aconfig_values module defined in "some_release/android.bar/Android.bp" ` +
`to "aconfig-values-platform_build_release-some_release-android.bar-all"`),
).RunTest(t)
}

View file

@ -33,7 +33,7 @@ func TestAconfigValues(t *testing.T) {
module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
// Check that the provider has the right contents
depData, _ := android.SingletonModuleProvider(result, module, valuesProviderKey)
depData, _ := android.OtherModuleProvider(result, module, valuesProviderKey)
android.AssertStringEquals(t, "package", "foo.package", depData.Package)
android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values)
}

View file

@ -15,8 +15,10 @@
package aconfig
import (
"android/soong/android"
"fmt"
"slices"
"android/soong/android"
)
// A singleton module that collects all of the aconfig flags declared in the
@ -27,70 +29,90 @@ import (
// ones that are relevant to the product currently being built, so that that infra
// doesn't need to pull from multiple builds and merge them.
func AllAconfigDeclarationsFactory() android.Singleton {
return &allAconfigDeclarationsSingleton{}
return &allAconfigDeclarationsSingleton{releaseMap: make(map[string]allAconfigReleaseDeclarationsSingleton)}
}
type allAconfigDeclarationsSingleton struct {
type allAconfigReleaseDeclarationsSingleton struct {
intermediateBinaryProtoPath android.OutputPath
intermediateTextProtoPath android.OutputPath
}
type allAconfigDeclarationsSingleton struct {
releaseMap map[string]allAconfigReleaseDeclarationsSingleton
}
func (this *allAconfigDeclarationsSingleton) sortedConfigNames() []string {
var names []string
for k := range this.releaseMap {
names = append(names, k)
}
slices.Sort(names)
return names
}
func (this *allAconfigDeclarationsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
// Find all of the aconfig_declarations modules
var packages = make(map[string]int)
var cacheFiles android.Paths
ctx.VisitAllModules(func(module android.Module) {
decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
if !ok {
return
for _, rcName := range append([]string{""}, ctx.Config().ReleaseAconfigExtraReleaseConfigs()...) {
// Find all of the aconfig_declarations modules
var packages = make(map[string]int)
var cacheFiles android.Paths
ctx.VisitAllModules(func(module android.Module) {
decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigReleaseDeclarationsProviderKey)
if !ok {
return
}
cacheFiles = append(cacheFiles, decl[rcName].IntermediateCacheOutputPath)
packages[decl[rcName].Package]++
})
var numOffendingPkg = 0
for pkg, cnt := range packages {
if cnt > 1 {
fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
numOffendingPkg++
}
}
cacheFiles = append(cacheFiles, decl.IntermediateCacheOutputPath)
packages[decl.Package]++
})
var numOffendingPkg = 0
for pkg, cnt := range packages {
if cnt > 1 {
fmt.Printf("%d aconfig_declarations found for package %s\n", cnt, pkg)
numOffendingPkg++
if numOffendingPkg > 0 {
panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
}
// Generate build action for aconfig (binary proto output)
paths := allAconfigReleaseDeclarationsSingleton{
intermediateBinaryProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.pb")),
intermediateTextProtoPath: android.PathForIntermediates(ctx, assembleFileName(rcName, "all_aconfig_declarations.textproto")),
}
this.releaseMap[rcName] = paths
ctx.Build(pctx, android.BuildParams{
Rule: AllDeclarationsRule,
Inputs: cacheFiles,
Output: this.releaseMap[rcName].intermediateBinaryProtoPath,
Description: "all_aconfig_declarations",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
},
})
ctx.Phony("all_aconfig_declarations", this.releaseMap[rcName].intermediateBinaryProtoPath)
// Generate build action for aconfig (text proto output)
ctx.Build(pctx, android.BuildParams{
Rule: AllDeclarationsRuleTextProto,
Inputs: cacheFiles,
Output: this.releaseMap[rcName].intermediateTextProtoPath,
Description: "all_aconfig_declarations_textproto",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
},
})
ctx.Phony("all_aconfig_declarations_textproto", this.releaseMap[rcName].intermediateTextProtoPath)
}
if numOffendingPkg > 0 {
panic(fmt.Errorf("Only one aconfig_declarations allowed for each package."))
}
// Generate build action for aconfig (binary proto output)
this.intermediateBinaryProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.pb")
ctx.Build(pctx, android.BuildParams{
Rule: AllDeclarationsRule,
Inputs: cacheFiles,
Output: this.intermediateBinaryProtoPath,
Description: "all_aconfig_declarations",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
},
})
ctx.Phony("all_aconfig_declarations", this.intermediateBinaryProtoPath)
// Generate build action for aconfig (text proto output)
this.intermediateTextProtoPath = android.PathForIntermediates(ctx, "all_aconfig_declarations.textproto")
ctx.Build(pctx, android.BuildParams{
Rule: AllDeclarationsRuleTextProto,
Inputs: cacheFiles,
Output: this.intermediateTextProtoPath,
Description: "all_aconfig_declarations_textproto",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(cacheFiles, "--cache "),
},
})
ctx.Phony("all_aconfig_declarations_textproto", this.intermediateTextProtoPath)
}
func (this *allAconfigDeclarationsSingleton) MakeVars(ctx android.MakeVarsContext) {
ctx.DistForGoal("droid", this.intermediateBinaryProtoPath)
for _, goal := range []string{"docs", "droid", "sdk"} {
ctx.DistForGoalWithFilename(goal, this.intermediateBinaryProtoPath, "flags.pb")
ctx.DistForGoalWithFilename(goal, this.intermediateTextProtoPath, "flags.textproto")
for _, rcName := range this.sortedConfigNames() {
ctx.DistForGoal("droid", this.releaseMap[rcName].intermediateBinaryProtoPath)
for _, goal := range []string{"docs", "droid", "sdk"} {
ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateBinaryProtoPath, assembleFileName(rcName, "flags.pb"))
ctx.DistForGoalWithFilename(goal, this.releaseMap[rcName].intermediateTextProtoPath, assembleFileName(rcName, "flags.textproto"))
}
}
}

View file

@ -38,7 +38,7 @@ func (this *allBuildFlagDeclarationsSingleton) GenerateBuildActions(ctx android.
// Find all of the build_flag_declarations modules
var intermediateFiles android.Paths
ctx.VisitAllModules(func(module android.Module) {
decl, ok := android.SingletonModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
decl, ok := android.OtherModuleProvider(ctx, module, BuildFlagDeclarationsProviderKey)
if !ok {
return
}

View file

@ -12,7 +12,6 @@ bootstrap_go_package {
"soong",
"soong-aconfig",
"soong-android",
"soong-bazel",
"soong-java",
"soong-rust",
],

View file

@ -32,6 +32,7 @@ var (
` --mode ${mode}` +
` --cache ${in}` +
` --out ${out}.tmp` +
` --allow-instrumentation ${debug}` +
` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
` && rm -rf ${out}.tmp`,
CommandDeps: []string{
@ -39,7 +40,7 @@ var (
"$soong_zip",
},
Restat: true,
}, "mode")
}, "mode", "debug")
// For cc_aconfig_library: Generate C++ library
cppRule = pctx.AndroidStaticRule("cc_aconfig_library",

View file

@ -20,6 +20,7 @@ import (
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
"strconv"
)
type declarationsTagType struct {
@ -71,6 +72,7 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) DepsMutator(module *ja
module.AddSharedLibrary("aconfig-annotations-lib")
// TODO(b/303773055): Remove the annotation after access issue is resolved.
module.AddSharedLibrary("unsupportedappusage")
module.AddSharedLibrary("aconfig_storage_reader_java")
}
}
@ -102,7 +104,8 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) GenerateSourceJarBuild
Output: srcJarPath,
Description: "aconfig.srcjar",
Args: map[string]string{
"mode": mode,
"mode": mode,
"debug": strconv.FormatBool(ctx.Config().ReleaseReadFromNewStorage()),
},
})

View file

@ -260,7 +260,7 @@ func TestMkEntriesMatchedContainer(t *testing.T) {
aconfig_declarations {
name: "my_aconfig_declarations_bar",
package: "com.example.package.bar",
container: "system_ext",
container: "vendor",
srcs: ["bar.aconfig"],
}

View file

@ -30,7 +30,7 @@ func (this *exportedJavaDeclarationsLibrarySingleton) GenerateBuildActions(ctx a
// Find all of the aconfig_declarations modules
var cacheFiles android.Paths
ctx.VisitAllModules(func(module android.Module) {
decl, ok := android.SingletonModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
decl, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey)
if !ok {
return
}

View file

@ -44,7 +44,7 @@ var (
// For create-device-config-sysprops: Generate aconfig flag value map text file
aconfigTextRule = pctx.AndroidStaticRule("aconfig_text",
blueprint.RuleParams{
Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` +
Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}:{permission}={state:bool}'` +
` --cache ${in}` +
` --out ${out}.tmp` +
` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,

View file

@ -23,7 +23,25 @@ import (
var PrepareForTestWithAconfigBuildComponents = android.FixtureRegisterWithContext(RegisterBuildComponents)
func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
return android.GroupFixturePreparers(PrepareForTestWithAconfigBuildComponents).
return PrepareForTest(t).
ExtendWithErrorHandler(errorHandler).
RunTestWithBp(t, bp)
}
func PrepareForTest(t *testing.T, preparers ...android.FixturePreparer) android.FixturePreparer {
preparers = append([]android.FixturePreparer{PrepareForTestWithAconfigBuildComponents}, preparers...)
return android.GroupFixturePreparers(preparers...)
}
func addBuildFlagsForTest(buildFlags map[string]string) android.FixturePreparer {
return android.GroupFixturePreparers(
android.FixtureModifyProductVariables(func(vars android.FixtureProductVariables) {
if vars.BuildFlags == nil {
vars.BuildFlags = make(map[string]string)
}
for k, v := range buildFlags {
vars.BuildFlags[k] = v
}
}),
)
}

View file

@ -29,4 +29,5 @@ bootstrap_go_package {
"aidl_library_test.go",
],
pluginFor: ["soong_build"],
visibility: ["//visibility:public"],
}

View file

@ -15,8 +15,9 @@
package aidl_library
import (
"android/soong/android"
"testing"
"android/soong/android"
)
func TestAidlLibrary(t *testing.T) {
@ -46,7 +47,7 @@ func TestAidlLibrary(t *testing.T) {
).RunTest(t).TestContext
foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
android.AssertArrayString(
t,
@ -95,7 +96,7 @@ func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) {
).RunTest(t).TestContext
foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
actualInfo, _ := android.SingletonModuleProvider(ctx, foo, AidlLibraryProvider)
actualInfo, _ := android.OtherModuleProvider(ctx, foo, AidlLibraryProvider)
android.AssertArrayString(
t,

View file

@ -39,7 +39,10 @@ bootstrap_go_package {
"arch_module_context.go",
"base_module_context.go",
"build_prop.go",
"compliance_metadata.go",
"config.go",
"container_violations.go",
"container.go",
"test_config.go",
"configurable_properties.go",
"configured_jars.go",
@ -56,6 +59,7 @@ bootstrap_go_package {
"gen_notice.go",
"hooks.go",
"image.go",
"init.go",
"license.go",
"license_kind.go",
"license_metadata.go",
@ -84,12 +88,14 @@ bootstrap_go_package {
"prebuilt.go",
"prebuilt_build_tool.go",
"product_config.go",
"product_config_to_bp.go",
"proto.go",
"provider.go",
"raw_files.go",
"register.go",
"rule_builder.go",
"sandbox.go",
"sbom.go",
"sdk.go",
"sdk_version.go",
"shared_properties.go",
@ -103,6 +109,7 @@ bootstrap_go_package {
"updatable_modules.go",
"util.go",
"variable.go",
"vintf_fragment.go",
"visibility.go",
],
testSrcs: [
@ -145,6 +152,9 @@ bootstrap_go_package {
"test_suites_test.go",
"util_test.go",
"variable_test.go",
"vintf_fragment_test.go",
"visibility_test.go",
],
// Used by plugins
visibility: ["//visibility:public"],
}

View file

@ -43,6 +43,10 @@ type AconfigDeclarationsProviderData struct {
var AconfigDeclarationsProviderKey = blueprint.NewProvider[AconfigDeclarationsProviderData]()
type AconfigReleaseDeclarationsProviderData map[string]AconfigDeclarationsProviderData
var AconfigReleaseDeclarationsProviderKey = blueprint.NewProvider[AconfigReleaseDeclarationsProviderData]()
type ModeInfo struct {
Container string
Mode string
@ -112,6 +116,8 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
}
// If we were generating on-device artifacts for other release configs, we would need to add code here to propagate
// those artifacts as well. See also b/298444886.
if dep, ok := OtherModuleProvider(ctx, module, AconfigPropagatingProviderKey); ok {
for container, v := range dep.AconfigFiles {
mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
@ -130,12 +136,12 @@ func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
AconfigFiles: mergedAconfigFiles,
ModeInfos: mergedModeInfos,
})
ctx.Module().base().aconfigFilePaths = getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles)
ctx.setAconfigPaths(getAconfigFilePaths(ctx.Module().base(), mergedAconfigFiles))
}
}
func aconfigUpdateAndroidMkData(ctx fillInEntriesContext, mod Module, data *AndroidMkData) {
info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
// If there is no aconfigPropagatingProvider, or there are no AconfigFiles, then we are done.
if !ok || len(info.AconfigFiles) == 0 {
return
@ -166,7 +172,7 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries
if len(*entries) == 0 {
return
}
info, ok := SingletonModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
if !ok || len(info.AconfigFiles) == 0 {
return
}
@ -181,6 +187,20 @@ func aconfigUpdateAndroidMkEntries(ctx fillInEntriesContext, mod Module, entries
}
}
func aconfigUpdateAndroidMkInfos(ctx fillInEntriesContext, mod Module, infos *AndroidMkProviderInfo) {
info, ok := OtherModuleProvider(ctx, mod, AconfigPropagatingProviderKey)
if !ok || len(info.AconfigFiles) == 0 {
return
}
// All of the files in the module potentially depend on the aconfig flag values.
infos.PrimaryInfo.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
if len(infos.ExtraInfo) > 0 {
for _, ei := range (*infos).ExtraInfo {
ei.AddPaths("LOCAL_ACONFIG_FILES", getAconfigFilePaths(mod.base(), info.AconfigFiles))
}
}
}
func mergeAconfigFiles(ctx ModuleContext, container string, inputs Paths, generateRule bool) Paths {
inputs = SortedUniquePaths(inputs)
if len(inputs) == 1 {
@ -213,7 +233,8 @@ func getAconfigFilePaths(m *ModuleBase, aconfigFiles map[string]Paths) (paths Pa
} else if m.ProductSpecific() {
container = "product"
} else if m.SystemExtSpecific() {
container = "system_ext"
// system_ext and system partitions should be treated as one container
container = "system"
}
paths = append(paths, aconfigFiles[container]...)

View file

@ -1,9 +1,11 @@
package android
import (
"android/soong/android/team_proto"
"path"
"path/filepath"
"android/soong/android/team_proto"
"google.golang.org/protobuf/proto"
)
@ -93,7 +95,7 @@ func (t *allTeamsSingleton) GenerateBuildActions(ctx SingletonContext) {
}
testModInfo := TestModuleInformation{}
if tmi, ok := SingletonModuleProvider(ctx, module, TestOnlyProviderKey); ok {
if tmi, ok := OtherModuleProvider(ctx, module, TestOnlyProviderKey); ok {
testModInfo = tmi
}
@ -152,6 +154,11 @@ func (t *allTeamsSingleton) lookupTeamForAllModules() *team_proto.AllTeams {
} else {
teamProperties, found = t.lookupDefaultTeam(m.bpFile)
}
// Deal with one blueprint file including another by looking up the default
// in the main Android.bp rather than one listed with "build = [My.bp]"
if !found {
teamProperties, found = t.lookupDefaultTeam(path.Join(path.Dir(m.bpFile), "Android.bp"))
}
trendy_team_id := ""
if found {

View file

@ -264,6 +264,84 @@ func TestPackageLookup(t *testing.T) {
AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
}
func TestPackageLookupForIncludedBlueprintFiles(t *testing.T) {
t.Parallel()
rootBp := `
package { default_team: "team_top"}
team {
name: "team_top",
trendy_team_id: "trendy://team_top",
}
build = ["include.bp"]
`
includeBp := `
fake {
name: "IncludedModule",
} `
ctx := GroupFixturePreparers(
prepareForTestWithTeamAndFakes,
PrepareForTestWithPackageModule,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
}),
FixtureAddTextFile("Android.bp", rootBp),
FixtureAddTextFile("include.bp", includeBp),
).RunTest(t)
var teams *team_proto.AllTeams
teams = getTeamProtoOutput(t, ctx)
// map of module name -> trendy team name.
actualTeams := make(map[string]*string)
for _, teamProto := range teams.Teams {
actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
}
expectedTeams := map[string]*string{
"IncludedModule": proto.String("trendy://team_top"),
}
AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
}
func TestPackageLookupForIncludedBlueprintFilesWithPackageInChildBlueprint(t *testing.T) {
t.Parallel()
rootBp := `
team {
name: "team_top",
trendy_team_id: "trendy://team_top",
}
build = ["include.bp"]
`
includeBp := `
package { default_team: "team_top"}
fake {
name: "IncludedModule",
} `
ctx := GroupFixturePreparers(
prepareForTestWithTeamAndFakes,
PrepareForTestWithPackageModule,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterParallelSingletonType("all_teams", AllTeamsFactory)
}),
FixtureAddTextFile("Android.bp", rootBp),
FixtureAddTextFile("include.bp", includeBp),
).RunTest(t)
var teams *team_proto.AllTeams
teams = getTeamProtoOutput(t, ctx)
// map of module name -> trendy team name.
actualTeams := make(map[string]*string)
for _, teamProto := range teams.Teams {
actualTeams[teamProto.GetTargetName()] = teamProto.TrendyTeamId
}
expectedTeams := map[string]*string{
"IncludedModule": proto.String("trendy://team_top"),
}
AssertDeepEquals(t, "compare maps", expectedTeams, actualTeams)
}
type fakeForTests struct {
ModuleBase

View file

@ -34,7 +34,6 @@ import (
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
@ -157,6 +156,7 @@ type AndroidMkEntries struct {
}
type AndroidMkEntriesContext interface {
OtherModuleProviderContext
Config() Config
}
@ -170,7 +170,7 @@ type androidMkExtraEntriesContext struct {
}
func (a *androidMkExtraEntriesContext) Provider(provider blueprint.AnyProviderKey) (any, bool) {
return a.ctx.moduleProvider(a.mod, provider)
return a.ctx.otherModuleProvider(a.mod, provider)
}
type AndroidMkExtraEntriesFunc func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries)
@ -354,14 +354,15 @@ func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContr
availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
}
info := OtherModuleProviderOrDefault(a.entryContext, mod, InstallFilesProvider)
// If the distFiles created by GenerateTaggedDistFiles contains paths for the
// DefaultDistTag then that takes priority so delete any existing paths.
if _, ok := amod.distFiles[DefaultDistTag]; ok {
if _, ok := info.DistFiles[DefaultDistTag]; ok {
delete(availableTaggedDists, DefaultDistTag)
}
// Finally, merge the distFiles created by GenerateTaggedDistFiles.
availableTaggedDists = availableTaggedDists.merge(amod.distFiles)
availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
if len(availableTaggedDists) == 0 {
// Nothing dist-able for this module.
@ -372,7 +373,7 @@ func (a *AndroidMkEntries) getDistContributions(mod blueprint.Module) *distContr
distContributions := &distContributions{}
if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
distContributions.licenseMetadataFile = amod.licenseMetadataFile
distContributions.licenseMetadataFile = info.LicenseMetadataFile
}
// Iterate over this module's dist structs, merged from the dist and dists properties.
@ -497,9 +498,10 @@ type fillInEntriesContext interface {
ModuleDir(module blueprint.Module) string
ModuleSubDir(module blueprint.Module) string
Config() Config
moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
ModuleType(module blueprint.Module) string
OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{})
HasMutatorFinished(mutatorName string) bool
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@ -516,6 +518,7 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
a.Include = "$(BUILD_PREBUILT)"
}
a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
@ -536,13 +539,14 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
a.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
// If the install rule was generated by Soong tell Make about it.
if len(base.katiInstalls) > 0 {
info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
if len(info.KatiInstalls) > 0 {
// Assume the primary install file is last since it probably needs to depend on any other
// installed files. If that is not the case we can add a method to specify the primary
// installed file.
a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
a.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
} else {
// Soong may not have generated the install rule also when `no_full_install: true`.
// Mark this module as uninstallable in order to prevent Make from creating an
@ -550,8 +554,16 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
}
if len(base.testData) > 0 {
a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(base.testData)...)
if info.UncheckedModule {
a.SetBool("LOCAL_DONT_CHECK_MODULE", true)
} else if info.CheckbuildTarget != nil {
a.SetPath("LOCAL_CHECKED_MODULE", info.CheckbuildTarget)
} else {
a.SetOptionalPath("LOCAL_CHECKED_MODULE", a.OutputFile)
}
if len(info.TestData) > 0 {
a.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
}
if am, ok := mod.(ApexModule); ok {
@ -588,10 +600,10 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
}
if !base.InVendorRamdisk() {
a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths)
a.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
}
if len(base.vintfFragmentsPaths) > 0 {
a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", base.vintfFragmentsPaths)
if len(info.VintfFragmentsPaths) > 0 {
a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
}
a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
@ -633,11 +645,11 @@ func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint
}
}
if licenseMetadata, ok := SingletonModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
}
if _, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
a.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
}
@ -794,15 +806,19 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs
// Additional cases here require review for correct license propagation to make.
var err error
switch x := mod.(type) {
case AndroidMkDataProvider:
err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
case bootstrap.GoBinaryTool:
err = translateGoBinaryModule(ctx, w, mod, x)
case AndroidMkEntriesProvider:
err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
default:
// Not exported to make so no make variables to set.
if info, ok := ctx.otherModuleProvider(mod, AndroidMkInfoProvider); ok {
androidMkEntriesInfos := info.(*AndroidMkProviderInfo)
err = translateAndroidMkEntriesInfoModule(ctx, w, moduleInfoJSONs, mod, androidMkEntriesInfos)
} else {
switch x := mod.(type) {
case AndroidMkDataProvider:
err = translateAndroidModule(ctx, w, moduleInfoJSONs, mod, x)
case AndroidMkEntriesProvider:
err = translateAndroidMkEntriesModule(ctx, w, moduleInfoJSONs, mod, x)
default:
// Not exported to make so no make variables to set.
}
}
if err != nil {
@ -812,23 +828,6 @@ func translateAndroidMkModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs
return err
}
// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
// m by making them phony targets.
func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
goBinary bootstrap.GoBinaryTool) error {
name := ctx.ModuleName(mod)
fmt.Fprintln(w, ".PHONY:", name)
fmt.Fprintln(w, name+":", goBinary.InstallPath())
fmt.Fprintln(w, "")
// Assuming no rules in make include go binaries in distributables.
// If the assumption is wrong, make will fail to build without the necessary .meta_lic and .meta_module files.
// In that case, add the targets and rules here to build a .meta_lic file for `name` and a .meta_module for
// `goBinary.InstallPath()` pointing to the `name`.meta_lic file.
return nil
}
func (data *AndroidMkData) fillInData(ctx fillInEntriesContext, mod blueprint.Module) {
// Get the preamble content through AndroidMkEntries logic.
data.Entries = AndroidMkEntries{
@ -861,6 +860,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *
}
data := provider.AndroidMk()
if data.Include == "" {
data.Include = "$(BUILD_PREBUILT)"
}
@ -900,6 +900,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *
case "*android_sdk.sdkRepoHost": // doesn't go through base_rules
case "*apex.apexBundle": // license properties written
case "*bpf.bpf": // license properties written (both for module and objs)
case "*libbpf_prog.libbpfProg": // license properties written (both for module and objs)
case "*genrule.Module": // writes non-custom before adding .phony
case "*java.SystemModules": // doesn't go through base_rules
case "*java.systemModulesImport": // doesn't go through base_rules
@ -907,6 +908,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *
case "*phony.PhonyRule": // writes phony deps and acts like `.PHONY`
case "*selinux.selinuxContextsModule": // license properties written
case "*sysprop.syspropLibrary": // license properties written
case "*vintf.vintfCompatibilityMatrixRule": // use case like phony
default:
if !ctx.Config().IsEnvFalse("ANDROID_REQUIRE_LICENSES") {
return fmt.Errorf("custom make rules not allowed for %q (%q) module %q", ctx.ModuleType(mod), reflect.TypeOf(mod), ctx.ModuleName(mod))
@ -918,7 +920,7 @@ func translateAndroidModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *
}
if !data.Entries.disabled() {
if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
}
}
@ -960,7 +962,7 @@ func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleIn
}
if len(entriesList) > 0 && !entriesList[0].disabled() {
if moduleInfoJSON, ok := SingletonModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
}
}
@ -968,11 +970,11 @@ func translateAndroidMkEntriesModule(ctx SingletonContext, w io.Writer, moduleIn
return nil
}
func ShouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module Module) bool {
func ShouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module Module) bool {
return shouldSkipAndroidMkProcessing(ctx, module.base())
}
func shouldSkipAndroidMkProcessing(ctx ConfigAndErrorContext, module *ModuleBase) bool {
func shouldSkipAndroidMkProcessing(ctx ConfigurableEvaluatorContext, module *ModuleBase) bool {
if !module.commonProperties.NamespaceExportedToMake {
// TODO(jeffrygaston) do we want to validate that there are no modules being
// exported to Kati that depend on this module?
@ -1048,3 +1050,564 @@ func AndroidMkEmitAssignList(w io.Writer, varName string, lists ...[]string) {
}
fmt.Fprintln(w)
}
type AndroidMkProviderInfo struct {
PrimaryInfo AndroidMkInfo
ExtraInfo []AndroidMkInfo
}
type AndroidMkInfo struct {
// Android.mk class string, e.g. EXECUTABLES, JAVA_LIBRARIES, ETC
Class string
// Optional suffix to append to the module name. Useful when a module wants to return multiple
// AndroidMkEntries objects. For example, when a java_library returns an additional entry for
// its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
// different name than the parent's.
SubName string
// If set, this value overrides the base module name. SubName is still appended.
OverrideName string
// Dist files to output
DistFiles TaggedDistFiles
// The output file for Kati to process and/or install. If absent, the module is skipped.
OutputFile OptionalPath
// If true, the module is skipped and does not appear on the final Android-<product name>.mk
// file. Useful when a module needs to be skipped conditionally.
Disabled bool
// The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk
// If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
Include string
// Required modules that need to be built and included in the final build output when building
// this module.
Required []string
// Required host modules that need to be built and included in the final build output when
// building this module.
Host_required []string
// Required device modules that need to be built and included in the final build output when
// building this module.
Target_required []string
HeaderStrings []string
FooterStrings []string
// A map that holds the up-to-date Make variable values. Can be accessed from tests.
EntryMap map[string][]string
// A list of EntryMap keys in insertion order. This serves a few purposes:
// 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
// the outputted Android-*.mk file may change even though there have been no content changes.
// 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
// without worrying about the variables being mixed up in the actual mk file.
// 3. Makes troubleshooting and spotting errors easier.
EntryOrder []string
}
// TODO: rename it to AndroidMkEntriesProvider after AndroidMkEntriesProvider interface is gone.
var AndroidMkInfoProvider = blueprint.NewProvider[*AndroidMkProviderInfo]()
func translateAndroidMkEntriesInfoModule(ctx SingletonContext, w io.Writer, moduleInfoJSONs *[]*ModuleInfoJSON,
mod blueprint.Module, providerInfo *AndroidMkProviderInfo) error {
if shouldSkipAndroidMkProcessing(ctx, mod.(Module).base()) {
return nil
}
// Deep copy the provider info since we need to modify the info later
info := deepCopyAndroidMkProviderInfo(providerInfo)
aconfigUpdateAndroidMkInfos(ctx, mod.(Module), &info)
// Any new or special cases here need review to verify correct propagation of license information.
info.PrimaryInfo.fillInEntries(ctx, mod)
info.PrimaryInfo.write(w)
if len(info.ExtraInfo) > 0 {
for _, ei := range info.ExtraInfo {
ei.fillInEntries(ctx, mod)
ei.write(w)
}
}
if !info.PrimaryInfo.disabled() {
if moduleInfoJSON, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
*moduleInfoJSONs = append(*moduleInfoJSONs, moduleInfoJSON)
}
}
return nil
}
// Utility funcs to manipulate Android.mk variable entries.
// SetString sets a Make variable with the given name to the given value.
func (a *AndroidMkInfo) SetString(name, value string) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = []string{value}
}
// SetPath sets a Make variable with the given name to the given path string.
func (a *AndroidMkInfo) SetPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = []string{path.String()}
}
// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
// It is a no-op if the given path is invalid.
func (a *AndroidMkInfo) SetOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.SetPath(name, path.Path())
}
}
// AddPath appends the given path string to a Make variable with the given name.
func (a *AndroidMkInfo) AddPath(name string, path Path) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = append(a.EntryMap[name], path.String())
}
// AddOptionalPath appends the given path string to a Make variable with the given name if it is
// valid. It is a no-op if the given path is invalid.
func (a *AndroidMkInfo) AddOptionalPath(name string, path OptionalPath) {
if path.Valid() {
a.AddPath(name, path.Path())
}
}
// SetPaths sets a Make variable with the given name to a slice of the given path strings.
func (a *AndroidMkInfo) SetPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = paths.Strings()
}
// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
// only if there are a non-zero amount of paths.
func (a *AndroidMkInfo) SetOptionalPaths(name string, paths Paths) {
if len(paths) > 0 {
a.SetPaths(name, paths)
}
}
// AddPaths appends the given path strings to a Make variable with the given name.
func (a *AndroidMkInfo) AddPaths(name string, paths Paths) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
}
// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
// It is a no-op if the given flag is false.
func (a *AndroidMkInfo) SetBoolIfTrue(name string, flag bool) {
if flag {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = []string{"true"}
}
}
// SetBool sets a Make variable with the given name to if the given bool flag value.
func (a *AndroidMkInfo) SetBool(name string, flag bool) {
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
if flag {
a.EntryMap[name] = []string{"true"}
} else {
a.EntryMap[name] = []string{"false"}
}
}
// AddStrings appends the given strings to a Make variable with the given name.
func (a *AndroidMkInfo) AddStrings(name string, value ...string) {
if len(value) == 0 {
return
}
if _, ok := a.EntryMap[name]; !ok {
a.EntryOrder = append(a.EntryOrder, name)
}
a.EntryMap[name] = append(a.EntryMap[name], value...)
}
// AddCompatibilityTestSuites adds the supplied test suites to the EntryMap, with special handling
// for partial MTS and MCTS test suites.
func (a *AndroidMkInfo) AddCompatibilityTestSuites(suites ...string) {
// M(C)TS supports a full test suite and partial per-module MTS test suites, with naming mts-${MODULE}.
// To reduce repetition, if we find a partial M(C)TS test suite without an full M(C)TS test suite,
// we add the full test suite to our list.
if PrefixInList(suites, "mts-") && !InList("mts", suites) {
suites = append(suites, "mts")
}
if PrefixInList(suites, "mcts-") && !InList("mcts", suites) {
suites = append(suites, "mcts")
}
a.AddStrings("LOCAL_COMPATIBILITY_SUITE", suites...)
}
func (a *AndroidMkInfo) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
helperInfo := AndroidMkInfo{
EntryMap: make(map[string][]string),
}
amod := mod.(Module)
base := amod.base()
name := base.BaseModuleName()
if a.OverrideName != "" {
name = a.OverrideName
}
if a.Include == "" {
a.Include = "$(BUILD_PREBUILT)"
}
a.Required = append(a.Required, amod.RequiredModuleNames(ctx)...)
a.Required = append(a.Required, amod.VintfFragmentModuleNames(ctx)...)
a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
for _, distString := range a.GetDistForGoals(ctx, mod) {
a.HeaderStrings = append(a.HeaderStrings, distString)
}
a.HeaderStrings = append(a.HeaderStrings, fmt.Sprintf("\ninclude $(CLEAR_VARS) # type: %s, name: %s, variant: %s\n", ctx.ModuleType(mod), base.BaseModuleName(), ctx.ModuleSubDir(mod)))
// Collect make variable assignment entries.
helperInfo.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
helperInfo.SetString("LOCAL_MODULE", name+a.SubName)
helperInfo.SetString("LOCAL_MODULE_CLASS", a.Class)
helperInfo.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
helperInfo.AddStrings("LOCAL_REQUIRED_MODULES", a.Required...)
helperInfo.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
helperInfo.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
helperInfo.AddStrings("LOCAL_SOONG_MODULE_TYPE", ctx.ModuleType(amod))
// If the install rule was generated by Soong tell Make about it.
info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
if len(info.KatiInstalls) > 0 {
// Assume the primary install file is last since it probably needs to depend on any other
// installed files. If that is not the case we can add a method to specify the primary
// installed file.
helperInfo.SetPath("LOCAL_SOONG_INSTALLED_MODULE", info.KatiInstalls[len(info.KatiInstalls)-1].to)
helperInfo.SetString("LOCAL_SOONG_INSTALL_PAIRS", info.KatiInstalls.BuiltInstalled())
helperInfo.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", info.KatiSymlinks.InstallPaths().Paths())
} else {
// Soong may not have generated the install rule also when `no_full_install: true`.
// Mark this module as uninstallable in order to prevent Make from creating an
// install rule there.
helperInfo.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
}
if len(info.TestData) > 0 {
helperInfo.AddStrings("LOCAL_TEST_DATA", androidMkDataPaths(info.TestData)...)
}
if am, ok := mod.(ApexModule); ok {
helperInfo.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
}
archStr := base.Arch().ArchType.String()
host := false
switch base.Os().Class {
case Host:
if base.Target().HostCross {
// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
if base.Arch().ArchType != Common {
helperInfo.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
}
} else {
// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
if base.Arch().ArchType != Common {
helperInfo.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
}
}
host = true
case Device:
// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
if base.Arch().ArchType != Common {
if base.Target().NativeBridge {
hostArchStr := base.Target().NativeBridgeHostArchName
if hostArchStr != "" {
helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
}
} else {
helperInfo.SetString("LOCAL_MODULE_TARGET_ARCH", archStr)
}
}
if !base.InVendorRamdisk() {
helperInfo.AddPaths("LOCAL_FULL_INIT_RC", info.InitRcPaths)
}
if len(info.VintfFragmentsPaths) > 0 {
helperInfo.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", info.VintfFragmentsPaths)
}
helperInfo.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
helperInfo.SetString("LOCAL_VENDOR_MODULE", "true")
}
helperInfo.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
helperInfo.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
helperInfo.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
if base.commonProperties.Owner != nil {
helperInfo.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
}
}
if host {
makeOs := base.Os().String()
if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
makeOs = "linux"
}
helperInfo.SetString("LOCAL_MODULE_HOST_OS", makeOs)
helperInfo.SetString("LOCAL_IS_HOST_MODULE", "true")
}
prefix := ""
if base.ArchSpecific() {
switch base.Os().Class {
case Host:
if base.Target().HostCross {
prefix = "HOST_CROSS_"
} else {
prefix = "HOST_"
}
case Device:
prefix = "TARGET_"
}
if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
prefix = "2ND_" + prefix
}
}
if licenseMetadata, ok := OtherModuleProvider(ctx, mod, LicenseMetadataProvider); ok {
helperInfo.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
}
if _, ok := OtherModuleProvider(ctx, mod, ModuleInfoJSONProvider); ok {
helperInfo.SetBool("LOCAL_SOONG_MODULE_INFO_JSON", true)
}
a.mergeEntries(&helperInfo)
// Write to footer.
a.FooterStrings = append([]string{"include " + a.Include}, a.FooterStrings...)
}
// This method merges the entries to helperInfo, then replaces a's EntryMap and
// EntryOrder with helperInfo's
func (a *AndroidMkInfo) mergeEntries(helperInfo *AndroidMkInfo) {
for _, extraEntry := range a.EntryOrder {
if v, ok := helperInfo.EntryMap[extraEntry]; ok {
v = append(v, a.EntryMap[extraEntry]...)
} else {
helperInfo.EntryMap[extraEntry] = a.EntryMap[extraEntry]
helperInfo.EntryOrder = append(helperInfo.EntryOrder, extraEntry)
}
}
a.EntryOrder = helperInfo.EntryOrder
a.EntryMap = helperInfo.EntryMap
}
func (a *AndroidMkInfo) disabled() bool {
return a.Disabled || !a.OutputFile.Valid()
}
// write flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
// given Writer object.
func (a *AndroidMkInfo) write(w io.Writer) {
if a.disabled() {
return
}
combinedHeaderString := strings.Join(a.HeaderStrings, "\n")
combinedFooterString := strings.Join(a.FooterStrings, "\n")
w.Write([]byte(combinedHeaderString))
for _, name := range a.EntryOrder {
AndroidMkEmitAssignList(w, name, a.EntryMap[name])
}
w.Write([]byte(combinedFooterString))
}
// Compute the list of Make strings to declare phony goals and dist-for-goals
// calls from the module's dist and dists properties.
func (a *AndroidMkInfo) GetDistForGoals(ctx fillInEntriesContext, mod blueprint.Module) []string {
distContributions := a.getDistContributions(ctx, mod)
if distContributions == nil {
return nil
}
return generateDistContributionsForMake(distContributions)
}
// Compute the contributions that the module makes to the dist.
func (a *AndroidMkInfo) getDistContributions(ctx fillInEntriesContext, mod blueprint.Module) *distContributions {
amod := mod.(Module).base()
name := amod.BaseModuleName()
// Collate the set of associated tag/paths available for copying to the dist.
// Start with an empty (nil) set.
var availableTaggedDists TaggedDistFiles
// Then merge in any that are provided explicitly by the module.
if a.DistFiles != nil {
// Merge the DistFiles into the set.
availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
}
// If no paths have been provided for the DefaultDistTag and the output file is
// valid then add that as the default dist path.
if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
}
info := OtherModuleProviderOrDefault(ctx, mod, InstallFilesProvider)
// If the distFiles created by GenerateTaggedDistFiles contains paths for the
// DefaultDistTag then that takes priority so delete any existing paths.
if _, ok := info.DistFiles[DefaultDistTag]; ok {
delete(availableTaggedDists, DefaultDistTag)
}
// Finally, merge the distFiles created by GenerateTaggedDistFiles.
availableTaggedDists = availableTaggedDists.merge(info.DistFiles)
if len(availableTaggedDists) == 0 {
// Nothing dist-able for this module.
return nil
}
// Collate the contributions this module makes to the dist.
distContributions := &distContributions{}
if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
distContributions.licenseMetadataFile = info.LicenseMetadataFile
}
// Iterate over this module's dist structs, merged from the dist and dists properties.
for _, dist := range amod.Dists() {
// Get the list of goals this dist should be enabled for. e.g. sdk, droidcore
goals := strings.Join(dist.Targets, " ")
// Get the tag representing the output files to be dist'd. e.g. ".jar", ".proguard_map"
var tag string
if dist.Tag == nil {
// If the dist struct does not specify a tag, use the default output files tag.
tag = DefaultDistTag
} else {
tag = *dist.Tag
}
// Get the paths of the output files to be dist'd, represented by the tag.
// Can be an empty list.
tagPaths := availableTaggedDists[tag]
if len(tagPaths) == 0 {
// Nothing to dist for this tag, continue to the next dist.
continue
}
if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
"file for %q goals tag %q in module %s. The list of dist files, " +
"which should have a single element, is:\n%s"
panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
}
copiesForGoals := distContributions.getCopiesForGoals(goals)
// Iterate over each path adding a copy instruction to copiesForGoals
for _, path := range tagPaths {
// It's possible that the Path is nil from errant modules. Be defensive here.
if path == nil {
tagName := "default" // for error message readability
if dist.Tag != nil {
tagName = *dist.Tag
}
panic(fmt.Errorf("Dist file should not be nil for the %s tag in %s", tagName, name))
}
dest := filepath.Base(path.String())
if dist.Dest != nil {
var err error
if dest, err = validateSafePath(*dist.Dest); err != nil {
// This was checked in ModuleBase.GenerateBuildActions
panic(err)
}
}
ext := filepath.Ext(dest)
suffix := ""
if dist.Suffix != nil {
suffix = *dist.Suffix
}
productString := ""
if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
}
if suffix != "" || productString != "" {
dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
}
if dist.Dir != nil {
var err error
if dest, err = validateSafePath(*dist.Dir, dest); err != nil {
// This was checked in ModuleBase.GenerateBuildActions
panic(err)
}
}
copiesForGoals.addCopyInstruction(path, dest)
}
}
return distContributions
}
func deepCopyAndroidMkProviderInfo(providerInfo *AndroidMkProviderInfo) AndroidMkProviderInfo {
info := AndroidMkProviderInfo{
PrimaryInfo: deepCopyAndroidMkInfo(&providerInfo.PrimaryInfo),
}
if len(providerInfo.ExtraInfo) > 0 {
for _, i := range providerInfo.ExtraInfo {
info.ExtraInfo = append(info.ExtraInfo, deepCopyAndroidMkInfo(&i))
}
}
return info
}
func deepCopyAndroidMkInfo(mkinfo *AndroidMkInfo) AndroidMkInfo {
info := AndroidMkInfo{
Class: mkinfo.Class,
SubName: mkinfo.SubName,
OverrideName: mkinfo.OverrideName,
// There is no modification on DistFiles or OutputFile, so no need to
// make their deep copy.
DistFiles: mkinfo.DistFiles,
OutputFile: mkinfo.OutputFile,
Disabled: mkinfo.Disabled,
Include: mkinfo.Include,
Required: deepCopyStringSlice(mkinfo.Required),
Host_required: deepCopyStringSlice(mkinfo.Host_required),
Target_required: deepCopyStringSlice(mkinfo.Target_required),
HeaderStrings: deepCopyStringSlice(mkinfo.HeaderStrings),
FooterStrings: deepCopyStringSlice(mkinfo.FooterStrings),
EntryOrder: deepCopyStringSlice(mkinfo.EntryOrder),
}
info.EntryMap = make(map[string][]string)
for k, v := range mkinfo.EntryMap {
info.EntryMap[k] = deepCopyStringSlice(v)
}
return info
}
func deepCopyStringSlice(original []string) []string {
result := make([]string, len(original))
copy(result, original)
return result
}

View file

@ -36,10 +36,6 @@ type customModule struct {
data AndroidMkData
distFiles TaggedDistFiles
outputFile OptionalPath
// The paths that will be used as the default dist paths if no tag is
// specified.
defaultDistPaths Paths
}
const (
@ -50,7 +46,7 @@ const (
func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
m.base().licenseMetadataFile = PathForOutput(ctx, "meta_lic")
var defaultDistPaths Paths
// If the dist_output_file: true then create an output file that is stored in
// the OutputFile property of the AndroidMkEntry.
@ -62,7 +58,7 @@ func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
// property in AndroidMkEntry when determining the default dist paths.
// Setting this first allows it to be overridden based on the
// default_dist_files setting replicating that previous behavior.
m.defaultDistPaths = Paths{path}
defaultDistPaths = Paths{path}
}
// Based on the setting of the default_dist_files property possibly create a
@ -71,29 +67,40 @@ func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
defaultDistFiles := proptools.StringDefault(m.properties.Default_dist_files, defaultDistFiles_Tagged)
switch defaultDistFiles {
case defaultDistFiles_None:
// Do nothing
m.setOutputFiles(ctx, defaultDistPaths)
case defaultDistFiles_Default:
path := PathForTesting("default-dist.out")
m.defaultDistPaths = Paths{path}
defaultDistPaths = Paths{path}
m.setOutputFiles(ctx, defaultDistPaths)
m.distFiles = MakeDefaultDistFiles(path)
case defaultDistFiles_Tagged:
// Module types that set AndroidMkEntry.DistFiles to the result of calling
// GenerateTaggedDistFiles(ctx) relied on no tag being treated as "" which
// meant that the default dist paths would be whatever was returned by
// OutputFiles(""). In order to preserve that behavior when treating no tag
// as being equal to DefaultDistTag this ensures that
// OutputFiles(DefaultDistTag) will return the same as OutputFiles("").
m.defaultDistPaths = PathsForTesting("one.out")
// meant that the default dist paths would be the same as empty-string-tag
// output files. In order to preserve that behavior when treating no tag
// as being equal to DefaultDistTag this ensures that DefaultDistTag output
// will be the same as empty-string-tag output.
defaultDistPaths = PathsForTesting("one.out")
m.setOutputFiles(ctx, defaultDistPaths)
// This must be called after setting defaultDistPaths/outputFile as
// GenerateTaggedDistFiles calls into OutputFiles(tag) which may use those
// fields.
// GenerateTaggedDistFiles calls into outputFiles property which may use
// those fields.
m.distFiles = m.GenerateTaggedDistFiles(ctx)
}
}
func (m *customModule) setOutputFiles(ctx ModuleContext, defaultDistPaths Paths) {
ctx.SetOutputFiles(PathsForTesting("one.out"), "")
ctx.SetOutputFiles(PathsForTesting("two.out", "three/four.out"), ".multiple")
ctx.SetOutputFiles(PathsForTesting("another.out"), ".another-tag")
if defaultDistPaths != nil {
ctx.SetOutputFiles(defaultDistPaths, DefaultDistTag)
}
}
func (m *customModule) AndroidMk() AndroidMkData {
return AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
@ -102,25 +109,6 @@ func (m *customModule) AndroidMk() AndroidMkData {
}
}
func (m *customModule) OutputFiles(tag string) (Paths, error) {
switch tag {
case DefaultDistTag:
if m.defaultDistPaths != nil {
return m.defaultDistPaths, nil
} else {
return nil, fmt.Errorf("default dist tag is not available")
}
case "":
return PathsForTesting("one.out"), nil
case ".multiple":
return PathsForTesting("two.out", "three/four.out"), nil
case ".another-tag":
return PathsForTesting("another.out"), nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
return []AndroidMkEntries{
{
@ -287,7 +275,8 @@ func TestGetDistForGoals(t *testing.T) {
)
}
for idx, line := range androidMkLines {
expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic", module.base().licenseMetadataFile.String())
expectedLine := strings.ReplaceAll(expectedAndroidMkLines[idx], "meta_lic",
OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile.String())
if line != expectedLine {
t.Errorf(
"Expected AndroidMk line to be '%s', got '%s'",

View file

@ -16,6 +16,7 @@ package android
import (
"fmt"
"reflect"
"slices"
"sort"
"strconv"
@ -84,6 +85,12 @@ type ApexInfo struct {
// Returns the name of the test apexes that this module is included in.
TestApexes []string
// Returns the name of the overridden apex (com.android.foo)
BaseApexName string
// Returns the value of `apex_available_name`
ApexAvailableName string
}
// AllApexInfo holds the ApexInfo of all apexes that include this module.
@ -142,6 +149,17 @@ func (i ApexInfo) InApexModule(apexModuleName string) bool {
return false
}
// To satisfy the comparable interface
func (i ApexInfo) Equal(other any) bool {
otherApexInfo, ok := other.(ApexInfo)
return ok && i.ApexVariationName == otherApexInfo.ApexVariationName &&
i.MinSdkVersion == otherApexInfo.MinSdkVersion &&
i.Updatable == otherApexInfo.Updatable &&
i.UsePlatformApis == otherApexInfo.UsePlatformApis &&
reflect.DeepEqual(i.InApexVariants, otherApexInfo.InApexVariants) &&
reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules)
}
// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
// module is not part of the APEX - and thus has access to APEX internals.
type ApexTestForInfo struct {
@ -277,7 +295,7 @@ type ApexProperties struct {
//
// "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX.
// "//apex_available:platform" refers to non-APEX partitions like "system.img".
// "com.android.gki.*" matches any APEX module name with the prefix "com.android.gki.".
// Prefix pattern (com.foo.*) can be used to match with any APEX name with the prefix(com.foo.).
// Default is ["//apex_available:platform"].
Apex_available []string
@ -470,15 +488,6 @@ func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
const (
AvailableToPlatform = "//apex_available:platform"
AvailableToAnyApex = "//apex_available:anyapex"
AvailableToGkiApex = "com.android.gki.*"
)
var (
AvailableToRecognziedWildcards = []string{
AvailableToPlatform,
AvailableToAnyApex,
AvailableToGkiApex,
}
)
// CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
@ -491,11 +500,27 @@ func CheckAvailableForApex(what string, apex_available []string) bool {
if len(apex_available) == 0 {
return what == AvailableToPlatform
}
return InList(what, apex_available) ||
(what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) ||
(strings.HasPrefix(what, "com.android.gki.") && InList(AvailableToGkiApex, apex_available)) ||
(what == "com.google.mainline.primary.libs") || // TODO b/248601389
(what == "com.google.mainline.go.primary.libs") // TODO b/248601389
// TODO b/248601389
if what == "com.google.mainline.primary.libs" || what == "com.google.mainline.go.primary.libs" {
return true
}
for _, apex_name := range apex_available {
// exact match.
if apex_name == what {
return true
}
// //apex_available:anyapex matches with any apex name, but not //apex_available:platform
if apex_name == AvailableToAnyApex && what != AvailableToPlatform {
return true
}
// prefix match.
if strings.HasSuffix(apex_name, ".*") && strings.HasPrefix(what, strings.TrimSuffix(apex_name, "*")) {
return true
}
}
return false
}
// Implements ApexModule
@ -521,7 +546,20 @@ func (m *ApexModuleBase) SetNotAvailableForPlatform() {
// This function makes sure that the apex_available property is valid
func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) {
for _, n := range m.ApexProperties.Apex_available {
if n == AvailableToPlatform || n == AvailableToAnyApex || n == AvailableToGkiApex {
if n == AvailableToPlatform || n == AvailableToAnyApex {
continue
}
// Prefix pattern should end with .* and has at least two components.
if strings.Contains(n, "*") {
if !strings.HasSuffix(n, ".*") {
mctx.PropertyErrorf("apex_available", "Wildcard should end with .* like com.foo.*")
}
if strings.Count(n, ".") < 2 {
mctx.PropertyErrorf("apex_available", "Wildcard requires two or more components like com.foo.*")
}
if strings.Count(n, "*") != 1 {
mctx.PropertyErrorf("apex_available", "Wildcard is not allowed in the middle.")
}
continue
}
if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() {
@ -607,9 +645,15 @@ func IncomingApexTransition(ctx IncomingTransitionContext, incomingVariation str
return ""
}
// If this module has no apex variations the use the platform variation.
if len(apexInfos) == 0 {
return ""
if ctx.IsAddingDependency() {
// If this module has no apex variations we can't do any mapping on the incoming variation, just return it
// and let the caller get a "missing variant" error.
return incomingVariation
} else {
// If this module has no apex variations the use the platform variation.
return ""
}
}
// Convert the list of apex infos into from the AllApexInfoProvider into the merged list
@ -671,7 +715,7 @@ func MutateApexTransition(ctx BaseModuleContext, variation string) {
base.ApexProperties.InAnyApex = true
base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) {
if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
// Do not install the module for platform, but still allow it to output
// uninstallable AndroidMk entries in certain cases when they have side
// effects. TODO(jiyong): move this routine to somewhere else

View file

@ -19,10 +19,10 @@ import (
"fmt"
"reflect"
"runtime"
"slices"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
)
@ -396,45 +396,21 @@ func (target Target) Variations() []blueprint.Variation {
// device_supported and host_supported properties to determine which OsTypes are enabled for this
// module, then searches through the Targets to determine which have enabled Targets for this
// module.
func osMutator(bpctx blueprint.BottomUpMutatorContext) {
var module Module
var ok bool
if module, ok = bpctx.Module().(Module); !ok {
// The module is not a Soong module, it is a Blueprint module.
if bootstrap.IsBootstrapModule(bpctx.Module()) {
// Bootstrap Go modules are always the build OS or linux bionic.
config := bpctx.Config().(Config)
osNames := []string{config.BuildOSTarget.OsVariation()}
for _, hostCrossTarget := range config.Targets[LinuxBionic] {
if hostCrossTarget.Arch.ArchType == config.BuildOSTarget.Arch.ArchType {
osNames = append(osNames, hostCrossTarget.OsVariation())
}
}
osNames = FirstUniqueStrings(osNames)
bpctx.CreateVariations(osNames...)
}
return
}
type osTransitionMutator struct{}
// Bootstrap Go module support above requires this mutator to be a
// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
// 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)
type allOsInfo struct {
Os map[string]OsType
Variations []string
}
base := module.base()
var allOsProvider = blueprint.NewMutatorProvider[*allOsInfo]("os_propagate")
// Nothing to do for modules that are not architecture specific (e.g. a genrule).
if !base.ArchSpecific() {
return
}
// Collect a list of OSTypes supported by this module based on the HostOrDevice value
// passed to InitAndroidArchModule and the device_supported and host_supported properties.
// moduleOSList collects a list of OSTypes supported by this module based on the HostOrDevice
// value passed to InitAndroidArchModule and the device_supported and host_supported properties.
func moduleOSList(ctx ConfigContext, base *ModuleBase) []OsType {
var moduleOSList []OsType
for _, os := range osTypeList {
for _, t := range mctx.Config().Targets[os] {
for _, t := range ctx.Config().Targets[os] {
if base.supportsTarget(t) {
moduleOSList = append(moduleOSList, os)
break
@ -442,53 +418,91 @@ func osMutator(bpctx blueprint.BottomUpMutatorContext) {
}
}
createCommonOSVariant := base.commonProperties.CreateCommonOSVariant
if base.commonProperties.CreateCommonOSVariant {
// A CommonOS variant was requested so add it to the list of OS variants to
// create. It needs to be added to the end because it needs to depend on the
// the other variants and inter variant dependencies can only be created from a
// later variant in that list to an earlier one. That is because variants are
// always processed in the order in which they are created.
moduleOSList = append(moduleOSList, CommonOS)
}
return moduleOSList
}
func (o *osTransitionMutator) Split(ctx BaseModuleContext) []string {
module := ctx.Module()
base := module.base()
// Nothing to do for modules that are not architecture specific (e.g. a genrule).
if !base.ArchSpecific() {
return []string{""}
}
moduleOSList := moduleOSList(ctx, base)
// If there are no supported OSes then disable the module.
if len(moduleOSList) == 0 && !createCommonOSVariant {
if len(moduleOSList) == 0 {
base.Disable()
return
return []string{""}
}
// Convert the list of supported OsTypes to the variation names.
osNames := make([]string, len(moduleOSList))
osMapping := make(map[string]OsType, len(moduleOSList))
for i, os := range moduleOSList {
osNames[i] = os.String()
osMapping[osNames[i]] = os
}
if createCommonOSVariant {
// A CommonOS variant was requested so add it to the list of OS variants to
// create. It needs to be added to the end because it needs to depend on the
// the other variants in the list returned by CreateVariations(...) and inter
// variant dependencies can only be created from a later variant in that list to
// an earlier one. That is because variants are always processed in the order in
// which they are returned from CreateVariations(...).
osNames = append(osNames, CommonOS.Name)
moduleOSList = append(moduleOSList, CommonOS)
SetProvider(ctx, allOsProvider, &allOsInfo{
Os: osMapping,
Variations: osNames,
})
return osNames
}
func (o *osTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
return sourceVariation
}
func (o *osTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
module := ctx.Module()
base := module.base()
if !base.ArchSpecific() {
return ""
}
// Create the variations, annotate each one with which OS it was created for, and
return incomingVariation
}
func (o *osTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
module := ctx.Module()
base := module.base()
if variation == "" {
return
}
allOsInfo, ok := ModuleProvider(ctx, allOsProvider)
if !ok {
panic(fmt.Errorf("missing allOsProvider"))
}
// Annotate this variant with which OS it was created for, and
// squash the appropriate OS-specific properties into the top level properties.
modules := mctx.CreateVariations(osNames...)
for i, m := range modules {
m.base().commonProperties.CompileOS = moduleOSList[i]
m.base().setOSProperties(mctx)
}
base.commonProperties.CompileOS = allOsInfo.Os[variation]
base.setOSProperties(ctx)
if createCommonOSVariant {
if variation == CommonOS.String() {
// A CommonOS variant was requested so add dependencies from it (the last one in
// the list) to the OS type specific variants.
last := len(modules) - 1
commonOSVariant := modules[last]
commonOSVariant.base().commonProperties.CommonOSVariant = true
for _, module := range modules[0:last] {
// Ignore modules that are enabled. Note, this will only avoid adding
// dependencies on OsType variants that are explicitly disabled in their
// properties. The CommonOS variant will still depend on disabled variants
// if they are disabled afterwards, e.g. in archMutator if
if module.Enabled(mctx) {
mctx.AddInterVariantDependency(commonOsToOsSpecificVariantTag, commonOSVariant, module)
}
osList := allOsInfo.Variations[:len(allOsInfo.Variations)-1]
for _, os := range osList {
variation := []blueprint.Variation{{"os", os}}
ctx.AddVariationDependencies(variation, commonOsToOsSpecificVariantTag, ctx.ModuleName())
}
}
}
@ -521,7 +535,7 @@ func GetOsSpecificVariantsOfCommonOSVariant(mctx BaseModuleContext) []Module {
var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
// archMutator splits a module into a variant for each Target requested by the module. Target selection
// archTransitionMutator splits a module into a variant for each Target requested by the module. Target selection
// for a module is in three levels, OsClass, multilib, and then Target.
// OsClass selection is determined by:
// - The HostOrDeviceSupported value passed in to InitAndroidArchModule by the module type factory, which selects
@ -552,54 +566,47 @@ var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
//
// Modules can be initialized with InitAndroidMultiTargetsArchModule, in which case they will be split by OsClass,
// but will have a common Target that is expected to handle all other selected Targets via ctx.MultiTargets().
func archMutator(bpctx blueprint.BottomUpMutatorContext) {
var module Module
var ok bool
if module, ok = bpctx.Module().(Module); !ok {
if bootstrap.IsBootstrapModule(bpctx.Module()) {
// Bootstrap Go modules are always the build architecture.
bpctx.CreateVariations(bpctx.Config().(Config).BuildOSTarget.ArchVariation())
}
return
}
type archTransitionMutator struct{}
// Bootstrap Go module support above requires this mutator to be a
// blueprint.BottomUpMutatorContext because android.BottomUpMutatorContext
// 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)
type allArchInfo struct {
Targets map[string]Target
MultiTargets []Target
Primary string
Multilib string
}
var allArchProvider = blueprint.NewMutatorProvider[*allArchInfo]("arch_propagate")
func (a *archTransitionMutator) Split(ctx BaseModuleContext) []string {
module := ctx.Module()
base := module.base()
if !base.ArchSpecific() {
return
return []string{""}
}
os := base.commonProperties.CompileOS
if os == CommonOS {
// Make sure that the target related properties are initialized for the
// CommonOS variant.
addTargetProperties(module, commonTargetMap[os.Name], nil, true)
// Do not create arch specific variants for the CommonOS variant.
return
return []string{""}
}
osTargets := mctx.Config().Targets[os]
osTargets := ctx.Config().Targets[os]
image := base.commonProperties.ImageVariation
// Filter NativeBridge targets unless they are explicitly supported.
// Skip creating native bridge variants for non-core modules.
if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) {
osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool {
return bool(t.NativeBridge)
})
}
var targets []Target
for _, t := range osTargets {
if !t.NativeBridge {
targets = append(targets, t)
}
}
osTargets = targets
// Filter HostCross targets if disabled.
if base.HostSupported() && !base.HostCrossSupported() {
osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool {
return t.HostCross
})
}
// only the primary arch in the ramdisk / vendor_ramdisk / recovery partition
@ -611,19 +618,18 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
prefer32 := os == Windows
// Determine the multilib selection for this module.
ignorePrefer32OnDevice := mctx.Config().IgnorePrefer32OnDevice()
multilib, extraMultilib := decodeMultilib(base, os, ignorePrefer32OnDevice)
multilib, extraMultilib := decodeMultilib(ctx, base)
// Convert the multilib selection into a list of Targets.
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
ctx.ModuleErrorf("%s", err.Error())
}
// If there are no supported targets disable the module.
if len(targets) == 0 {
base.Disable()
return
return []string{""}
}
// If the module is using extraMultilib, decode the extraMultilib selection into
@ -632,7 +638,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
if extraMultilib != "" {
multiTargets, err = decodeMultilibTargets(extraMultilib, osTargets, prefer32)
if err != nil {
mctx.ModuleErrorf("%s", err.Error())
ctx.ModuleErrorf("%s", err.Error())
}
multiTargets = filterHostCross(multiTargets, targets[0].HostCross)
}
@ -640,7 +646,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
// Recovery is always the primary architecture, filter out any other architectures.
// Common arch is also allowed
if image == RecoveryVariation {
primaryArch := mctx.Config().DevicePrimaryArchType()
primaryArch := ctx.Config().DevicePrimaryArchType()
targets = filterToArch(targets, primaryArch, Common)
multiTargets = filterToArch(multiTargets, primaryArch, Common)
}
@ -648,37 +654,109 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
// If there are no supported targets disable the module.
if len(targets) == 0 {
base.Disable()
return
return []string{""}
}
// Convert the targets into a list of arch variation names.
targetNames := make([]string, len(targets))
targetMapping := make(map[string]Target, len(targets))
for i, target := range targets {
targetNames[i] = target.ArchVariation()
targetMapping[targetNames[i]] = targets[i]
}
// Create the variations, annotate each one with which Target it was created for, and
// squash the appropriate arch-specific properties into the top level properties.
modules := mctx.CreateVariations(targetNames...)
for i, m := range modules {
addTargetProperties(m, targets[i], multiTargets, i == 0)
m.base().setArchProperties(mctx)
SetProvider(ctx, allArchProvider, &allArchInfo{
Targets: targetMapping,
MultiTargets: multiTargets,
Primary: targetNames[0],
Multilib: multilib,
})
return targetNames
}
// Install support doesn't understand Darwin+Arm64
if os == Darwin && targets[i].HostCross {
m.base().commonProperties.SkipInstall = true
func (a *archTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
return sourceVariation
}
func (a *archTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
module := ctx.Module()
base := module.base()
if !base.ArchSpecific() {
return ""
}
os := base.commonProperties.CompileOS
if os == CommonOS {
// Do not create arch specific variants for the CommonOS variant.
return ""
}
if incomingVariation == "" {
multilib, _ := decodeMultilib(ctx, base)
if multilib == "common" {
return "common"
}
}
return incomingVariation
}
func (a *archTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
module := ctx.Module()
base := module.base()
os := base.commonProperties.CompileOS
if os == CommonOS {
// Make sure that the target related properties are initialized for the
// CommonOS variant.
addTargetProperties(module, commonTargetMap[os.Name], nil, true)
return
}
if variation == "" {
return
}
if !base.ArchSpecific() {
panic(fmt.Errorf("found variation %q for non arch specifc module", variation))
}
allArchInfo, ok := ModuleProvider(ctx, allArchProvider)
if !ok {
return
}
target, ok := allArchInfo.Targets[variation]
if !ok {
panic(fmt.Errorf("missing Target for %q", variation))
}
primary := variation == allArchInfo.Primary
multiTargets := allArchInfo.MultiTargets
// Annotate the new variant with which Target it was created for, and
// squash the appropriate arch-specific properties into the top level properties.
addTargetProperties(ctx.Module(), target, multiTargets, primary)
base.setArchProperties(ctx)
// Install support doesn't understand Darwin+Arm64
if os == Darwin && target.HostCross {
base.commonProperties.SkipInstall = true
}
// Create a dependency for Darwin Universal binaries from the primary to secondary
// architecture. The module itself will be responsible for calling lipo to merge the outputs.
if os == Darwin {
if multilib == "darwin_universal" && len(modules) == 2 {
mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
} else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
isUniversalBinary := (allArchInfo.Multilib == "darwin_universal" && len(allArchInfo.Targets) == 2) ||
allArchInfo.Multilib == "darwin_universal_common_first" && len(allArchInfo.Targets) == 3
isPrimary := variation == ctx.Config().BuildArch.String()
hasSecondaryConfigured := len(ctx.Config().Targets[Darwin]) > 1
if isUniversalBinary && isPrimary && hasSecondaryConfigured {
secondaryArch := ctx.Config().Targets[Darwin][1].Arch.String()
variation := []blueprint.Variation{{"arch", secondaryArch}}
ctx.AddVariationDependencies(variation, DarwinUniversalVariantTag, ctx.ModuleName())
}
}
}
// addTargetProperties annotates a variant with the Target is is being compiled for, the list
@ -695,7 +773,9 @@ func addTargetProperties(m Module, target Target, multiTargets []Target, primary
// multilib from the factory's call to InitAndroidArchModule if none was set. For modules that
// called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
// the actual multilib in extraMultilib.
func decodeMultilib(base *ModuleBase, os OsType, ignorePrefer32OnDevice bool) (multilib, extraMultilib string) {
func decodeMultilib(ctx ConfigContext, base *ModuleBase) (multilib, extraMultilib string) {
os := base.commonProperties.CompileOS
ignorePrefer32OnDevice := ctx.Config().IgnorePrefer32OnDevice()
// First check the "android.compile_multilib" or "host.compile_multilib" properties.
switch os.Class {
case Device:

View file

@ -16,7 +16,6 @@ package android
var archVariants = map[ArchType][]string{
Arm: {
"armv7-a",
"armv7-a-neon",
"armv8-a",
"armv8-2a",
@ -27,6 +26,7 @@ var archVariants = map[ArchType][]string{
"armv8-2a",
"armv8-2a-dotprod",
"armv9-a",
"armv9-2a",
},
X86: {
"amberlake",
@ -160,6 +160,9 @@ var androidArchFeatureMap = map[ArchType]map[string][]string{
"armv9-a": {
"dotprod",
},
"armv9-2a": {
"dotprod",
},
},
X86: {
"amberlake": {

View file

@ -331,6 +331,12 @@ func TestArchMutator(t *testing.T) {
host_supported: true,
}
module {
name: "nohostcross",
host_supported: true,
host_cross_supported: false,
}
module {
name: "baz",
device_supported: false,
@ -355,13 +361,14 @@ func TestArchMutator(t *testing.T) {
`
testCases := []struct {
name string
preparer FixturePreparer
fooVariants []string
barVariants []string
bazVariants []string
quxVariants []string
firstVariants []string
name string
preparer FixturePreparer
fooVariants []string
barVariants []string
noHostCrossVariants []string
bazVariants []string
quxVariants []string
firstVariants []string
multiTargetVariants []string
multiTargetVariantsMap map[string][]string
@ -373,6 +380,7 @@ func TestArchMutator(t *testing.T) {
preparer: nil,
fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"),
bazVariants: nil,
quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"),
firstVariants: append(buildOS64Variants, "android_arm64_armv8-a"),
@ -390,6 +398,7 @@ func TestArchMutator(t *testing.T) {
}),
fooVariants: nil,
barVariants: buildOSVariants,
noHostCrossVariants: buildOSVariants,
bazVariants: nil,
quxVariants: buildOS32Variants,
firstVariants: buildOS64Variants,
@ -406,6 +415,7 @@ func TestArchMutator(t *testing.T) {
}),
fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"},
barVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"},
bazVariants: nil,
quxVariants: []string{"linux_musl_x86", "android_arm_armv7-a-neon"},
firstVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"},
@ -441,7 +451,7 @@ func TestArchMutator(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
if tt.goOS != runtime.GOOS {
if tt.goOS != "" && tt.goOS != runtime.GOOS {
t.Skipf("requries runtime.GOOS %s", tt.goOS)
}
@ -461,6 +471,10 @@ func TestArchMutator(t *testing.T) {
t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g)
}
if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) {
t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g)
}
if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) {
t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g)
}

View file

@ -220,6 +220,10 @@ type BaseModuleContext interface {
// EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
// can be used to evaluate the final value of Configurable properties.
EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue
// HasMutatorFinished returns true if the given mutator has finished running.
// It will panic if given an invalid mutator name.
HasMutatorFinished(mutatorName string) bool
}
type baseModuleContext struct {
@ -270,6 +274,10 @@ func (b *baseModuleContext) setProvider(provider blueprint.AnyProviderKey, value
b.bp.SetProvider(provider, value)
}
func (b *baseModuleContext) HasMutatorFinished(mutatorName string) bool {
return b.bp.HasMutatorFinished(mutatorName)
}
func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module {
return b.bp.GetDirectDepWithTag(name, tag)
}

View file

@ -0,0 +1,337 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"bytes"
"encoding/csv"
"encoding/gob"
"fmt"
"slices"
"strconv"
"strings"
"github.com/google/blueprint"
)
var (
// Constants of property names used in compliance metadata of modules
ComplianceMetadataProp = struct {
NAME string
PACKAGE string
MODULE_TYPE string
OS string
ARCH string
IS_PRIMARY_ARCH string
VARIANT string
IS_STATIC_LIB string
INSTALLED_FILES string
BUILT_FILES string
STATIC_DEPS string
STATIC_DEP_FILES string
WHOLE_STATIC_DEPS string
WHOLE_STATIC_DEP_FILES string
LICENSES string
// module_type=package
PKG_DEFAULT_APPLICABLE_LICENSES string
// module_type=license
LIC_LICENSE_KINDS string
LIC_LICENSE_TEXT string
LIC_PACKAGE_NAME string
// module_type=license_kind
LK_CONDITIONS string
LK_URL string
}{
"name",
"package",
"module_type",
"os",
"arch",
"is_primary_arch",
"variant",
"is_static_lib",
"installed_files",
"built_files",
"static_deps",
"static_dep_files",
"whole_static_deps",
"whole_static_dep_files",
"licenses",
"pkg_default_applicable_licenses",
"lic_license_kinds",
"lic_license_text",
"lic_package_name",
"lk_conditions",
"lk_url",
}
// A constant list of all property names in compliance metadata
// Order of properties here is the order of columns in the exported CSV file.
COMPLIANCE_METADATA_PROPS = []string{
ComplianceMetadataProp.NAME,
ComplianceMetadataProp.PACKAGE,
ComplianceMetadataProp.MODULE_TYPE,
ComplianceMetadataProp.OS,
ComplianceMetadataProp.ARCH,
ComplianceMetadataProp.VARIANT,
ComplianceMetadataProp.IS_STATIC_LIB,
ComplianceMetadataProp.IS_PRIMARY_ARCH,
// Space separated installed files
ComplianceMetadataProp.INSTALLED_FILES,
// Space separated built files
ComplianceMetadataProp.BUILT_FILES,
// Space separated module names of static dependencies
ComplianceMetadataProp.STATIC_DEPS,
// Space separated file paths of static dependencies
ComplianceMetadataProp.STATIC_DEP_FILES,
// Space separated module names of whole static dependencies
ComplianceMetadataProp.WHOLE_STATIC_DEPS,
// Space separated file paths of whole static dependencies
ComplianceMetadataProp.WHOLE_STATIC_DEP_FILES,
ComplianceMetadataProp.LICENSES,
// module_type=package
ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES,
// module_type=license
ComplianceMetadataProp.LIC_LICENSE_KINDS,
ComplianceMetadataProp.LIC_LICENSE_TEXT, // resolve to file paths
ComplianceMetadataProp.LIC_PACKAGE_NAME,
// module_type=license_kind
ComplianceMetadataProp.LK_CONDITIONS,
ComplianceMetadataProp.LK_URL,
}
)
// ComplianceMetadataInfo provides all metadata of a module, e.g. name, module type, package, license,
// dependencies, built/installed files, etc. It is a wrapper on a map[string]string with some utility
// methods to get/set properties' values.
type ComplianceMetadataInfo struct {
properties map[string]string
}
func NewComplianceMetadataInfo() *ComplianceMetadataInfo {
return &ComplianceMetadataInfo{
properties: map[string]string{},
}
}
func (c *ComplianceMetadataInfo) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := encoder.Encode(c.properties)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (c *ComplianceMetadataInfo) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := decoder.Decode(&c.properties)
if err != nil {
return err
}
return nil
}
func (c *ComplianceMetadataInfo) SetStringValue(propertyName string, value string) {
if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
}
c.properties[propertyName] = value
}
func (c *ComplianceMetadataInfo) SetListValue(propertyName string, value []string) {
c.SetStringValue(propertyName, strings.TrimSpace(strings.Join(value, " ")))
}
func (c *ComplianceMetadataInfo) getStringValue(propertyName string) string {
if !slices.Contains(COMPLIANCE_METADATA_PROPS, propertyName) {
panic(fmt.Errorf("Unknown metadata property: %s.", propertyName))
}
return c.properties[propertyName]
}
func (c *ComplianceMetadataInfo) getAllValues() map[string]string {
return c.properties
}
var (
ComplianceMetadataProvider = blueprint.NewProvider[*ComplianceMetadataInfo]()
)
// buildComplianceMetadataProvider starts with the ModuleContext.ComplianceMetadataInfo() and fills in more common metadata
// for different module types without accessing their private fields but through android.Module interface
// and public/private fields of package android. The final metadata is stored to a module's ComplianceMetadataProvider.
func buildComplianceMetadataProvider(ctx *moduleContext, m *ModuleBase) {
complianceMetadataInfo := ctx.ComplianceMetadataInfo()
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.NAME, m.Name())
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.PACKAGE, ctx.ModuleDir())
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.MODULE_TYPE, ctx.ModuleType())
switch ctx.ModuleType() {
case "license":
licenseModule := m.module.(*licenseModule)
complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_KINDS, licenseModule.properties.License_kinds)
complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LIC_LICENSE_TEXT, PathsForModuleSrc(ctx, licenseModule.properties.License_text).Strings())
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LIC_PACKAGE_NAME, String(licenseModule.properties.Package_name))
case "license_kind":
licenseKindModule := m.module.(*licenseKindModule)
complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LK_CONDITIONS, licenseKindModule.properties.Conditions)
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.LK_URL, licenseKindModule.properties.Url)
default:
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.OS, ctx.Os().String())
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.ARCH, ctx.Arch().String())
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.IS_PRIMARY_ARCH, strconv.FormatBool(ctx.PrimaryArch()))
complianceMetadataInfo.SetStringValue(ComplianceMetadataProp.VARIANT, ctx.ModuleSubDir())
if m.primaryLicensesProperty != nil && m.primaryLicensesProperty.getName() == "licenses" {
complianceMetadataInfo.SetListValue(ComplianceMetadataProp.LICENSES, m.primaryLicensesProperty.getStrings())
}
var installed InstallPaths
installed = append(installed, ctx.installFiles...)
installed = append(installed, ctx.katiInstalls.InstallPaths()...)
installed = append(installed, ctx.katiSymlinks.InstallPaths()...)
installed = append(installed, ctx.katiInitRcInstalls.InstallPaths()...)
installed = append(installed, ctx.katiVintfInstalls.InstallPaths()...)
complianceMetadataInfo.SetListValue(ComplianceMetadataProp.INSTALLED_FILES, FirstUniqueStrings(installed.Strings()))
}
ctx.setProvider(ComplianceMetadataProvider, complianceMetadataInfo)
}
func init() {
RegisterComplianceMetadataSingleton(InitRegistrationContext)
}
func RegisterComplianceMetadataSingleton(ctx RegistrationContext) {
ctx.RegisterParallelSingletonType("compliance_metadata_singleton", complianceMetadataSingletonFactory)
}
var (
// sqlite3 command line tool
sqlite3 = pctx.HostBinToolVariable("sqlite3", "sqlite3")
// Command to import .csv files to sqlite3 database
importCsv = pctx.AndroidStaticRule("importCsv",
blueprint.RuleParams{
Command: `rm -rf $out && ` +
`${sqlite3} $out ".import --csv $in modules" && ` +
`${sqlite3} $out ".import --csv ${make_metadata} make_metadata" && ` +
`${sqlite3} $out ".import --csv ${make_modules} make_modules"`,
CommandDeps: []string{"${sqlite3}"},
}, "make_metadata", "make_modules")
)
func complianceMetadataSingletonFactory() Singleton {
return &complianceMetadataSingleton{}
}
type complianceMetadataSingleton struct {
}
func writerToCsv(csvWriter *csv.Writer, row []string) {
err := csvWriter.Write(row)
if err != nil {
panic(err)
}
}
// Collect compliance metadata from all Soong modules, write to a CSV file and
// import compliance metadata from Make and Soong to a sqlite3 database.
func (c *complianceMetadataSingleton) GenerateBuildActions(ctx SingletonContext) {
if !ctx.Config().HasDeviceProduct() {
return
}
var buffer bytes.Buffer
csvWriter := csv.NewWriter(&buffer)
// Collect compliance metadata of modules in Soong and write to out/soong/compliance-metadata/<product>/soong-modules.csv file.
columnNames := []string{"id"}
columnNames = append(columnNames, COMPLIANCE_METADATA_PROPS...)
writerToCsv(csvWriter, columnNames)
rowId := -1
ctx.VisitAllModules(func(module Module) {
if !module.Enabled(ctx) {
return
}
moduleType := ctx.ModuleType(module)
if moduleType == "package" {
metadataMap := map[string]string{
ComplianceMetadataProp.NAME: ctx.ModuleName(module),
ComplianceMetadataProp.MODULE_TYPE: ctx.ModuleType(module),
ComplianceMetadataProp.PKG_DEFAULT_APPLICABLE_LICENSES: strings.Join(module.base().primaryLicensesProperty.getStrings(), " "),
}
rowId = rowId + 1
metadata := []string{strconv.Itoa(rowId)}
for _, propertyName := range COMPLIANCE_METADATA_PROPS {
metadata = append(metadata, metadataMap[propertyName])
}
writerToCsv(csvWriter, metadata)
return
}
if provider, ok := ctx.otherModuleProvider(module, ComplianceMetadataProvider); ok {
metadataInfo := provider.(*ComplianceMetadataInfo)
rowId = rowId + 1
metadata := []string{strconv.Itoa(rowId)}
for _, propertyName := range COMPLIANCE_METADATA_PROPS {
metadata = append(metadata, metadataInfo.getStringValue(propertyName))
}
writerToCsv(csvWriter, metadata)
return
}
})
csvWriter.Flush()
deviceProduct := ctx.Config().DeviceProduct()
modulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "soong-modules.csv")
WriteFileRuleVerbatim(ctx, modulesCsv, buffer.String())
// Metadata generated in Make
makeMetadataCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-metadata.csv")
makeModulesCsv := PathForOutput(ctx, "compliance-metadata", deviceProduct, "make-modules.csv")
// Import metadata from Make and Soong to sqlite3 database
complianceMetadataDb := PathForOutput(ctx, "compliance-metadata", deviceProduct, "compliance-metadata.db")
ctx.Build(pctx, BuildParams{
Rule: importCsv,
Input: modulesCsv,
Implicits: []Path{
makeMetadataCsv,
makeModulesCsv,
},
Output: complianceMetadataDb,
Args: map[string]string{
"make_metadata": makeMetadataCsv.String(),
"make_modules": makeModulesCsv.String(),
},
})
// Phony rule "compliance-metadata.db". "m compliance-metadata.db" to create the compliance metadata database.
ctx.Build(pctx, BuildParams{
Rule: blueprint.Phony,
Inputs: []Path{complianceMetadataDb},
Output: PathForPhony(ctx, "compliance-metadata.db"),
})
}

View file

@ -173,6 +173,13 @@ func (c Config) DisableVerifyOverlaps() bool {
return c.IsEnvTrue("DISABLE_VERIFY_OVERLAPS") || c.ReleaseDisableVerifyOverlaps() || !c.ReleaseDefaultModuleBuildFromSource()
}
func (c Config) CoverageSuffix() string {
if v := c.IsEnvTrue("EMMA_INSTRUMENT"); v {
return "coverage."
}
return ""
}
// MaxPageSizeSupported returns the max page size supported by the device. This
// value will define the ELF segment alignment for binaries (executables and
// shared libraries).
@ -198,12 +205,44 @@ func (c Config) ReleaseAconfigValueSets() []string {
return c.config.productVariables.ReleaseAconfigValueSets
}
func (c Config) ReleaseAconfigExtraReleaseConfigs() []string {
result := []string{}
if val, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"]; ok {
if len(val) > 0 {
// Remove any duplicates from the list.
found := make(map[string]bool)
for _, k := range strings.Split(val, " ") {
if !found[k] {
found[k] = true
result = append(result, k)
}
}
}
}
return result
}
func (c Config) ReleaseAconfigExtraReleaseConfigsValueSets() map[string][]string {
result := make(map[string][]string)
for _, rcName := range c.ReleaseAconfigExtraReleaseConfigs() {
if value, ok := c.config.productVariables.BuildFlags["RELEASE_ACONFIG_VALUE_SETS_"+rcName]; ok {
result[rcName] = strings.Split(value, " ")
}
}
return result
}
// The flag default permission value passed to aconfig
// derived from RELEASE_ACONFIG_FLAG_DEFAULT_PERMISSION
func (c Config) ReleaseAconfigFlagDefaultPermission() string {
return c.config.productVariables.ReleaseAconfigFlagDefaultPermission
}
// Enable object size sanitizer
func (c Config) ReleaseBuildObjectSizeSanitizer() bool {
return c.config.productVariables.GetBuildFlagBool("RELEASE_BUILD_OBJECT_SIZE_SANITIZER")
}
// The flag indicating behavior for the tree wrt building modules or using prebuilts
// derived from RELEASE_DEFAULT_MODULE_BUILD_FROM_SOURCE
func (c Config) ReleaseDefaultModuleBuildFromSource() bool {
@ -321,9 +360,6 @@ type config struct {
// modules that aren't mixed-built for at least one variant will cause a build
// failure
ensureAllowlistIntegrity bool
// List of Api libraries that contribute to Api surfaces.
apiLibraries map[string]struct{}
}
type deviceConfig struct {
@ -573,40 +609,6 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error)
setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph)
setBuildMode(cmdArgs.DocFile, GenerateDocFile)
// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
config.apiLibraries = map[string]struct{}{
"android.net.ipsec.ike": {},
"art.module.public.api": {},
"conscrypt.module.public.api": {},
"framework-adservices": {},
"framework-appsearch": {},
"framework-bluetooth": {},
"framework-configinfrastructure": {},
"framework-connectivity": {},
"framework-connectivity-t": {},
"framework-devicelock": {},
"framework-graphics": {},
"framework-healthfitness": {},
"framework-location": {},
"framework-media": {},
"framework-mediaprovider": {},
"framework-nfc": {},
"framework-ondevicepersonalization": {},
"framework-pdf": {},
"framework-pdf-v": {},
"framework-permission": {},
"framework-permission-s": {},
"framework-scheduling": {},
"framework-sdkextensions": {},
"framework-statsd": {},
"framework-sdksandbox": {},
"framework-tethering": {},
"framework-uwb": {},
"framework-virtualization": {},
"framework-wifi": {},
"i18n.module.public.api": {},
}
config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub())
return Config{config}, err
@ -1033,6 +1035,22 @@ func (c *config) DefaultAppCertificate(ctx PathContext) (pem, key SourcePath) {
return defaultDir.Join(ctx, "testkey.x509.pem"), defaultDir.Join(ctx, "testkey.pk8")
}
func (c *config) ExtraOtaKeys(ctx PathContext, recovery bool) []SourcePath {
var otaKeys []string
if recovery {
otaKeys = c.productVariables.ExtraOtaRecoveryKeys
} else {
otaKeys = c.productVariables.ExtraOtaKeys
}
otaPaths := make([]SourcePath, len(otaKeys))
for i, key := range otaKeys {
otaPaths[i] = PathForSource(ctx, key+".x509.pem")
}
return otaPaths
}
func (c *config) BuildKeys() string {
defaultCert := String(c.productVariables.DefaultAppCertificate)
if defaultCert == "" || defaultCert == filepath.Join(testKeyDir, "testkey") {
@ -1157,6 +1175,10 @@ func (c *config) UseGoma() bool {
return Bool(c.productVariables.UseGoma)
}
func (c *config) UseABFS() bool {
return Bool(c.productVariables.UseABFS)
}
func (c *config) UseRBE() bool {
return Bool(c.productVariables.UseRBE)
}
@ -1318,10 +1340,6 @@ func (c *config) FrameworksBaseDirExists(ctx PathGlobContext) bool {
return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid()
}
func (c *config) VndkSnapshotBuildArtifacts() bool {
return Bool(c.productVariables.VndkSnapshotBuildArtifacts)
}
func (c *config) HasMultilibConflict(arch ArchType) bool {
return c.multilibConflicts[arch]
}
@ -1385,10 +1403,6 @@ func (c *deviceConfig) VendorPath() string {
return "vendor"
}
func (c *deviceConfig) RecoverySnapshotVersion() string {
return String(c.config.productVariables.RecoverySnapshotVersion)
}
func (c *deviceConfig) CurrentApiLevelForVendorModules() string {
return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current")
}
@ -1490,11 +1504,6 @@ func (c *deviceConfig) NativeCoverageEnabledForPath(path string) bool {
}
}
if coverage && len(c.config.productVariables.NativeCoverageExcludePaths) > 0 {
// Workaround coverage boot failure.
// http://b/269981180
if strings.HasPrefix(path, "external/protobuf") {
coverage = false
}
if HasAnyPrefix(path, c.config.productVariables.NativeCoverageExcludePaths) {
coverage = false
}
@ -1663,6 +1672,17 @@ func (c *config) ApexTrimEnabled() bool {
return Bool(c.productVariables.TrimmedApex)
}
func (c *config) UseSoongSystemImage() bool {
return Bool(c.productVariables.UseSoongSystemImage)
}
func (c *config) SoongDefinedSystemImage() string {
if c.UseSoongSystemImage() {
return String(c.productVariables.ProductSoongDefinedSystemImage)
}
return ""
}
func (c *config) EnforceSystemCertificate() bool {
return Bool(c.productVariables.EnforceSystemCertificate)
}
@ -1770,22 +1790,6 @@ func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
}
func (c *deviceConfig) DirectedVendorSnapshot() bool {
return c.config.productVariables.DirectedVendorSnapshot
}
func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
return c.config.productVariables.VendorSnapshotModules
}
func (c *deviceConfig) DirectedRecoverySnapshot() bool {
return c.config.productVariables.DirectedRecoverySnapshot
}
func (c *deviceConfig) RecoverySnapshotModules() map[string]bool {
return c.config.productVariables.RecoverySnapshotModules
}
func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) {
var ret = make(map[string]bool)
for _, dir := range dirs {
@ -1812,40 +1816,6 @@ func (c *deviceConfig) createDirsMapOnce(onceKey OnceKey, previous map[string]bo
return dirMap.(map[string]bool)
}
var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap")
func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool {
return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil,
c.config.productVariables.VendorSnapshotDirsExcluded)
}
var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap")
func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool {
excludedMap := c.VendorSnapshotDirsExcludedMap()
return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap,
c.config.productVariables.VendorSnapshotDirsIncluded)
}
var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap")
func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool {
return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil,
c.config.productVariables.RecoverySnapshotDirsExcluded)
}
var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap")
func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool {
excludedMap := c.RecoverySnapshotDirsExcludedMap()
return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap,
c.config.productVariables.RecoverySnapshotDirsIncluded)
}
func (c *deviceConfig) HostFakeSnapshotEnabled() bool {
return c.config.productVariables.HostFakeSnapshotEnabled
}
func (c *deviceConfig) ShippingApiLevel() ApiLevel {
if c.config.productVariables.Shipping_api_level == nil {
return NoneApiLevel
@ -1878,10 +1848,6 @@ func (c *deviceConfig) BuildBrokenTrebleSyspropNeverallow() bool {
return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
}
func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool {
return c.config.productVariables.BuildBrokenUsesSoongPython2Modules
}
func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
return c.config.productVariables.BuildDebugfsRestrictionsEnabled
}
@ -1999,17 +1965,6 @@ func (c *config) SetBuildFromTextStub(b bool) {
c.productVariables.Build_from_text_stub = boolPtr(b)
}
func (c *config) SetApiLibraries(libs []string) {
c.apiLibraries = make(map[string]struct{})
for _, lib := range libs {
c.apiLibraries[lib] = struct{}{}
}
}
func (c *config) GetApiLibraries() map[string]struct{} {
return c.apiLibraries
}
func (c *deviceConfig) CheckVendorSeappViolations() bool {
return Bool(c.config.productVariables.CheckVendorSeappViolations)
}
@ -2019,10 +1974,18 @@ func (c *config) GetBuildFlag(name string) (string, bool) {
return val, ok
}
func (c *config) UseOptimizedResourceShrinkingByDefault() bool {
return c.productVariables.GetBuildFlagBool("RELEASE_USE_OPTIMIZED_RESOURCE_SHRINKING_BY_DEFAULT")
}
func (c *config) UseResourceProcessorByDefault() bool {
return c.productVariables.GetBuildFlagBool("RELEASE_USE_RESOURCE_PROCESSOR_BY_DEFAULT")
}
func (c *config) UseTransitiveJarsInClasspath() bool {
return c.productVariables.GetBuildFlagBool("RELEASE_USE_TRANSITIVE_JARS_IN_CLASSPATH")
}
var (
mainlineApexContributionBuildFlagsToApexNames = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ADBD": "com.android.adbd",
@ -2125,3 +2088,19 @@ func (c *config) OdmPropFiles(ctx PathContext) Paths {
func (c *config) EnableUffdGc() string {
return String(c.productVariables.EnableUffdGc)
}
func (c *config) DeviceFrameworkCompatibilityMatrixFile() []string {
return c.productVariables.DeviceFrameworkCompatibilityMatrixFile
}
func (c *config) DeviceProductCompatibilityMatrixFile() []string {
return c.productVariables.DeviceProductCompatibilityMatrixFile
}
func (c *config) BoardAvbEnable() bool {
return Bool(c.productVariables.BoardAvbEnable)
}
func (c *config) BoardAvbSystemAddHashtreeFooterArgs() []string {
return c.productVariables.BoardAvbSystemAddHashtreeFooterArgs
}

View file

@ -125,6 +125,38 @@ func assertStringEquals(t *testing.T, expected, actual string) {
}
}
func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) {
testCases := []struct {
name string
flag string
expected []string
}{
{
name: "empty",
flag: "",
expected: []string{},
},
{
name: "specified",
flag: "bar foo",
expected: []string{"bar", "foo"},
},
{
name: "duplicates",
flag: "foo bar foo",
expected: []string{"foo", "bar"},
},
}
for _, tc := range testCases {
fixture := GroupFixturePreparers(
PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag),
)
actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs()
AssertArrayString(t, tc.name, tc.expected, actual)
}
}
func TestConfiguredJarList(t *testing.T) {
list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"})

View file

@ -26,3 +26,9 @@ func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] {
resultCases,
)
}
func NewSimpleConfigurable[T proptools.ConfigurableElements](value T) proptools.Configurable[T] {
return proptools.NewConfigurable(nil, []proptools.ConfigurableCase[T]{
proptools.NewConfigurableCase(nil, &value),
})
}

506
android/container.go Normal file
View file

@ -0,0 +1,506 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"fmt"
"reflect"
"slices"
"strings"
"github.com/google/blueprint"
)
// ----------------------------------------------------------------------------
// Start of the definitions of exception functions and the lookup table.
//
// Functions cannot be used as a value passed in providers, because functions are not
// hashable. As a workaround, the [exceptionHandleFuncLabel] enum values are passed using providers,
// and the corresponding functions are called from [exceptionHandleFunctionsTable] map.
// ----------------------------------------------------------------------------
type exceptionHandleFunc func(ModuleContext, Module, Module) bool
type StubsAvailableModule interface {
IsStubsModule() bool
}
// Returns true if the dependency module is a stubs module
var depIsStubsModule exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
if stubsModule, ok := dep.(StubsAvailableModule); ok {
return stubsModule.IsStubsModule()
}
return false
}
// Returns true if the dependency module belongs to any of the apexes.
var depIsApexModule exceptionHandleFunc = func(mctx ModuleContext, _, dep Module) bool {
depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
return InList(ApexContainer, depContainersInfo.belongingContainers)
}
// Returns true if the module and the dependent module belongs to common apexes.
var belongsToCommonApexes exceptionHandleFunc = func(mctx ModuleContext, m, dep Module) bool {
mContainersInfo, _ := getContainerModuleInfo(mctx, m)
depContainersInfo, _ := getContainerModuleInfo(mctx, dep)
return HasIntersection(mContainersInfo.ApexNames(), depContainersInfo.ApexNames())
}
// Returns true when all apexes that the module belongs to are non updatable.
// For an apex module to be allowed to depend on a non-apex partition module,
// all apexes that the module belong to must be non updatable.
var belongsToNonUpdatableApex exceptionHandleFunc = func(mctx ModuleContext, m, _ Module) bool {
mContainersInfo, _ := getContainerModuleInfo(mctx, m)
return !mContainersInfo.UpdatableApex()
}
// Returns true if the dependency is added via dependency tags that are not used to tag dynamic
// dependency tags.
var depIsNotDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
mInstallable, _ := m.(InstallableModule)
depTag := ctx.OtherModuleDependencyTag(dep)
return !InList(depTag, mInstallable.DynamicDependencyTags())
}
// Returns true if the dependency is added via dependency tags that are not used to tag static
// or dynamic dependency tags. These dependencies do not affect the module in compile time or in
// runtime, thus are not significant enough to raise an error.
var depIsNotStaticOrDynamicDepTag exceptionHandleFunc = func(ctx ModuleContext, m, dep Module) bool {
mInstallable, _ := m.(InstallableModule)
depTag := ctx.OtherModuleDependencyTag(dep)
return !InList(depTag, append(mInstallable.StaticDependencyTags(), mInstallable.DynamicDependencyTags()...))
}
var globallyAllowlistedDependencies = []string{
// Modules that provide annotations used within the platform and apexes.
"aconfig-annotations-lib",
"framework-annotations-lib",
"unsupportedappusage",
// TODO(b/363016634): Remove from the allowlist when the module is converted
// to java_sdk_library and the java_aconfig_library modules depend on the stub.
"aconfig_storage_reader_java",
// framework-res provides core resources essential for building apps and system UI.
// This module is implicitly added as a dependency for java modules even when the
// dependency specifies sdk_version.
"framework-res",
// jacocoagent is implicitly added as a dependency in coverage builds, and is not installed
// on the device.
"jacocoagent",
}
// Returns true when the dependency is globally allowlisted for inter-container dependency
var depIsGloballyAllowlisted exceptionHandleFunc = func(_ ModuleContext, _, dep Module) bool {
return InList(dep.Name(), globallyAllowlistedDependencies)
}
// Labels of exception functions, which are used to determine special dependencies that allow
// otherwise restricted inter-container dependencies
type exceptionHandleFuncLabel int
const (
checkStubs exceptionHandleFuncLabel = iota
checkApexModule
checkInCommonApexes
checkApexIsNonUpdatable
checkNotDynamicDepTag
checkNotStaticOrDynamicDepTag
checkGlobalAllowlistedDep
)
// Map of [exceptionHandleFuncLabel] to the [exceptionHandleFunc]
var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]exceptionHandleFunc{
checkStubs: depIsStubsModule,
checkApexModule: depIsApexModule,
checkInCommonApexes: belongsToCommonApexes,
checkApexIsNonUpdatable: belongsToNonUpdatableApex,
checkNotDynamicDepTag: depIsNotDynamicDepTag,
checkNotStaticOrDynamicDepTag: depIsNotStaticOrDynamicDepTag,
checkGlobalAllowlistedDep: depIsGloballyAllowlisted,
}
// ----------------------------------------------------------------------------
// Start of the definitions of container determination functions.
//
// Similar to the above section, below defines the functions used to determine
// the container of each modules.
// ----------------------------------------------------------------------------
type containerBoundaryFunc func(mctx ModuleContext) bool
var vendorContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
m, ok := mctx.Module().(ImageInterface)
return mctx.Module().InstallInVendor() || (ok && m.VendorVariantNeeded(mctx))
}
var systemContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
module := mctx.Module()
return !module.InstallInTestcases() &&
!module.InstallInData() &&
!module.InstallInRamdisk() &&
!module.InstallInVendorRamdisk() &&
!module.InstallInDebugRamdisk() &&
!module.InstallInRecovery() &&
!module.InstallInVendor() &&
!module.InstallInOdm() &&
!module.InstallInProduct() &&
determineModuleKind(module.base(), mctx.blueprintBaseModuleContext()) == platformModule
}
var productContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
m, ok := mctx.Module().(ImageInterface)
return mctx.Module().InstallInProduct() || (ok && m.ProductVariantNeeded(mctx))
}
var apexContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
_, ok := ModuleProvider(mctx, AllApexInfoProvider)
return ok
}
var ctsContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
props := mctx.Module().GetProperties()
for _, prop := range props {
val := reflect.ValueOf(prop).Elem()
if val.Kind() == reflect.Struct {
testSuites := val.FieldByName("Test_suites")
if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") {
return true
}
}
}
return false
}
type unstableInfo struct {
// Determines if the module contains the private APIs of the platform.
ContainsPlatformPrivateApis bool
}
var unstableInfoProvider = blueprint.NewProvider[unstableInfo]()
func determineUnstableModule(mctx ModuleContext) bool {
module := mctx.Module()
unstableModule := module.Name() == "framework-minus-apex"
if installable, ok := module.(InstallableModule); ok {
for _, staticDepTag := range installable.StaticDependencyTags() {
mctx.VisitDirectDepsWithTag(staticDepTag, func(dep Module) {
if unstableInfo, ok := OtherModuleProvider(mctx, dep, unstableInfoProvider); ok {
unstableModule = unstableModule || unstableInfo.ContainsPlatformPrivateApis
}
})
}
}
return unstableModule
}
var unstableContainerBoundaryFunc containerBoundaryFunc = func(mctx ModuleContext) bool {
return determineUnstableModule(mctx)
}
// Map of [*container] to the [containerBoundaryFunc]
var containerBoundaryFunctionsTable = map[*container]containerBoundaryFunc{
VendorContainer: vendorContainerBoundaryFunc,
SystemContainer: systemContainerBoundaryFunc,
ProductContainer: productContainerBoundaryFunc,
ApexContainer: apexContainerBoundaryFunc,
CtsContainer: ctsContainerBoundaryFunc,
UnstableContainer: unstableContainerBoundaryFunc,
}
// ----------------------------------------------------------------------------
// End of the definitions of container determination functions.
// ----------------------------------------------------------------------------
type InstallableModule interface {
StaticDependencyTags() []blueprint.DependencyTag
DynamicDependencyTags() []blueprint.DependencyTag
}
type restriction struct {
// container of the dependency
dependency *container
// Error message to be emitted to the user when the dependency meets this restriction
errorMessage string
// List of labels of allowed exception functions that allows bypassing this restriction.
// If any of the functions mapped to each labels returns true, this dependency would be
// considered allowed and an error will not be thrown.
allowedExceptions []exceptionHandleFuncLabel
}
type container struct {
// The name of the container i.e. partition, api domain
name string
// Map of dependency restricted containers.
restricted []restriction
}
var (
VendorContainer = &container{
name: VendorVariation,
restricted: nil,
}
SystemContainer = &container{
name: "system",
restricted: []restriction{
{
dependency: VendorContainer,
errorMessage: "Module belonging to the system partition other than HALs is " +
"not allowed to depend on the vendor partition module, in order to support " +
"independent development/update cycles and to support the Generic System " +
"Image. Try depending on HALs, VNDK or AIDL instead.",
allowedExceptions: []exceptionHandleFuncLabel{
checkStubs,
checkNotDynamicDepTag,
checkGlobalAllowlistedDep,
},
},
},
}
ProductContainer = &container{
name: ProductVariation,
restricted: []restriction{
{
dependency: VendorContainer,
errorMessage: "Module belonging to the product partition is not allowed to " +
"depend on the vendor partition module, as this may lead to security " +
"vulnerabilities. Try depending on the HALs or utilize AIDL instead.",
allowedExceptions: []exceptionHandleFuncLabel{
checkStubs,
checkNotDynamicDepTag,
checkGlobalAllowlistedDep,
},
},
},
}
ApexContainer = initializeApexContainer()
CtsContainer = &container{
name: "cts",
restricted: []restriction{
{
dependency: UnstableContainer,
errorMessage: "CTS module should not depend on the modules that contain the " +
"platform implementation details, including \"framework\". Depending on these " +
"modules may lead to disclosure of implementation details and regression " +
"due to API changes across platform versions. Try depending on the stubs instead " +
"and ensure that the module sets an appropriate 'sdk_version'.",
allowedExceptions: []exceptionHandleFuncLabel{
checkStubs,
checkNotStaticOrDynamicDepTag,
checkGlobalAllowlistedDep,
},
},
},
}
// Container signifying that the module contains unstable platform private APIs
UnstableContainer = &container{
name: "unstable",
restricted: nil,
}
allContainers = []*container{
VendorContainer,
SystemContainer,
ProductContainer,
ApexContainer,
CtsContainer,
UnstableContainer,
}
)
func initializeApexContainer() *container {
apexContainer := &container{
name: "apex",
restricted: []restriction{
{
dependency: SystemContainer,
errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
"modules belonging to the system partition. Either statically depend on the " +
"module or convert the depending module to java_sdk_library and depend on " +
"the stubs.",
allowedExceptions: []exceptionHandleFuncLabel{
checkStubs,
checkApexModule,
checkInCommonApexes,
checkApexIsNonUpdatable,
checkNotStaticOrDynamicDepTag,
checkGlobalAllowlistedDep,
},
},
},
}
apexContainer.restricted = append(apexContainer.restricted, restriction{
dependency: apexContainer,
errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " +
"modules belonging to other Apex(es). Either include the depending " +
"module in the Apex or convert the depending module to java_sdk_library " +
"and depend on its stubs.",
allowedExceptions: []exceptionHandleFuncLabel{
checkStubs,
checkInCommonApexes,
checkNotStaticOrDynamicDepTag,
checkGlobalAllowlistedDep,
},
})
return apexContainer
}
type ContainersInfo struct {
belongingContainers []*container
belongingApexes []ApexInfo
}
func (c *ContainersInfo) BelongingContainers() []*container {
return c.belongingContainers
}
func (c *ContainersInfo) ApexNames() (ret []string) {
for _, apex := range c.belongingApexes {
ret = append(ret, apex.InApexModules...)
}
slices.Sort(ret)
return ret
}
// Returns true if any of the apex the module belongs to is updatable.
func (c *ContainersInfo) UpdatableApex() bool {
for _, apex := range c.belongingApexes {
if apex.Updatable {
return true
}
}
return false
}
var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]()
func satisfyAllowedExceptions(ctx ModuleContext, allowedExceptionLabels []exceptionHandleFuncLabel, m, dep Module) bool {
for _, label := range allowedExceptionLabels {
if exceptionHandleFunctionsTable[label](ctx, m, dep) {
return true
}
}
return false
}
func (c *ContainersInfo) GetViolations(mctx ModuleContext, m, dep Module, depInfo ContainersInfo) []string {
var violations []string
// Any containers that the module belongs to but the dependency does not belong to must be examined.
_, containersUniqueToModule, _ := ListSetDifference(c.belongingContainers, depInfo.belongingContainers)
// Apex container should be examined even if both the module and the dependency belong to
// the apex container to check that the two modules belong to the same apex.
if InList(ApexContainer, c.belongingContainers) && !InList(ApexContainer, containersUniqueToModule) {
containersUniqueToModule = append(containersUniqueToModule, ApexContainer)
}
for _, containerUniqueToModule := range containersUniqueToModule {
for _, restriction := range containerUniqueToModule.restricted {
if InList(restriction.dependency, depInfo.belongingContainers) {
if !satisfyAllowedExceptions(mctx, restriction.allowedExceptions, m, dep) {
violations = append(violations, restriction.errorMessage)
}
}
}
}
return violations
}
func generateContainerInfo(ctx ModuleContext) ContainersInfo {
var containers []*container
for _, cnt := range allContainers {
if containerBoundaryFunctionsTable[cnt](ctx) {
containers = append(containers, cnt)
}
}
var belongingApexes []ApexInfo
if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok {
belongingApexes = apexInfo.ApexInfos
}
return ContainersInfo{
belongingContainers: containers,
belongingApexes: belongingApexes,
}
}
func getContainerModuleInfo(ctx ModuleContext, module Module) (ContainersInfo, bool) {
if ctx.Module() == module {
return ctx.getContainersInfo(), true
}
return OtherModuleProvider(ctx, module, ContainersInfoProvider)
}
func setContainerInfo(ctx ModuleContext) {
// Required to determine the unstable container. This provider is set here instead of the
// unstableContainerBoundaryFunc in order to prevent setting the provider multiple times.
SetProvider(ctx, unstableInfoProvider, unstableInfo{
ContainsPlatformPrivateApis: determineUnstableModule(ctx),
})
if _, ok := ctx.Module().(InstallableModule); ok {
containersInfo := generateContainerInfo(ctx)
ctx.setContainersInfo(containersInfo)
SetProvider(ctx, ContainersInfoProvider, containersInfo)
}
}
func checkContainerViolations(ctx ModuleContext) {
if _, ok := ctx.Module().(InstallableModule); ok {
containersInfo, _ := getContainerModuleInfo(ctx, ctx.Module())
ctx.VisitDirectDepsIgnoreBlueprint(func(dep Module) {
if !dep.Enabled(ctx) {
return
}
// Pre-existing violating dependencies are tracked in containerDependencyViolationAllowlist.
// If this dependency is allowlisted, do not check for violation.
// If not, check if this dependency matches any restricted dependency and
// satisfies any exception functions, which allows bypassing the
// restriction. If all of the exceptions are not satisfied, throw an error.
if depContainersInfo, ok := getContainerModuleInfo(ctx, dep); ok {
if allowedViolations, ok := ContainerDependencyViolationAllowlist[ctx.ModuleName()]; ok && InList(dep.Name(), allowedViolations) {
return
} else {
violations := containersInfo.GetViolations(ctx, ctx.Module(), dep, depContainersInfo)
if len(violations) > 0 {
errorMessage := fmt.Sprintf("%s cannot depend on %s. ", ctx.ModuleName(), dep.Name())
errorMessage += strings.Join(violations, " ")
ctx.ModuleErrorf(errorMessage)
}
}
}
})
}
}

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,6 @@
package android
import (
"fmt"
"strings"
"github.com/google/blueprint"
@ -109,10 +108,6 @@ func (i DeapexerInfo) GetExportedModuleNames() []string {
return i.exportedModuleNames
}
// Provider that can be used from within the `GenerateAndroidBuildActions` of a module that depends
// on a `deapexer` module to retrieve its `DeapexerInfo`.
var DeapexerProvider = blueprint.NewProvider[DeapexerInfo]()
// NewDeapexerInfo creates and initializes a DeapexerInfo that is suitable
// for use with a prebuilt_apex module.
//
@ -169,41 +164,6 @@ type RequiresFilesFromPrebuiltApexTag interface {
RequiresFilesFromPrebuiltApex()
}
// FindDeapexerProviderForModule searches through the direct dependencies of the current context
// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
// deapexer module isn't found then it returns it an error
// clients should check the value of error and call ctx.ModuleErrof if a non nil error is received
func FindDeapexerProviderForModule(ctx ModuleContext) (*DeapexerInfo, error) {
var di *DeapexerInfo
var err error
ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
if err != nil {
// An err has been found. Do not visit further.
return
}
c, _ := OtherModuleProvider(ctx, m, DeapexerProvider)
p := &c
if di != nil {
// If two DeapexerInfo providers have been found then check if they are
// equivalent. If they are then use the selected one, otherwise fail.
if selected := equivalentDeapexerInfoProviders(di, p); selected != nil {
di = selected
return
}
err = fmt.Errorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s", di.ApexModuleName(), p.ApexModuleName())
}
di = p
})
if err != nil {
return nil, err
}
if di != nil {
return di, nil
}
ai, _ := ModuleProvider(ctx, ApexInfoProvider)
return nil, fmt.Errorf("No prebuilt APEX provides a deapexer module for APEX variant %s", ai.ApexVariationName)
}
// removeCompressedApexSuffix removes the _compressed suffix from the name if present.
func removeCompressedApexSuffix(name string) string {
return strings.TrimSuffix(name, "_compressed")

View file

@ -28,7 +28,7 @@ type defaultsDependencyTag struct {
var DefaultsDepTag defaultsDependencyTag
type defaultsProperties struct {
Defaults proptools.Configurable[[]string]
Defaults []string
}
type DefaultableModuleBase struct {
@ -69,7 +69,7 @@ type Defaultable interface {
// Apply defaults from the supplied Defaults to the property structures supplied to
// setProperties(...).
applyDefaults(TopDownMutatorContext, []Defaults)
applyDefaults(BottomUpMutatorContext, []Defaults)
// Set the hook to be called after any defaults have been applied.
//
@ -101,6 +101,7 @@ func InitDefaultableModule(module DefaultableModule) {
// A restricted subset of context methods, similar to LoadHookContext.
type DefaultableHookContext interface {
EarlyModuleContext
OtherModuleProviderContext
CreateModule(ModuleFactory, ...interface{}) Module
AddMissingDependencies(missingDeps []string)
@ -209,7 +210,7 @@ func InitDefaultsModule(module DefaultsModule) {
var _ Defaults = (*DefaultsModuleBase)(nil)
func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
func (defaultable *DefaultableModuleBase) applyDefaults(ctx BottomUpMutatorContext,
defaultsList []Defaults) {
for _, defaults := range defaultsList {
@ -226,7 +227,7 @@ func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContex
// Product variable properties need special handling, the type of the filtered product variable
// property struct may not be identical between the defaults module and the defaultable module.
// Use PrependMatchingProperties to apply whichever properties match.
func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx TopDownMutatorContext,
func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx BottomUpMutatorContext,
defaults Defaults, defaultableProp interface{}) {
if defaultableProp == nil {
return
@ -254,7 +255,7 @@ func (defaultable *DefaultableModuleBase) applyDefaultVariableProperties(ctx Top
}
}
func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMutatorContext,
func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx BottomUpMutatorContext,
defaults Defaults, defaultableProp interface{}) {
for _, def := range defaults.properties() {
@ -273,18 +274,22 @@ func (defaultable *DefaultableModuleBase) applyDefaultProperties(ctx TopDownMuta
func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) {
ctx.BottomUp("defaults_deps", defaultsDepsMutator).Parallel()
ctx.TopDown("defaults", defaultsMutator).Parallel()
ctx.BottomUp("defaults", defaultsMutator).Parallel()
}
func defaultsDepsMutator(ctx BottomUpMutatorContext) {
if defaultable, ok := ctx.Module().(Defaultable); ok {
ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults.GetOrDefault(ctx, nil)...)
ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults...)
}
}
func defaultsMutator(ctx TopDownMutatorContext) {
func defaultsMutator(ctx BottomUpMutatorContext) {
if defaultable, ok := ctx.Module().(Defaultable); ok {
defaults := defaultable.defaults().Defaults.GetOrDefault(ctx, nil)
if _, isDefaultsModule := ctx.Module().(Defaults); isDefaultsModule {
// Don't squash transitive defaults into defaults modules
return
}
defaults := defaultable.defaults().Defaults
if len(defaults) > 0 {
var defaultsList []Defaults
seen := make(map[Defaults]bool)
@ -295,7 +300,7 @@ func defaultsMutator(ctx TopDownMutatorContext) {
if !seen[defaults] {
seen[defaults] = true
defaultsList = append(defaultsList, defaults)
return len(defaults.defaults().Defaults.GetOrDefault(ctx, nil)) > 0
return len(defaults.defaults().Defaults) > 0
}
} else {
ctx.PropertyErrorf("defaults", "module %s is not an defaults module",

View file

@ -16,7 +16,6 @@ package android
import (
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
)
var (
@ -120,8 +119,3 @@ func init() {
return ctx.Config().RBEWrapper()
})
}
// GlobToListFileRule creates a rule that writes a list of files matching a pattern to a file.
func GlobToListFileRule(ctx ModuleContext, pattern string, excludes []string, file WritablePath) {
bootstrap.GlobFile(ctx.blueprintModuleContext(), pattern, excludes, file.String())
}

View file

@ -15,6 +15,9 @@
package android
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
)
@ -65,6 +68,30 @@ type DepSet[T depSettableType] struct {
transitive []*DepSet[T]
}
func (d *DepSet[T]) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := errors.Join(encoder.Encode(d.preorder), encoder.Encode(d.reverse),
encoder.Encode(d.order), encoder.Encode(d.direct), encoder.Encode(d.transitive))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (d *DepSet[T]) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := errors.Join(decoder.Decode(&d.preorder), decoder.Decode(&d.reverse),
decoder.Decode(&d.order), decoder.Decode(&d.direct), decoder.Decode(&d.transitive))
if err != nil {
return err
}
return nil
}
// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] {
var directCopy []T

View file

@ -14,11 +14,17 @@
package android
// ImageInterface is implemented by modules that need to be split by the imageMutator.
// ImageInterface is implemented by modules that need to be split by the imageTransitionMutator.
type ImageInterface interface {
// ImageMutatorBegin is called before any other method in the ImageInterface.
ImageMutatorBegin(ctx BaseModuleContext)
// VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image).
VendorVariantNeeded(ctx BaseModuleContext) bool
// ProductVariantNeeded should return true if the module needs a product variant (installed on the product image).
ProductVariantNeeded(ctx BaseModuleContext) bool
// CoreVariantNeeded should return true if the module needs a core variant (installed on the system image).
CoreVariantNeeded(ctx BaseModuleContext) bool
@ -49,6 +55,14 @@ type ImageInterface interface {
}
const (
// VendorVariation is the variant name used for /vendor code that does not
// compile against the VNDK.
VendorVariation string = "vendor"
// ProductVariation is the variant name used for /product code that does not
// compile against the VNDK.
ProductVariation string = "product"
// CoreVariation is the variant used for framework-private libraries, or
// SDK libraries. (which framework-private libraries can use), which
// will be installed to the system image.
@ -67,18 +81,16 @@ const (
DebugRamdiskVariation string = "debug_ramdisk"
)
// imageMutator creates variants for modules that implement the ImageInterface that
// imageTransitionMutator creates variants for modules that implement the ImageInterface that
// allow them to build differently for each partition (recovery, core, vendor, etc.).
func imageMutator(ctx BottomUpMutatorContext) {
if ctx.Os() != Android {
return
}
type imageTransitionMutator struct{}
if m, ok := ctx.Module().(ImageInterface); ok {
func (imageTransitionMutator) Split(ctx BaseModuleContext) []string {
var variations []string
if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok {
m.ImageMutatorBegin(ctx)
var variations []string
if m.CoreVariantNeeded(ctx) {
variations = append(variations, CoreVariation)
}
@ -94,18 +106,38 @@ func imageMutator(ctx BottomUpMutatorContext) {
if m.RecoveryVariantNeeded(ctx) {
variations = append(variations, RecoveryVariation)
}
if m.VendorVariantNeeded(ctx) {
variations = append(variations, VendorVariation)
}
if m.ProductVariantNeeded(ctx) {
variations = append(variations, ProductVariation)
}
extraVariations := m.ExtraImageVariations(ctx)
variations = append(variations, extraVariations...)
}
if len(variations) == 0 {
return
}
if len(variations) == 0 {
variations = append(variations, "")
}
mod := ctx.CreateVariations(variations...)
for i, v := range variations {
mod[i].base().setImageVariation(v)
mod[i].(ImageInterface).SetImageVariation(ctx, v)
}
return variations
}
func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
return sourceVariation
}
func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok {
return CoreVariation
}
return incomingVariation
}
func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
ctx.Module().base().setImageVariation(variation)
if m, ok := ctx.Module().(ImageInterface); ok {
m.SetImageVariation(ctx, variation)
}
}

23
android/init.go Normal file
View file

@ -0,0 +1,23 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import "encoding/gob"
func init() {
gob.Register(ModuleOutPath{})
gob.Register(PhonyPath{})
gob.Register(unstableInfo{})
}

View file

@ -33,7 +33,7 @@ var (
}, "args")
)
func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
func buildLicenseMetadata(ctx *moduleContext, licenseMetadataFile WritablePath) {
base := ctx.Module().base()
if !base.Enabled(ctx) {
@ -45,16 +45,15 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
}
var outputFiles Paths
if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
outputFiles, _ = outputFileProducer.OutputFiles("")
if outputFiles, err := outputFilesForModule(ctx, ctx.Module(), ""); err == nil {
outputFiles = PathsIfNonNil(outputFiles...)
}
// Only pass the last installed file to isContainerFromFileExtensions so a *.zip file in test data
// doesn't mark the whole module as a container.
var installFiles InstallPaths
if len(base.installFiles) > 0 {
installFiles = InstallPaths{base.installFiles[len(base.installFiles)-1]}
if len(ctx.installFiles) > 0 {
installFiles = InstallPaths{ctx.installFiles[len(ctx.installFiles)-1]}
}
isContainer := isContainerFromFileExtensions(installFiles, outputFiles)
@ -93,7 +92,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 {
if depInstallFiles := OtherModuleProviderOrDefault(ctx, dep, InstallFilesProvider).InstallFiles; len(depInstallFiles) > 0 {
allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
} else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
depOutputFiles = PathsIfNonNil(depOutputFiles...)
@ -153,7 +152,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
// Install map
args = append(args,
JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m "))
JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.licenseInstallMap), "-m "))
// Built files
if len(outputFiles) > 0 {
@ -163,7 +162,7 @@ func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
// Installed files
args = append(args,
JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(ctx.installFiles.Strings()), "-i "))
if isContainer {
args = append(args, "--is_container")

View file

@ -42,7 +42,7 @@ func (l *logtagsSingleton) GenerateBuildActions(ctx SingletonContext) {
if !module.ExportedToMake() {
return
}
if logtagsInfo, ok := SingletonModuleProvider(ctx, module, LogtagsProviderKey); ok {
if logtagsInfo, ok := OtherModuleProvider(ctx, module, LogtagsProviderKey); ok {
allLogtags = append(allLogtags, logtagsInfo.Logtags...)
}
})

View file

@ -94,7 +94,7 @@ type MakeVarsContext interface {
ModuleDir(module blueprint.Module) string
ModuleSubDir(module blueprint.Module) string
ModuleType(module blueprint.Module) string
moduleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
otherModuleProvider(module blueprint.Module, key blueprint.AnyProviderKey) (any, bool)
BlueprintFile(module blueprint.Module) string
ModuleErrorf(module blueprint.Module, format string, args ...interface{})
@ -279,10 +279,11 @@ func (s *makeVarsSingleton) GenerateBuildActions(ctx SingletonContext) {
}
if m.ExportedToMake() {
katiInstalls = append(katiInstalls, m.base().katiInstalls...)
katiInitRcInstalls = append(katiInitRcInstalls, m.base().katiInitRcInstalls...)
katiVintfManifestInstalls = append(katiVintfManifestInstalls, m.base().katiVintfInstalls...)
katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
info := OtherModuleProviderOrDefault(ctx, m, InstallFilesProvider)
katiInstalls = append(katiInstalls, info.KatiInstalls...)
katiInitRcInstalls = append(katiInitRcInstalls, info.KatiInitRcInstalls...)
katiVintfManifestInstalls = append(katiVintfManifestInstalls, info.KatiVintfInstalls...)
katiSymlinks = append(katiSymlinks, info.KatiSymlinks...)
}
})

File diff suppressed because it is too large Load diff

View file

@ -85,7 +85,9 @@ type ModuleBuildParams BuildParams
type ModuleContext interface {
BaseModuleContext
blueprintModuleContext() blueprint.ModuleContext
// BlueprintModuleContext returns the blueprint.ModuleContext that the ModuleContext wraps. It may only be
// used by the golang module types that need to call into the bootstrap module types.
BlueprintModuleContext() blueprint.ModuleContext
// Deprecated: use ModuleContext.Build instead.
ModuleBuild(pctx PackageContext, params ModuleBuildParams)
@ -107,68 +109,79 @@ type ModuleContext interface {
// InstallExecutable creates a rule to copy srcPath to name in the installPath directory,
// with the given additional dependencies. The file is marked executable after copying.
//
// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
// installed file will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
// InstallFile creates a rule to copy srcPath to name in the installPath directory,
// with the given additional dependencies.
//
// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
// InstallFileWithoutCheckbuild creates a rule to copy srcPath to name in the installPath directory,
// with the given additional dependencies, but does not add the file to the list of files to build
// during `m checkbuild`.
//
// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
// installed file will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
InstallFile(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path, deps ...InstallPath) InstallPath
// InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath
// directory, and also unzip a zip file containing extra files to install into the same
// directory.
//
// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
// installed file will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The installed file can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...InstallPath) InstallPath
// InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath
// directory.
//
// The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
// installed file will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath
// InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name
// in the installPath directory.
//
// The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the
// installed file will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The installed symlink can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed file can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath
// InstallTestData creates rules to install test data (e.g. data files used during a test) into
// the installPath directory.
//
// The installed files will be returned by FilesToInstall(), and the PackagingSpec for the
// installed files will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The installed files can be accessed by InstallFilesInfo.InstallFiles, and the PackagingSpec
// for the installed files can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
InstallTestData(installPath InstallPath, data []DataPath) InstallPaths
// PackageFile creates a PackagingSpec as if InstallFile was called, but without creating
// the rule to copy the file. This is useful to define how a module would be packaged
// without installing it into the global installation directories.
//
// The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by
// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
// for which IsInstallDepNeeded returns true.
// The created PackagingSpec can be accessed by InstallFilesInfo.PackagingSpecs on this module
// or by InstallFilesInfo.TransitivePackagingSpecs on modules that depend on this module through
// dependency tags for which IsInstallDepNeeded returns true.
PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
CheckbuildFile(srcPath Path)
CheckbuildFile(srcPaths ...Path)
UncheckedModule()
InstallInData() bool
InstallInTestcases() bool
@ -183,12 +196,11 @@ type ModuleContext interface {
InstallInVendor() bool
InstallForceOS() (*OsType, *ArchType)
RequiredModuleNames(ctx ConfigAndErrorContext) []string
RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
HostRequiredModuleNames() []string
TargetRequiredModuleNames() []string
ModuleSubDir() string
SoongConfigTraceHash() string
Variable(pctx PackageContext, name, value string)
Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule
@ -216,19 +228,58 @@ type ModuleContext interface {
// SetOutputFiles stores the outputFiles to outputFiles property, which is used
// to set the OutputFilesProvider later.
SetOutputFiles(outputFiles Paths, tag string)
GetOutputFiles() OutputFilesInfo
// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
// apex container for use when generation the license metadata file.
SetLicenseInstallMap(installMap []string)
// ComplianceMetadataInfo returns a ComplianceMetadataInfo instance for different module types to dump metadata,
// which usually happens in GenerateAndroidBuildActions() of a module type.
// See android.ModuleBase.complianceMetadataInfo
ComplianceMetadataInfo() *ComplianceMetadataInfo
// Get the information about the containers this module belongs to.
getContainersInfo() ContainersInfo
setContainersInfo(info ContainersInfo)
setAconfigPaths(paths Paths)
}
type moduleContext struct {
bp blueprint.ModuleContext
baseModuleContext
packagingSpecs []PackagingSpec
installFiles InstallPaths
checkbuildFiles Paths
module Module
phonies map[string]Paths
packagingSpecs []PackagingSpec
installFiles InstallPaths
checkbuildFiles Paths
checkbuildTarget Path
uncheckedModule bool
module Module
phonies map[string]Paths
// outputFiles stores the output of a module by tag and is used to set
// the OutputFilesProvider in GenerateBuildActions
outputFiles OutputFilesInfo
katiInstalls []katiInstall
katiSymlinks []katiInstall
TransitiveInstallFiles *DepSet[InstallPath]
// set of dependency module:location mappings used to populate the license metadata for
// apex containers.
licenseInstallMap []string
// The path to the generated license metadata file for the module.
licenseMetadataFile WritablePath
katiInstalls katiInstalls
katiSymlinks katiInstalls
// katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are
// allowed to have duplicates across modules and variants.
katiInitRcInstalls katiInstalls
katiVintfInstalls katiInstalls
initRcPaths Paths
vintfFragmentsPaths Paths
installedInitRcPaths InstallPaths
installedVintfFragmentsPaths InstallPaths
testData []DataPath
@ -236,6 +287,21 @@ type moduleContext struct {
buildParams []BuildParams
ruleParams map[blueprint.Rule]blueprint.RuleParams
variables map[string]string
// moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will
// be included in the final module-info.json produced by Make.
moduleInfoJSON *ModuleInfoJSON
// containersInfo stores the information about the containers and the information of the
// apexes the module belongs to.
containersInfo ContainersInfo
// Merged Aconfig files for all transitive deps.
aconfigFilePaths Paths
// complianceMetadataInfo is for different module types to dump metadata.
// See android.ModuleContext interface.
complianceMetadataInfo *ComplianceMetadataInfo
}
var _ ModuleContext = &moduleContext{}
@ -357,7 +423,7 @@ func (m *moduleContext) Build(pctx PackageContext, params BuildParams) {
}
func (m *moduleContext) Phony(name string, deps ...Path) {
addPhony(m.config, name, deps...)
m.phonies[name] = append(m.phonies[name], deps...)
}
func (m *moduleContext) GetMissingDependencies() []string {
@ -377,10 +443,6 @@ func (m *moduleContext) ModuleSubDir() string {
return m.bp.ModuleSubDir()
}
func (m *moduleContext) SoongConfigTraceHash() string {
return m.module.base().commonProperties.SoongConfigTraceHash
}
func (m *moduleContext) InstallInData() bool {
return m.module.InstallInData()
}
@ -465,17 +527,22 @@ func (m *moduleContext) requiresFullInstall() bool {
func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
deps ...InstallPath) InstallPath {
return m.installFile(installPath, name, srcPath, deps, false, true, nil)
return m.installFile(installPath, name, srcPath, deps, false, true, true, nil)
}
func (m *moduleContext) InstallFileWithoutCheckbuild(installPath InstallPath, name string, srcPath Path,
deps ...InstallPath) InstallPath {
return m.installFile(installPath, name, srcPath, deps, false, true, false, nil)
}
func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
deps ...InstallPath) InstallPath {
return m.installFile(installPath, name, srcPath, deps, true, true, nil)
return m.installFile(installPath, name, srcPath, deps, true, true, true, nil)
}
func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path,
extraZip Path, deps ...InstallPath) InstallPath {
return m.installFile(installPath, name, srcPath, deps, false, true, &extraFilesZip{
return m.installFile(installPath, name, srcPath, deps, false, true, true, &extraFilesZip{
zip: extraZip,
dir: installPath,
})
@ -487,11 +554,16 @@ func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPat
}
func (m *moduleContext) getAconfigPaths() *Paths {
return &m.module.base().aconfigFilePaths
return &m.aconfigFilePaths
}
func (m *moduleContext) setAconfigPaths(paths Paths) {
m.aconfigFilePaths = paths
}
func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec {
licenseFiles := m.Module().EffectiveLicenseFiles()
overrides := CopyOf(m.Module().base().commonProperties.Overrides)
spec := PackagingSpec{
relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
srcPath: srcPath,
@ -502,13 +574,15 @@ func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, e
skipInstall: m.skipInstall(),
aconfigPaths: m.getAconfigPaths(),
archType: m.target.Arch.ArchType,
overrides: &overrides,
owner: m.ModuleName(),
}
m.packagingSpecs = append(m.packagingSpecs, spec)
return spec
}
func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []InstallPath,
executable bool, hooks bool, extraZip *extraFilesZip) InstallPath {
executable bool, hooks bool, checkbuild bool, extraZip *extraFilesZip) InstallPath {
fullInstallPath := installPath.Join(m, name)
if hooks {
@ -516,9 +590,9 @@ func (m *moduleContext) installFile(installPath InstallPath, name string, srcPat
}
if m.requiresFullInstall() {
deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
deps = append(deps, m.module.base().installedInitRcPaths...)
deps = append(deps, m.module.base().installedVintfFragmentsPaths...)
deps = append(deps, InstallPaths(m.TransitiveInstallFiles.ToList())...)
deps = append(deps, m.installedInitRcPaths...)
deps = append(deps, m.installedVintfFragmentsPaths...)
var implicitDeps, orderOnlyDeps Paths
@ -575,7 +649,9 @@ func (m *moduleContext) installFile(installPath InstallPath, name string, srcPat
m.packageFile(fullInstallPath, srcPath, executable)
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
if checkbuild {
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
return fullInstallPath
}
@ -616,9 +692,9 @@ func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, src
}
m.installFiles = append(m.installFiles, fullInstallPath)
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
}
overrides := CopyOf(m.Module().base().commonProperties.Overrides)
m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
srcPath: nil,
@ -628,6 +704,8 @@ func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, src
skipInstall: m.skipInstall(),
aconfigPaths: m.getAconfigPaths(),
archType: m.target.Arch.ArchType,
overrides: &overrides,
owner: m.ModuleName(),
})
return fullInstallPath
@ -663,6 +741,7 @@ func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name str
m.installFiles = append(m.installFiles, fullInstallPath)
}
overrides := CopyOf(m.Module().base().commonProperties.Overrides)
m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{
relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()),
srcPath: nil,
@ -672,6 +751,8 @@ func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name str
skipInstall: m.skipInstall(),
aconfigPaths: m.getAconfigPaths(),
archType: m.target.Arch.ArchType,
overrides: &overrides,
owner: m.ModuleName(),
})
return fullInstallPath
@ -683,52 +764,73 @@ func (m *moduleContext) InstallTestData(installPath InstallPath, data []DataPath
ret := make(InstallPaths, 0, len(data))
for _, d := range data {
relPath := d.ToRelativeInstallPath()
installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, nil)
installed := m.installFile(installPath, relPath, d.SrcPath, nil, false, false, true, nil)
ret = append(ret, installed)
}
return ret
}
func (m *moduleContext) CheckbuildFile(srcPath Path) {
m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
// CheckbuildFile specifies the output files that should be built by checkbuild.
func (m *moduleContext) CheckbuildFile(srcPaths ...Path) {
m.checkbuildFiles = append(m.checkbuildFiles, srcPaths...)
}
func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
// UncheckedModule marks the current module has having no files that should be built by checkbuild.
func (m *moduleContext) UncheckedModule() {
m.uncheckedModule = true
}
func (m *moduleContext) BlueprintModuleContext() blueprint.ModuleContext {
return m.bp
}
func (m *moduleContext) LicenseMetadataFile() Path {
return m.module.base().licenseMetadataFile
return m.licenseMetadataFile
}
func (m *moduleContext) ModuleInfoJSON() *ModuleInfoJSON {
if moduleInfoJSON := m.module.base().moduleInfoJSON; moduleInfoJSON != nil {
if moduleInfoJSON := m.moduleInfoJSON; moduleInfoJSON != nil {
return moduleInfoJSON
}
moduleInfoJSON := &ModuleInfoJSON{}
m.module.base().moduleInfoJSON = moduleInfoJSON
m.moduleInfoJSON = moduleInfoJSON
return moduleInfoJSON
}
func (m *moduleContext) SetOutputFiles(outputFiles Paths, tag string) {
if tag == "" {
if len(m.module.base().outputFiles.DefaultOutputFiles) > 0 {
if len(m.outputFiles.DefaultOutputFiles) > 0 {
m.ModuleErrorf("Module %s default OutputFiles cannot be overwritten", m.ModuleName())
}
m.module.base().outputFiles.DefaultOutputFiles = outputFiles
m.outputFiles.DefaultOutputFiles = outputFiles
} else {
if m.module.base().outputFiles.TaggedOutputFiles == nil {
m.module.base().outputFiles.TaggedOutputFiles = make(map[string]Paths)
if m.outputFiles.TaggedOutputFiles == nil {
m.outputFiles.TaggedOutputFiles = make(map[string]Paths)
}
if _, exists := m.module.base().outputFiles.TaggedOutputFiles[tag]; exists {
if _, exists := m.outputFiles.TaggedOutputFiles[tag]; exists {
m.ModuleErrorf("Module %s OutputFiles at tag %s cannot be overwritten", m.ModuleName(), tag)
} else {
m.module.base().outputFiles.TaggedOutputFiles[tag] = outputFiles
m.outputFiles.TaggedOutputFiles[tag] = outputFiles
}
}
}
func (m *moduleContext) GetOutputFiles() OutputFilesInfo {
return m.outputFiles
}
func (m *moduleContext) SetLicenseInstallMap(installMap []string) {
m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
}
func (m *moduleContext) ComplianceMetadataInfo() *ComplianceMetadataInfo {
if m.complianceMetadataInfo == nil {
m.complianceMetadataInfo = NewComplianceMetadataInfo()
}
return m.complianceMetadataInfo
}
// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must
// be tagged with `android:"path" to support automatic source module dependency resolution.
//
@ -755,7 +857,7 @@ func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) Optional
return OptionalPath{}
}
func (m *moduleContext) RequiredModuleNames(ctx ConfigAndErrorContext) []string {
func (m *moduleContext) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string {
return m.module.RequiredModuleNames(ctx)
}
@ -766,3 +868,11 @@ func (m *moduleContext) HostRequiredModuleNames() []string {
func (m *moduleContext) TargetRequiredModuleNames() []string {
return m.module.TargetRequiredModuleNames()
}
func (m *moduleContext) getContainersInfo() ContainersInfo {
return m.containersInfo
}
func (m *moduleContext) setContainersInfo(info ContainersInfo) {
m.containersInfo = info
}

View file

@ -722,6 +722,7 @@ test {
propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
propInfo{Name: "B", Type: "bool", Value: "true"},
propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
propInfo{Name: "Name", Type: "string", Value: "foo"},
propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
@ -933,31 +934,54 @@ func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) {
}
}
type fakeBlueprintModule struct{}
func (fakeBlueprintModule) Name() string { return "foo" }
func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
type sourceProducerTestModule struct {
fakeBlueprintModule
source Path
ModuleBase
props struct {
// A represents the source file
A string
}
}
func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
type outputFileProducerTestModule struct {
fakeBlueprintModule
output map[string]Path
error map[string]error
func sourceProducerTestModuleFactory() Module {
module := &sourceProducerTestModule{}
module.AddProperties(&module.props)
InitAndroidModule(module)
return module
}
func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
return PathsIfNonNil(o.output[tag]), o.error[tag]
func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {}
func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) }
type outputFilesTestModule struct {
ModuleBase
props struct {
// A represents the tag
A string
// B represents the output file for tag A
B string
}
}
func outputFilesTestModuleFactory() Module {
module := &outputFilesTestModule{}
module.AddProperties(&module.props)
InitAndroidModule(module)
return module
}
func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
if o.props.A != "" || o.props.B != "" {
ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A)
}
// This is to simulate the case that some module uses an object to set its
// OutputFilesProvider, but the object itself is empty.
ctx.SetOutputFiles(Paths{}, "missing")
}
type pathContextAddMissingDependenciesWrapper struct {
PathContext
OtherModuleProviderContext
missingDeps []string
}
@ -968,52 +992,91 @@ func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module bluepr
return module.Name()
}
func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo {
return OutputFilesInfo{}
}
func TestOutputFileForModule(t *testing.T) {
testcases := []struct {
name string
module blueprint.Module
bp string
tag string
env map[string]string
config func(*config)
expected string
missingDeps []string
env map[string]string
config func(*config)
}{
{
name: "SourceFileProducer",
module: &sourceProducerTestModule{source: PathForTesting("foo.txt")},
expected: "foo.txt",
name: "SourceFileProducer",
bp: `spt_module {
name: "test_module",
a: "spt.txt",
}
`,
tag: "",
expected: "spt.txt",
},
{
name: "OutputFileProducer",
module: &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
expected: "foo.txt",
name: "OutputFileProviderEmptyStringTag",
bp: `oft_module {
name: "test_module",
a: "",
b: "empty.txt",
}
`,
tag: "",
expected: "empty.txt",
},
{
name: "OutputFileProducer_tag",
module: &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
name: "OutputFileProviderTag",
bp: `oft_module {
name: "test_module",
a: "foo",
b: "foo.txt",
}
`,
tag: "foo",
expected: "foo.txt",
},
{
name: "OutputFileProducer_AllowMissingDependencies",
name: "OutputFileAllowMissingDependencies",
bp: `oft_module {
name: "test_module",
}
`,
tag: "missing",
expected: "missing_output_file/test_module",
missingDeps: []string{"test_module"},
config: func(config *config) {
config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
},
module: &outputFileProducerTestModule{},
missingDeps: []string{"foo"},
expected: "missing_output_file/foo",
},
}
for _, tt := range testcases {
config := TestConfig(buildDir, tt.env, "", nil)
if tt.config != nil {
tt.config(config.config)
}
ctx := &pathContextAddMissingDependenciesWrapper{
PathContext: PathContextForTesting(config),
}
got := OutputFileForModule(ctx, tt.module, tt.tag)
AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
t.Run(tt.name, func(t *testing.T) {
result := GroupFixturePreparers(
PrepareForTestWithDefaults,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory)
ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory)
}),
FixtureWithRootAndroidBp(tt.bp),
).RunTest(t)
config := TestConfig(buildDir, tt.env, tt.bp, nil)
if tt.config != nil {
tt.config(config.config)
}
ctx := &pathContextAddMissingDependenciesWrapper{
PathContext: PathContextForTesting(config),
OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
}
got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag)
AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
})
}
}

View file

@ -148,9 +148,9 @@ var preArch = []RegisterMutatorFunc{
}
func registerArchMutator(ctx RegisterMutatorsContext) {
ctx.BottomUpBlueprint("os", osMutator).Parallel()
ctx.BottomUp("image", imageMutator).Parallel()
ctx.BottomUpBlueprint("arch", archMutator).Parallel()
ctx.Transition("os", &osTransitionMutator{})
ctx.Transition("image", &imageTransitionMutator{})
ctx.Transition("arch", &archTransitionMutator{})
}
var preDeps = []RegisterMutatorFunc{
@ -193,16 +193,16 @@ type BaseMutatorContext interface {
// Rename all variants of a module. The new name is not visible to calls to ModuleName,
// AddDependency or OtherModuleName until after this mutator pass is complete.
Rename(name string)
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
// the specified property structs to it as if the properties were set in a blueprint file.
CreateModule(ModuleFactory, ...interface{}) Module
}
type TopDownMutator func(TopDownMutatorContext)
type TopDownMutatorContext interface {
BaseMutatorContext
// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
// the specified property structs to it as if the properties were set in a blueprint file.
CreateModule(ModuleFactory, ...interface{}) Module
}
type topDownMutatorContext struct {
@ -400,6 +400,12 @@ type IncomingTransitionContext interface {
Config() Config
DeviceConfig() DeviceConfig
// IsAddingDependency returns true if the transition is being called while adding a dependency
// after the transition mutator has already run, or false if it is being called when the transition
// mutator is running. This should be used sparingly, all uses will have to be removed in order
// to support creating variants on demand.
IsAddingDependency() bool
}
type OutgoingTransitionContext interface {
@ -510,6 +516,9 @@ type androidTransitionMutator struct {
}
func (a *androidTransitionMutator) Split(ctx blueprint.BaseModuleContext) []string {
if a.finalPhase {
panic("TransitionMutator not allowed in FinalDepsMutators")
}
if m, ok := ctx.Module().(Module); ok {
moduleContext := m.base().baseModuleContextFactory(ctx)
return a.mutator.Split(&moduleContext)
@ -574,6 +583,10 @@ func (c *incomingTransitionContextImpl) DeviceConfig() DeviceConfig {
return DeviceConfig{c.bp.Config().(Config).deviceConfig}
}
func (c *incomingTransitionContextImpl) IsAddingDependency() bool {
return c.bp.IsAddingDependency()
}
func (c *incomingTransitionContextImpl) provider(provider blueprint.AnyProviderKey) (any, bool) {
return c.bp.Provider(provider)
}
@ -729,6 +742,14 @@ func (b *bottomUpMutatorContext) Rename(name string) {
b.Module().base().commonProperties.DebugName = name
}
func (b *bottomUpMutatorContext) createModule(factory blueprint.ModuleFactory, name string, props ...interface{}) blueprint.Module {
return b.bp.CreateModule(factory, name, props...)
}
func (b *bottomUpMutatorContext) CreateModule(factory ModuleFactory, props ...interface{}) Module {
return createModule(b, factory, "_bottomUpMutatorModule", props...)
}
func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
if b.baseModuleContext.checkedMissingDeps() {
panic("Adding deps not allowed after checking for missing deps")

View file

@ -81,6 +81,40 @@ func TestMutatorAddMissingDependencies(t *testing.T) {
AssertDeepEquals(t, "foo missing deps", []string{"added_missing_dep", "regular_missing_dep"}, foo.missingDeps)
}
type testTransitionMutator struct {
split func(ctx BaseModuleContext) []string
outgoingTransition func(ctx OutgoingTransitionContext, sourceVariation string) string
incomingTransition func(ctx IncomingTransitionContext, incomingVariation string) string
mutate func(ctx BottomUpMutatorContext, variation string)
}
func (t *testTransitionMutator) Split(ctx BaseModuleContext) []string {
if t.split != nil {
return t.split(ctx)
}
return []string{""}
}
func (t *testTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
if t.outgoingTransition != nil {
return t.outgoingTransition(ctx, sourceVariation)
}
return sourceVariation
}
func (t *testTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
if t.incomingTransition != nil {
return t.incomingTransition(ctx, incomingVariation)
}
return incomingVariation
}
func (t *testTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
if t.mutate != nil {
t.mutate(ctx, variation)
}
}
func TestModuleString(t *testing.T) {
bp := `
test {
@ -94,9 +128,11 @@ func TestModuleString(t *testing.T) {
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreArchMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("pre_arch", func(ctx BottomUpMutatorContext) {
moduleStrings = append(moduleStrings, ctx.Module().String())
ctx.CreateVariations("a", "b")
ctx.Transition("pre_arch", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
moduleStrings = append(moduleStrings, ctx.Module().String())
return []string{"a", "b"}
},
})
ctx.TopDown("rename_top_down", func(ctx TopDownMutatorContext) {
moduleStrings = append(moduleStrings, ctx.Module().String())
@ -105,16 +141,23 @@ func TestModuleString(t *testing.T) {
})
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("pre_deps", func(ctx BottomUpMutatorContext) {
moduleStrings = append(moduleStrings, ctx.Module().String())
ctx.CreateVariations("c", "d")
ctx.Transition("pre_deps", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
moduleStrings = append(moduleStrings, ctx.Module().String())
return []string{"c", "d"}
},
})
})
ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("post_deps", func(ctx BottomUpMutatorContext) {
moduleStrings = append(moduleStrings, ctx.Module().String())
ctx.CreateLocalVariations("e", "f")
ctx.Transition("post_deps", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
moduleStrings = append(moduleStrings, ctx.Module().String())
return []string{"e", "f"}
},
outgoingTransition: func(ctx OutgoingTransitionContext, sourceVariation string) string {
return ""
},
})
ctx.BottomUp("rename_bottom_up", func(ctx BottomUpMutatorContext) {
moduleStrings = append(moduleStrings, ctx.Module().String())
@ -138,15 +181,15 @@ func TestModuleString(t *testing.T) {
"foo{pre_arch:b}",
"foo{pre_arch:a}",
// After rename_top_down.
"foo_renamed1{pre_arch:a}",
// After rename_top_down (reversed because pre_deps TransitionMutator.Split is TopDown).
"foo_renamed1{pre_arch:b}",
"foo_renamed1{pre_arch:a}",
// After pre_deps.
"foo_renamed1{pre_arch:a,pre_deps:c}",
"foo_renamed1{pre_arch:a,pre_deps:d}",
"foo_renamed1{pre_arch:b,pre_deps:c}",
// After pre_deps (reversed because post_deps TransitionMutator.Split is TopDown).
"foo_renamed1{pre_arch:b,pre_deps:d}",
"foo_renamed1{pre_arch:b,pre_deps:c}",
"foo_renamed1{pre_arch:a,pre_deps:d}",
"foo_renamed1{pre_arch:a,pre_deps:c}",
// After post_deps.
"foo_renamed1{pre_arch:a,pre_deps:c,post_deps:e}",
@ -202,8 +245,10 @@ func TestFinalDepsPhase(t *testing.T) {
ctx.AddFarVariationDependencies([]blueprint.Variation{}, dep1Tag, "common_dep_1")
}
})
ctx.BottomUp("variant", func(ctx BottomUpMutatorContext) {
ctx.CreateLocalVariations("a", "b")
ctx.Transition("variant", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
return []string{"a", "b"}
},
})
})
@ -243,27 +288,20 @@ func TestFinalDepsPhase(t *testing.T) {
}
func TestNoCreateVariationsInFinalDeps(t *testing.T) {
checkErr := func() {
if err := recover(); err == nil || !strings.Contains(fmt.Sprintf("%s", err), "not allowed in FinalDepsMutators") {
panic("Expected FinalDepsMutators consistency check to fail")
}
}
GroupFixturePreparers(
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("vars", func(ctx BottomUpMutatorContext) {
defer checkErr()
ctx.CreateVariations("a", "b")
})
ctx.BottomUp("local_vars", func(ctx BottomUpMutatorContext) {
defer checkErr()
ctx.CreateLocalVariations("a", "b")
ctx.Transition("vars", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
return []string{"a", "b"}
},
})
})
ctx.RegisterModuleType("test", mutatorTestModuleFactory)
}),
FixtureWithRootAndroidBp(`test {name: "foo"}`),
).RunTest(t)
).
ExtendWithErrorHandler(FixtureExpectsOneErrorPattern("not allowed in FinalDepsMutators")).
RunTest(t)
}

View file

@ -60,6 +60,7 @@ func init() {
AddNeverAllowRules(createCcStubsRule())
AddNeverAllowRules(createJavaExcludeStaticLibsRule())
AddNeverAllowRules(createProhibitHeaderOnlyRule())
AddNeverAllowRules(createLimitNdkExportRule()...)
AddNeverAllowRules(createKotlinPluginRule()...)
}
@ -183,6 +184,7 @@ func createCcSdkVariantRules() []Rule {
"packages/modules/SdkExtensions/derive_sdk",
// These are for apps and shouldn't be used by non-SDK variant modules.
"prebuilts/ndk",
"frameworks/native/libs/binder/ndk",
"tools/test/graphicsbenchmark/apps/sample_app",
"tools/test/graphicsbenchmark/functional_tests/java",
"vendor/xts/gts-tests/hostsidetests/gamedevicecert/apps/javatests",
@ -213,7 +215,7 @@ func createCcSdkVariantRules() []Rule {
func createCcStubsRule() Rule {
ccStubsImplementationInstallableProjectsAllowedList := []string{
"packages/modules/Virtualization/vm_payload",
"packages/modules/Virtualization/libs/libvm_payload",
}
return NeverAllow().
@ -238,6 +240,7 @@ func createInitFirstStageRules() []Rule {
Without("name", "init_first_stage").
Without("name", "init_first_stage.microdroid").
With("install_in_root", "true").
NotModuleType("prebuilt_root").
Because("install_in_root is only for init_first_stage."),
}
}
@ -266,6 +269,22 @@ func createProhibitHeaderOnlyRule() Rule {
Because("headers_only can only be used for generating framework-minus-apex headers for non-updatable modules")
}
func createLimitNdkExportRule() []Rule {
reason := "If the headers you're trying to export are meant to be a part of the NDK, they should be exposed by an ndk_headers module. If the headers shouldn't be a part of the NDK, the headers should instead be exposed from a separate `cc_library_headers` which consumers depend on."
// DO NOT ADD HERE - please consult danalbert@
// b/357711733
return []Rule{
NeverAllow().
NotIn("frameworks/native/libs/binder/ndk").
ModuleType("ndk_library").
WithMatcher("export_header_libs", isSetMatcherInstance).Because(reason),
NeverAllow().ModuleType("ndk_library").WithMatcher("export_generated_headers", isSetMatcherInstance).Because(reason),
NeverAllow().ModuleType("ndk_library").WithMatcher("export_include_dirs", isSetMatcherInstance).Because(reason),
NeverAllow().ModuleType("ndk_library").WithMatcher("export_shared_lib_headers", isSetMatcherInstance).Because(reason),
NeverAllow().ModuleType("ndk_library").WithMatcher("export_static_lib_headers", isSetMatcherInstance).Because(reason),
}
}
func createKotlinPluginRule() []Rule {
kotlinPluginProjectsAllowedList := []string{
// TODO: Migrate compose plugin to the bundled compiler plugin

View file

@ -36,10 +36,22 @@ func modulesOutputDirs(ctx BuilderContext, modules ...Module) []string {
return SortedUniqueStrings(dirs)
}
func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths {
type BuilderAndOtherModuleProviderContext interface {
BuilderContext
OtherModuleProviderContext
}
func modulesLicenseMetadata(ctx OtherModuleProviderContext, modules ...Module) Paths {
result := make(Paths, 0, len(modules))
mctx, isMctx := ctx.(ModuleContext)
for _, module := range modules {
if mf := module.base().licenseMetadataFile; mf != nil {
var mf Path
if isMctx && mctx.Module() == module {
mf = mctx.LicenseMetadataFile()
} else {
mf = OtherModuleProviderOrDefault(ctx, module, InstallFilesProvider).LicenseMetadataFile
}
if mf != nil {
result = append(result, mf)
}
}
@ -48,7 +60,7 @@ func modulesLicenseMetadata(ctx BuilderContext, modules ...Module) Paths {
// buildNoticeOutputFromLicenseMetadata writes out a notice file.
func buildNoticeOutputFromLicenseMetadata(
ctx BuilderContext, tool, ruleName string, outputFile WritablePath,
ctx BuilderAndOtherModuleProviderContext, tool, ruleName string, outputFile WritablePath,
libraryName string, stripPrefix []string, modules ...Module) {
depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
rule := NewRuleBuilder(pctx, ctx)
@ -84,7 +96,7 @@ func buildNoticeOutputFromLicenseMetadata(
// on the license metadata files for the input `modules` defaulting to the
// current context module if none given.
func BuildNoticeTextOutputFromLicenseMetadata(
ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
stripPrefix []string, modules ...Module) {
buildNoticeOutputFromLicenseMetadata(ctx, "textnotice", "text_notice_"+ruleName,
outputFile, libraryName, stripPrefix, modules...)
@ -94,7 +106,7 @@ func BuildNoticeTextOutputFromLicenseMetadata(
// on the license metadata files for the input `modules` defaulting to the
// current context module if none given.
func BuildNoticeHtmlOutputFromLicenseMetadata(
ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
stripPrefix []string, modules ...Module) {
buildNoticeOutputFromLicenseMetadata(ctx, "htmlnotice", "html_notice_"+ruleName,
outputFile, libraryName, stripPrefix, modules...)
@ -104,7 +116,7 @@ func BuildNoticeHtmlOutputFromLicenseMetadata(
// on the license metadata files for the input `modules` defaulting to the
// current context module if none given.
func BuildNoticeXmlOutputFromLicenseMetadata(
ctx BuilderContext, outputFile WritablePath, ruleName, libraryName string,
ctx BuilderAndOtherModuleProviderContext, outputFile WritablePath, ruleName, libraryName string,
stripPrefix []string, modules ...Module) {
buildNoticeOutputFromLicenseMetadata(ctx, "xmlnotice", "xml_notice_"+ruleName,
outputFile, libraryName, stripPrefix, modules...)

View file

@ -15,8 +15,12 @@
package android
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"path/filepath"
"sort"
"strings"
"github.com/google/blueprint"
@ -55,6 +59,42 @@ type PackagingSpec struct {
// ArchType of the module which produced this packaging spec
archType ArchType
// List of module names that this packaging spec overrides
overrides *[]string
// Name of the module where this packaging spec is output of
owner string
}
func (p *PackagingSpec) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := errors.Join(encoder.Encode(p.relPathInPackage), encoder.Encode(p.srcPath),
encoder.Encode(p.symlinkTarget), encoder.Encode(p.executable),
encoder.Encode(p.effectiveLicenseFiles), encoder.Encode(p.partition),
encoder.Encode(p.skipInstall), encoder.Encode(p.aconfigPaths),
encoder.Encode(p.archType))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (p *PackagingSpec) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := errors.Join(decoder.Decode(&p.relPathInPackage), decoder.Decode(&p.srcPath),
decoder.Decode(&p.symlinkTarget), decoder.Decode(&p.executable),
decoder.Decode(&p.effectiveLicenseFiles), decoder.Decode(&p.partition),
decoder.Decode(&p.skipInstall), decoder.Decode(&p.aconfigPaths),
decoder.Decode(&p.archType))
if err != nil {
return err
}
return nil
}
func (p *PackagingSpec) Equals(other *PackagingSpec) bool {
@ -324,7 +364,10 @@ func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.Dep
}
func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter func(PackagingSpec) bool) map[string]PackagingSpec {
m := make(map[string]PackagingSpec)
// all packaging specs gathered from the dep.
var all []PackagingSpec
// list of module names overridden
var overridden []string
var arches []ArchType
for _, target := range getSupportedTargets(ctx) {
@ -345,7 +388,8 @@ func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter
if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() {
return
}
for _, ps := range child.TransitivePackagingSpecs() {
for _, ps := range OtherModuleProviderOrDefault(
ctx, child, InstallFilesProvider).TransitivePackagingSpecs.ToList() {
if !filterArch(ps) {
continue
}
@ -355,17 +399,33 @@ func (p *PackagingBase) GatherPackagingSpecsWithFilter(ctx ModuleContext, filter
continue
}
}
dstPath := ps.relPathInPackage
if existingPs, ok := m[dstPath]; ok {
if !existingPs.Equals(&ps) {
ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
}
continue
all = append(all, ps)
if ps.overrides != nil {
overridden = append(overridden, *ps.overrides...)
}
m[dstPath] = ps
}
})
// all minus packaging specs that are overridden
var filtered []PackagingSpec
for _, ps := range all {
if ps.owner != "" && InList(ps.owner, overridden) {
continue
}
filtered = append(filtered, ps)
}
m := make(map[string]PackagingSpec)
for _, ps := range filtered {
dstPath := ps.relPathInPackage
if existingPs, ok := m[dstPath]; ok {
if !existingPs.Equals(&ps) {
ctx.ModuleErrorf("packaging conflict at %v:\n%v\n%v", dstPath, existingPs, ps)
}
continue
}
m[dstPath] = ps
}
return m
}
@ -377,31 +437,59 @@ func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]Packa
// 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) {
if len(specs) == 0 {
dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec)
dirsToSpecs[dir] = specs
return p.CopySpecsToDirs(ctx, builder, dirsToSpecs)
}
// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec
// entries into corresponding directories.
func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) {
empty := true
for _, specs := range dirsToSpecs {
if len(specs) > 0 {
empty = false
break
}
}
if empty {
return entries
}
seenDir := make(map[string]bool)
preparerPath := PathForModuleOut(ctx, "preparer.sh")
cmd := builder.Command().Tool(preparerPath)
var sb strings.Builder
sb.WriteString("set -e\n")
for _, k := range SortedKeys(specs) {
ps := specs[k]
destPath := filepath.Join(dir.String(), ps.relPathInPackage)
destDir := filepath.Dir(destPath)
entries = append(entries, ps.relPathInPackage)
if _, ok := seenDir[destDir]; !ok {
seenDir[destDir] = true
sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
}
if ps.symlinkTarget == "" {
cmd.Implicit(ps.srcPath)
sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
} else {
sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
}
if ps.executable {
sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
dirs := make([]WritablePath, 0, len(dirsToSpecs))
for dir, _ := range dirsToSpecs {
dirs = append(dirs, dir)
}
sort.Slice(dirs, func(i, j int) bool {
return dirs[i].String() < dirs[j].String()
})
for _, dir := range dirs {
specs := dirsToSpecs[dir]
for _, k := range SortedKeys(specs) {
ps := specs[k]
destPath := filepath.Join(dir.String(), ps.relPathInPackage)
destDir := filepath.Dir(destPath)
entries = append(entries, ps.relPathInPackage)
if _, ok := seenDir[destDir]; !ok {
seenDir[destDir] = true
sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir))
}
if ps.symlinkTarget == "" {
cmd.Implicit(ps.srcPath)
sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath))
} else {
sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath))
}
if ps.executable {
sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath))
}
}
}

View file

@ -28,6 +28,7 @@ type componentTestModule struct {
props struct {
Deps []string
Skip_install *bool
Overrides []string
}
}
@ -117,6 +118,7 @@ func runPackagingTest(t *testing.T, config testConfig, bp string, expected []str
}
result := GroupFixturePreparers(
PrepareForTestWithDefaults,
PrepareForTestWithArchMutator,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("component", componentTestModuleFactory)
@ -650,3 +652,64 @@ func TestPrefer32Deps(t *testing.T) {
runPackagingTest(t, config, bp, tc.expected)
}
}
func TestOverrides(t *testing.T) {
bpTemplate := `
component {
name: "foo",
deps: ["bar"],
}
component {
name: "bar",
}
component {
name: "bar_override",
overrides: ["bar"],
}
component {
name: "baz",
deps: ["bar_override"],
}
package_module {
name: "package",
deps: %DEPS%,
}
`
testcases := []struct {
deps []string
expected []string
}{
{
deps: []string{"foo"},
expected: []string{"lib64/foo", "lib64/bar"},
},
{
deps: []string{"foo", "bar_override"},
expected: []string{"lib64/foo", "lib64/bar_override"},
},
{
deps: []string{"foo", "bar", "bar_override"},
expected: []string{"lib64/foo", "lib64/bar_override"},
},
{
deps: []string{"bar", "bar_override"},
expected: []string{"lib64/bar_override"},
},
{
deps: []string{"foo", "baz"},
expected: []string{"lib64/foo", "lib64/baz", "lib64/bar_override"},
},
}
for _, tc := range testcases {
config := testConfig{
multiTarget: true,
depsCollectFirstTargetOnly: false,
}
bp := strings.Replace(bpTemplate, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
runPackagingTest(t, config, bp, tc.expected)
}
}

View file

@ -15,6 +15,9 @@
package android
import (
"bytes"
"encoding/gob"
"errors"
"fmt"
"os"
"path/filepath"
@ -24,7 +27,6 @@ import (
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/pathtools"
)
@ -89,8 +91,10 @@ func GlobFiles(ctx EarlyModulePathContext, globPattern string, excludes []string
// the Path methods that rely on module dependencies having been resolved.
type ModuleWithDepsPathContext interface {
EarlyModulePathContext
OtherModuleProviderContext
VisitDirectDepsBlueprint(visit func(blueprint.Module))
OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
HasMutatorFinished(mutatorName string) bool
}
// ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
@ -234,6 +238,9 @@ type Path interface {
// directory, and OutputPath.Join("foo").Rel() would return "foo".
Rel() string
// WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base().
WithoutRel() Path
// RelativeToTop returns a new path relative to the top, it is provided solely for use in tests.
//
// It is guaranteed to always return the same type as it is called on, e.g. if called on an
@ -242,13 +249,13 @@ type Path interface {
// A standard build has the following structure:
// ../top/
// out/ - make install files go here.
// out/soong - this is the soongOutDir passed to NewTestConfig()
// out/soong - this is the outDir passed to NewTestConfig()
// ... - the source files
//
// This function converts a path so that it appears relative to the ../top/ directory, i.e.
// * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
// * Make install paths, which have the pattern "outDir/../<path>" are converted into the top
// relative path "out/<path>"
// * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
// * Soong install paths and other writable paths, which have the pattern "outDir/soong/<path>" are
// converted into the top relative path "out/soong/<path>".
// * Source paths are already relative to the top.
// * Phony paths are not relative to anything.
@ -258,8 +265,9 @@ type Path interface {
}
const (
OutDir = "out"
OutSoongDir = OutDir + "/soong"
testOutDir = "out"
testOutSoongSubDir = "/soong"
TestOutSoongDir = testOutDir + testOutSoongSubDir
)
// WritablePath is a type of path that can be used as an output for build rules.
@ -460,8 +468,8 @@ func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths {
// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
// source directory.
// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
// filepath.
// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
// source filepath.
//
// Properties passed as the paths argument must have been annotated with struct tag
// `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
@ -488,8 +496,8 @@ type SourceInput struct {
// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
// source directory. Not valid in excludes.
// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
// filepath.
// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
// source filepath.
//
// excluding the items (similarly resolved
// Properties passed as the paths argument must have been annotated with struct tag
@ -547,13 +555,6 @@ func (p OutputPaths) Strings() []string {
return ret
}
// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin")
rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
return goBinaryInstallDir.Join(ctx, rel)
}
// Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
// If the dependency is not found, a missingErrorDependency is returned.
// If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
@ -565,10 +566,6 @@ func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag
if aModule, ok := module.(Module); ok && !aModule.Enabled(ctx) {
return nil, missingDependencyError{[]string{moduleName}}
}
if goBinary, ok := module.(bootstrap.GoBinaryTool); ok && tag == "" {
goBinaryPath := PathForGoBinary(ctx, goBinary)
return Paths{goBinaryPath}, nil
}
outputFiles, err := outputFilesForModule(ctx, module, tag)
if outputFiles != nil && err == nil {
return outputFiles, nil
@ -617,8 +614,8 @@ func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string)
// - glob, relative to the local module directory, resolves as filepath(s), relative to the local
// source directory. Not valid in excludes.
// - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer
// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source
// filepath.
// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated
// source filepath.
//
// and a list of the module names of missing module dependencies are returned as the second return.
// Properties passed as the paths argument must have been annotated with struct tag
@ -1068,6 +1065,28 @@ type basePath struct {
rel string
}
func (p basePath) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := errors.Join(encoder.Encode(p.path), encoder.Encode(p.rel))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (p *basePath) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := errors.Join(decoder.Decode(&p.path), decoder.Decode(&p.rel))
if err != nil {
return err
}
return nil
}
func (p basePath) Ext() string {
return filepath.Ext(p.path)
}
@ -1093,8 +1112,8 @@ func (p basePath) withRel(rel string) basePath {
return p
}
func (p basePath) RelativeToTop() Path {
ensureTestOnly()
func (p basePath) withoutRel() basePath {
p.rel = filepath.Base(p.path)
return p
}
@ -1110,6 +1129,11 @@ func (p SourcePath) withRel(rel string) SourcePath {
return p
}
func (p SourcePath) RelativeToTop() Path {
ensureTestOnly()
return p
}
// safePathForSource is for paths that we expect are safe -- only for use by go
// code that is embedding ninja variables in paths
func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
@ -1243,11 +1267,13 @@ func PathForSourceRelaxed(ctx PathContext, pathComponents ...string) SourcePath
// PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput,
// the path is relative to the root of the output folder, not the out/soong folder.
func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path {
p, err := validatePath(pathComponents...)
path, err := validatePath(pathComponents...)
if err != nil {
reportPathError(ctx, err)
}
return basePath{path: filepath.Join(ctx.Config().OutDir(), p)}
fullPath := filepath.Join(ctx.Config().OutDir(), path)
path = fullPath[len(fullPath)-len(path):]
return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath}
}
// MaybeExistentPathForSource joins the provided path components and validates that the result
@ -1300,6 +1326,11 @@ func (p SourcePath) String() string {
return p.path
}
func (p SourcePath) WithoutRel() Path {
p.basePath = p.basePath.withoutRel()
return p
}
// Join creates a new SourcePath with paths... joined with the current path. The
// provided paths... may not use '..' to escape from the current path.
func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath {
@ -1350,25 +1381,47 @@ func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) Opt
type OutputPath struct {
basePath
// The soong build directory, i.e. Config.SoongOutDir()
soongOutDir string
// The base out directory for this path, either Config.SoongOutDir() or Config.OutDir()
outDir string
fullPath string
}
func (p OutputPath) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (p *OutputPath) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath))
if err != nil {
return err
}
return nil
}
func (p OutputPath) withRel(rel string) OutputPath {
p.basePath = p.basePath.withRel(rel)
p.fullPath = filepath.Join(p.fullPath, rel)
return p
}
func (p OutputPath) WithoutRel() OutputPath {
p.basePath.rel = filepath.Base(p.basePath.path)
func (p OutputPath) WithoutRel() Path {
p.basePath = p.basePath.withoutRel()
return p
}
func (p OutputPath) getSoongOutDir() string {
return p.soongOutDir
return p.outDir
}
func (p OutputPath) RelativeToTop() Path {
@ -1376,8 +1429,13 @@ func (p OutputPath) RelativeToTop() Path {
}
func (p OutputPath) outputPathRelativeToTop() OutputPath {
p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
p.soongOutDir = OutSoongDir
p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath)
if strings.HasSuffix(p.outDir, testOutSoongSubDir) {
p.outDir = TestOutSoongDir
} else {
// Handle the PathForArbitraryOutput case
p.outDir = testOutDir
}
return p
}
@ -1394,6 +1452,11 @@ type toolDepPath struct {
basePath
}
func (t toolDepPath) WithoutRel() Path {
t.basePath = t.basePath.withoutRel()
return t
}
func (t toolDepPath) RelativeToTop() Path {
ensureTestOnly()
return t
@ -1423,7 +1486,7 @@ func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath {
return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
}
// PathsForOutput returns Paths rooted from soongOutDir
// PathsForOutput returns Paths rooted from outDir
func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
ret := make(WritablePaths, len(paths))
for i, path := range paths {
@ -1607,11 +1670,10 @@ type ModuleOutPathContext interface {
ModuleName() string
ModuleDir() string
ModuleSubDir() string
SoongConfigTraceHash() string
}
func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), ctx.SoongConfigTraceHash())
return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
}
// PathForModuleOut returns a Path representing the paths... under the module's
@ -1744,6 +1806,32 @@ type InstallPath struct {
fullPath string
}
func (p *InstallPath) GobEncode() ([]byte, error) {
w := new(bytes.Buffer)
encoder := gob.NewEncoder(w)
err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir),
encoder.Encode(p.partitionDir), encoder.Encode(p.partition),
encoder.Encode(p.makePath), encoder.Encode(p.fullPath))
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
func (p *InstallPath) GobDecode(data []byte) error {
r := bytes.NewBuffer(data)
decoder := gob.NewDecoder(r)
err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir),
decoder.Decode(&p.partitionDir), decoder.Decode(&p.partition),
decoder.Decode(&p.makePath), decoder.Decode(&p.fullPath))
if err != nil {
return err
}
return nil
}
// Will panic if called from outside a test environment.
func ensureTestOnly() {
if PrefixInList(os.Args, "-test.") {
@ -1755,14 +1843,19 @@ func ensureTestOnly() {
func (p InstallPath) RelativeToTop() Path {
ensureTestOnly()
if p.makePath {
p.soongOutDir = OutDir
p.soongOutDir = testOutDir
} else {
p.soongOutDir = OutSoongDir
p.soongOutDir = TestOutSoongDir
}
p.fullPath = filepath.Join(p.soongOutDir, p.path)
return p
}
func (p InstallPath) WithoutRel() Path {
p.basePath = p.basePath.withoutRel()
return p
}
func (p InstallPath) getSoongOutDir() string {
return p.soongOutDir
}
@ -2083,6 +2176,11 @@ func (p PhonyPath) RelativeToTop() Path {
return p
}
func (p PhonyPath) WithoutRel() Path {
p.basePath = p.basePath.withoutRel()
return p
}
func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
panic("Not implemented")
}
@ -2099,6 +2197,11 @@ func (p testPath) RelativeToTop() Path {
return p
}
func (p testPath) WithoutRel() Path {
p.basePath = p.basePath.withoutRel()
return p
}
func (p testPath) String() string {
return p.path
}

View file

@ -1183,9 +1183,6 @@ type pathForModuleSrcOutputFileProviderModule struct {
Outs []string
Tagged []string
}
outs Paths
tagged Paths
}
func pathForModuleSrcOutputFileProviderModuleFactory() Module {
@ -1196,24 +1193,17 @@ func pathForModuleSrcOutputFileProviderModuleFactory() Module {
}
func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
var outs, taggedOuts Paths
for _, out := range p.props.Outs {
p.outs = append(p.outs, PathForModuleOut(ctx, out))
outs = append(outs, PathForModuleOut(ctx, out))
}
for _, tagged := range p.props.Tagged {
p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged))
taggedOuts = append(taggedOuts, PathForModuleOut(ctx, tagged))
}
}
func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) {
switch tag {
case "":
return p.outs, nil
case ".tagged":
return p.tagged, nil
default:
return nil, fmt.Errorf("unsupported tag %q", tag)
}
ctx.SetOutputFiles(outs, "")
ctx.SetOutputFiles(taggedOuts, ".tagged")
}
type pathForModuleSrcTestCase struct {

View file

@ -26,14 +26,20 @@ type phonyMap map[string]Paths
var phonyMapLock sync.Mutex
func getPhonyMap(config Config) phonyMap {
type ModulePhonyInfo struct {
Phonies map[string]Paths
}
var ModulePhonyProvider = blueprint.NewProvider[ModulePhonyInfo]()
func getSingletonPhonyMap(config Config) phonyMap {
return config.Once(phonyMapOnceKey, func() interface{} {
return make(phonyMap)
}).(phonyMap)
}
func addPhony(config Config, name string, deps ...Path) {
phonyMap := getPhonyMap(config)
func addSingletonPhony(config Config, name string, deps ...Path) {
phonyMap := getSingletonPhonyMap(config)
phonyMapLock.Lock()
defer phonyMapLock.Unlock()
phonyMap[name] = append(phonyMap[name], deps...)
@ -47,7 +53,15 @@ type phonySingleton struct {
var _ SingletonMakeVarsProvider = (*phonySingleton)(nil)
func (p *phonySingleton) GenerateBuildActions(ctx SingletonContext) {
p.phonyMap = getPhonyMap(ctx.Config())
p.phonyMap = getSingletonPhonyMap(ctx.Config())
ctx.VisitAllModules(func(m Module) {
if info, ok := OtherModuleProvider(ctx, m, ModulePhonyProvider); ok {
for k, v := range info.Phonies {
p.phonyMap[k] = append(p.phonyMap[k], v...)
}
}
})
p.phonyList = SortedKeys(p.phonyMap)
for _, phony := range p.phonyList {
p.phonyMap[phony] = SortedUniquePaths(p.phonyMap[phony])

View file

@ -61,7 +61,7 @@ var _ ExcludeFromApexContentsTag = PrebuiltDepTag
type UserSuppliedPrebuiltProperties struct {
// When prefer is set to true the prebuilt will be used instead of any source module with
// a matching name.
Prefer *bool `android:"arch_variant"`
Prefer proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
// When specified this names a Soong config variable that controls the prefer property.
//
@ -148,11 +148,7 @@ func PrebuiltNameFromSource(name string) string {
}
func (p *Prebuilt) ForcePrefer() {
p.properties.Prefer = proptools.BoolPtr(true)
}
func (p *Prebuilt) Prefer() bool {
return proptools.Bool(p.properties.Prefer)
p.properties.Prefer = NewSimpleConfigurable(true)
}
// SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the
@ -248,6 +244,8 @@ func InitPrebuiltModuleWithSrcSupplier(module PrebuiltInterface, srcsSupplier Pr
p.srcsPropertyName = srcsPropertyName
}
// InitPrebuiltModule is the same as InitPrebuiltModuleWithSrcSupplier, but uses the
// provided list of strings property as the source provider.
func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
if srcs == nil {
panic(fmt.Errorf("srcs must not be nil"))
@ -260,6 +258,20 @@ func InitPrebuiltModule(module PrebuiltInterface, srcs *[]string) {
InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
}
// InitConfigurablePrebuiltModule is the same as InitPrebuiltModule, but uses a
// Configurable list of strings property instead of a regular list of strings.
func InitConfigurablePrebuiltModule(module PrebuiltInterface, srcs *proptools.Configurable[[]string]) {
if srcs == nil {
panic(fmt.Errorf("srcs must not be nil"))
}
srcsSupplier := func(ctx BaseModuleContext, _ Module) []string {
return srcs.GetOrDefault(ctx, nil)
}
InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "srcs")
}
func InitSingleSourcePrebuiltModule(module PrebuiltInterface, srcProps interface{}, srcField string) {
srcPropsValue := reflect.ValueOf(srcProps).Elem()
srcStructField, _ := srcPropsValue.Type().FieldByName(srcField)
@ -738,7 +750,7 @@ func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt M
}
// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
return Bool(p.properties.Prefer)
return p.properties.Prefer.GetOrDefault(ctx, false)
}
func (p *Prebuilt) SourceExists() bool {

View file

@ -15,7 +15,6 @@
package android
import (
"fmt"
"testing"
"github.com/google/blueprint"
@ -494,7 +493,6 @@ type prebuiltModule struct {
properties struct {
Srcs []string `android:"path,arch_variant"`
}
src Path
}
func newPrebuiltModule() Module {
@ -510,24 +508,17 @@ func (p *prebuiltModule) Name() string {
}
func (p *prebuiltModule) GenerateAndroidBuildActions(ctx ModuleContext) {
var src Path
if len(p.properties.Srcs) >= 1 {
p.src = p.prebuilt.SingleSourcePath(ctx)
src = p.prebuilt.SingleSourcePath(ctx)
}
ctx.SetOutputFiles(Paths{src}, "")
}
func (p *prebuiltModule) Prebuilt() *Prebuilt {
return &p.prebuilt
}
func (p *prebuiltModule) OutputFiles(tag string) (Paths, error) {
switch tag {
case "":
return Paths{p.src}, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
type sourceModuleProperties struct {
Deps []string `android:"path,arch_variant"`
}
@ -583,11 +574,7 @@ func newOverrideSourceModule() Module {
func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) {
selectMainlineModuleContritbutions := GroupFixturePreparers(
FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions",
}
}),
PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_apex_contributions"),
)
testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, `
source {

View file

@ -14,7 +14,9 @@
package android
import "github.com/google/blueprint/proptools"
import (
"github.com/google/blueprint/proptools"
)
func init() {
ctx := InitRegistrationContext
@ -37,8 +39,10 @@ func (p *productConfigModule) GenerateAndroidBuildActions(ctx ModuleContext) {
if targetProduct != "" {
targetProduct += "."
}
soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"variables")
extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+"extra.variables")
coverageSuffix := ctx.Config().CoverageSuffix()
soongVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"variables")
extraVariablesPath := PathForOutput(ctx, "soong."+targetProduct+coverageSuffix+"extra.variables")
rule := NewRuleBuilder(pctx, ctx)
rule.Command().BuiltTool("merge_json").

View file

@ -0,0 +1,35 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
func init() {
ctx := InitRegistrationContext
ctx.RegisterParallelSingletonType("product_config_to_bp_singleton", productConfigToBpSingletonFactory)
}
type productConfigToBpSingleton struct{}
func (s *productConfigToBpSingleton) GenerateBuildActions(ctx SingletonContext) {
// TODO: update content from make-based product config
var content string
generatedBp := PathForOutput(ctx, "soong_generated_product_config.bp")
WriteFileRule(ctx, generatedBp, content)
ctx.Phony("product_config_to_bp", generatedBp)
}
// productConfigToBpSingleton generates a bp file from make-based product config
func productConfigToBpSingletonFactory() Singleton {
return &productConfigToBpSingleton{}
}

View file

@ -14,6 +14,8 @@ var _ OtherModuleProviderContext = BaseModuleContext(nil)
var _ OtherModuleProviderContext = ModuleContext(nil)
var _ OtherModuleProviderContext = BottomUpMutatorContext(nil)
var _ OtherModuleProviderContext = TopDownMutatorContext(nil)
var _ OtherModuleProviderContext = SingletonContext(nil)
var _ OtherModuleProviderContext = (*TestContext)(nil)
// OtherModuleProvider reads the provider for the given module. If the provider has been set the value is
// returned and the boolean is true. If it has not been set the zero value of the provider's type is returned
@ -30,6 +32,11 @@ func OtherModuleProvider[K any](ctx OtherModuleProviderContext, module blueprint
return value.(K), ok
}
func OtherModuleProviderOrDefault[K any](ctx OtherModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) K {
value, _ := OtherModuleProvider(ctx, module, provider)
return value
}
// ModuleProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext for use in ModuleProvider.
type ModuleProviderContext interface {
@ -56,26 +63,6 @@ func ModuleProvider[K any](ctx ModuleProviderContext, provider blueprint.Provide
return value.(K), ok
}
type SingletonModuleProviderContext interface {
moduleProvider(blueprint.Module, blueprint.AnyProviderKey) (any, bool)
}
var _ SingletonModuleProviderContext = SingletonContext(nil)
var _ SingletonModuleProviderContext = (*TestContext)(nil)
// SingletonModuleProvider wraps blueprint.SingletonModuleProvider to provide a type-safe method to retrieve the value
// of the given provider from a module using a SingletonContext. If the provider has not been set the first return
// value will be the zero value of the provider's type, and the second return value will be false. If the provider has
// been set the second return value will be true.
func SingletonModuleProvider[K any](ctx SingletonModuleProviderContext, module blueprint.Module, provider blueprint.ProviderKey[K]) (K, bool) {
value, ok := ctx.moduleProvider(module, provider)
if !ok {
var k K
return k, false
}
return value.(K), ok
}
// SetProviderContext is a helper interface that is a subset of ModuleContext, BottomUpMutatorContext, or
// TopDownMutatorContext for use in SetProvider.
type SetProviderContext interface {

View file

@ -58,6 +58,7 @@ type RuleBuilder struct {
sboxInputs bool
sboxManifestPath WritablePath
missingDeps []string
args map[string]string
}
// NewRuleBuilder returns a newly created RuleBuilder.
@ -78,6 +79,17 @@ func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder {
return rb
}
// Set the phony_output argument.
// This causes the output files to be ignored.
// If the output isn't created, it's not treated as an error.
// The build rule is run every time whether or not the output is created.
func (rb *RuleBuilder) SetPhonyOutput() {
if rb.args == nil {
rb.args = make(map[string]string)
}
rb.args["phony_output"] = "true"
}
// RuleBuilderInstall is a tuple of install from and to locations.
type RuleBuilderInstall struct {
From Path
@ -451,6 +463,8 @@ func (r *RuleBuilder) Build(name string, desc string) {
r.build(name, desc, true)
}
var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables")
func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
name = ninjaNameEscape(name)
@ -542,6 +556,12 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b
To: proto.String(r.sboxPathForInputRel(input)),
})
}
for _, input := range r.OrderOnlys() {
command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
From: proto.String(input.String()),
To: proto.String(r.sboxPathForInputRel(input)),
})
}
// If using rsp files copy them and their contents into the sbox directory with
// the appropriate path mappings.
@ -562,6 +582,44 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b
})
}
// Only allow the build to access certain environment variables
command.DontInheritEnv = proto.Bool(true)
command.Env = r.ctx.Config().Once(sandboxEnvOnceKey, func() interface{} {
// The list of allowed variables was found by running builds of all
// genrules and seeing what failed
var result []*sbox_proto.EnvironmentVariable
inheritedVars := []string{
"PATH",
"JAVA_HOME",
"TMPDIR",
// Allow RBE variables because the art tests invoke RBE manually
"RBE_log_dir",
"RBE_platform",
"RBE_server_address",
// TODO: RBE_exec_root is set to the absolute path to the root of the source
// tree, which we don't want sandboxed actions to find. Remap it to ".".
"RBE_exec_root",
}
for _, v := range inheritedVars {
result = append(result, &sbox_proto.EnvironmentVariable{
Name: proto.String(v),
State: &sbox_proto.EnvironmentVariable_Inherit{
Inherit: true,
},
})
}
// Set OUT_DIR to the relative path of the sandboxed out directory.
// Otherwise, OUT_DIR will be inherited from the rest of the build,
// which will allow scripts to escape the sandbox if OUT_DIR is an
// absolute path.
result = append(result, &sbox_proto.EnvironmentVariable{
Name: proto.String("OUT_DIR"),
State: &sbox_proto.EnvironmentVariable_Value{
Value: sboxOutSubDir,
},
})
return result
}).([]*sbox_proto.EnvironmentVariable)
command.Chdir = proto.Bool(true)
}
@ -726,6 +784,12 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b
commandString = proptools.NinjaEscape(commandString)
}
args_vars := make([]string, len(r.args))
i := 0
for k, _ := range r.args {
args_vars[i] = k
i++
}
r.ctx.Build(r.pctx, BuildParams{
Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
Command: commandString,
@ -734,7 +798,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b
Rspfile: proptools.NinjaEscape(rspFile),
RspfileContent: rspFileContent,
Pool: pool,
}),
}, args_vars...),
Inputs: rspFileInputs,
Implicits: inputs,
OrderOnly: r.OrderOnlys(),
@ -744,6 +808,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b
Depfile: depFile,
Deps: depFormat,
Description: desc,
Args: r.args,
})
}

111
android/sbom.go Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"io"
"path/filepath"
"strings"
"github.com/google/blueprint"
)
var (
// Command line tool to generate SBOM in Soong
genSbom = pctx.HostBinToolVariable("genSbom", "gen_sbom")
// Command to generate SBOM in Soong.
genSbomRule = pctx.AndroidStaticRule("genSbomRule", blueprint.RuleParams{
Command: "rm -rf $out && ${genSbom} --output_file ${out} --metadata ${in} --product_out ${productOut} --soong_out ${soongOut} --build_version \"$$(cat ${buildFingerprintFile})\" --product_mfr \"${productManufacturer}\" --json",
CommandDeps: []string{"${genSbom}"},
}, "productOut", "soongOut", "buildFingerprintFile", "productManufacturer")
)
func init() {
RegisterSbomSingleton(InitRegistrationContext)
}
func RegisterSbomSingleton(ctx RegistrationContext) {
ctx.RegisterParallelSingletonType("sbom_singleton", sbomSingletonFactory)
}
// sbomSingleton is used to generate build actions of generating SBOM of products.
type sbomSingleton struct {
sbomFile OutputPath
}
func sbomSingletonFactory() Singleton {
return &sbomSingleton{}
}
// Generates SBOM of products
func (this *sbomSingleton) GenerateBuildActions(ctx SingletonContext) {
if !ctx.Config().HasDeviceProduct() {
return
}
// Get all METADATA files and add them as implicit input
metadataFileListFile := PathForArbitraryOutput(ctx, ".module_paths", "METADATA.list")
f, err := ctx.Config().fs.Open(metadataFileListFile.String())
if err != nil {
panic(err)
}
b, err := io.ReadAll(f)
if err != nil {
panic(err)
}
allMetadataFiles := strings.Split(string(b), "\n")
implicits := []Path{metadataFileListFile}
for _, path := range allMetadataFiles {
implicits = append(implicits, PathForSource(ctx, path))
}
prodVars := ctx.Config().productVariables
buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt")
implicits = append(implicits, buildFingerprintFile)
// Add installed_files.stamp as implicit input, which depends on all installed files of the product.
installedFilesStamp := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp")
implicits = append(implicits, installedFilesStamp)
metadataDb := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db")
this.sbomFile = PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json")
ctx.Build(pctx, BuildParams{
Rule: genSbomRule,
Input: metadataDb,
Implicits: implicits,
Output: this.sbomFile,
Args: map[string]string{
"productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", String(prodVars.DeviceName)),
"soongOut": ctx.Config().soongOutDir,
"buildFingerprintFile": buildFingerprintFile.String(),
"productManufacturer": ctx.Config().ProductVariables().ProductManufacturer,
},
})
if !ctx.Config().UnbundledBuildApps() {
// When building SBOM of products, phony rule "sbom" is for generating product SBOM in Soong.
ctx.Build(pctx, BuildParams{
Rule: blueprint.Phony,
Inputs: []Path{this.sbomFile},
Output: PathForPhony(ctx, "sbom"),
})
}
}
func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) {
// When building SBOM of products
if !ctx.Config().UnbundledBuildApps() {
ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json")
}
}

View file

@ -513,6 +513,9 @@ type SdkMemberType interface {
// SupportedLinkages returns the names of the linkage variants supported by this module.
SupportedLinkages() []string
// DisablesStrip returns true if the stripping needs to be disabled for this module.
DisablesStrip() bool
// ArePrebuiltsRequired returns true if prebuilts are required in the sdk snapshot, false
// otherwise.
ArePrebuiltsRequired() bool
@ -618,6 +621,9 @@ type SdkMemberTypeBase struct {
// The names of linkage variants supported by this module.
SupportedLinkageNames []string
// StripDisabled returns true if the stripping needs to be disabled for this module.
StripDisabled bool
// When set to true BpPropertyNotRequired indicates that the member type does not require the
// property to be specifiable in an Android.bp file.
BpPropertyNotRequired bool
@ -689,6 +695,10 @@ func (b *SdkMemberTypeBase) SupportedLinkages() []string {
return b.SupportedLinkageNames
}
func (b *SdkMemberTypeBase) DisablesStrip() bool {
return b.StripDisabled
}
// registeredModuleExportsMemberTypes is the set of registered SdkMemberTypes for module_exports
// modules.
var registeredModuleExportsMemberTypes = &sdkRegistry{}
@ -803,8 +813,6 @@ type SdkMemberProperties interface {
// SdkMemberContext provides access to information common to a specific member.
type SdkMemberContext interface {
ConfigAndErrorContext
// SdkModuleContext returns the module context of the sdk common os variant which is creating the
// snapshot.
//

View file

@ -93,6 +93,15 @@ func (k SdkKind) String() string {
}
}
func ToSdkKind(s string) SdkKind {
for kind := SdkNone; kind <= SdkPrivate; kind++ {
if s == kind.String() {
return kind
}
}
return SdkInvalid
}
func (k SdkKind) DefaultJavaLibraryName() string {
switch k {
case SdkPublic:

View file

@ -35,7 +35,7 @@ type SingletonContext interface {
// Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context.
ModuleVariantsFromName(referer Module, name string) []Module
moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool)
ModuleErrorf(module blueprint.Module, format string, args ...interface{})
Errorf(format string, args ...interface{})
@ -90,6 +90,10 @@ type SingletonContext interface {
// OtherModulePropertyErrorf reports an error on the line number of the given property of the given module
OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{})
// HasMutatorFinished returns true if the given mutator has finished running.
// It will panic if given an invalid mutator name.
HasMutatorFinished(mutatorName string) bool
}
type singletonAdaptor struct {
@ -177,7 +181,7 @@ func (s *singletonContextAdaptor) Build(pctx PackageContext, params BuildParams)
}
func (s *singletonContextAdaptor) Phony(name string, deps ...Path) {
addPhony(s.Config(), name, deps...)
addSingletonPhony(s.Config(), name, deps...)
}
func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
@ -279,10 +283,14 @@ func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name st
return result
}
func (s *singletonContextAdaptor) moduleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
func (s *singletonContextAdaptor) otherModuleProvider(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return s.SingletonContext.ModuleProvider(module, provider)
}
func (s *singletonContextAdaptor) OtherModulePropertyErrorf(module Module, property string, format string, args ...interface{}) {
s.blueprintSingletonContext().OtherModulePropertyErrorf(module, property, format, args...)
}
func (s *singletonContextAdaptor) HasMutatorFinished(mutatorName string) bool {
return s.blueprintSingletonContext().HasMutatorFinished(mutatorName)
}

View file

@ -68,7 +68,7 @@ type SingletonModuleBase struct {
func (smb *SingletonModuleBase) GenerateBuildActions(ctx blueprint.ModuleContext) {
smb.lock.Lock()
if smb.variant != "" {
ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
ctx.ModuleErrorf("GenerateAndroidBuildActions already called for variant %q, SingletonModules can only have one variant", smb.variant)
}
smb.variant = ctx.ModuleSubDir()
smb.lock.Unlock()

View file

@ -96,12 +96,6 @@ func TestUnusedSingletonModule(t *testing.T) {
}
}
func testVariantSingletonModuleMutator(ctx BottomUpMutatorContext) {
if _, ok := ctx.Module().(*testSingletonModule); ok {
ctx.CreateVariations("a", "b")
}
}
func TestVariantSingletonModule(t *testing.T) {
if testing.Short() {
t.Skip("test fails with data race enabled")
@ -116,7 +110,11 @@ func TestVariantSingletonModule(t *testing.T) {
prepareForSingletonModuleTest,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("test_singleton_module_mutator", testVariantSingletonModuleMutator)
ctx.Transition("test_singleton_module_mutator", &testTransitionMutator{
split: func(ctx BaseModuleContext) []string {
return []string{"a", "b"}
},
})
})
}),
).

View file

@ -463,57 +463,6 @@ func loadSoongConfigModuleTypeDefinition(ctx LoadHookContext, from string) map[s
}).(map[string]blueprint.ModuleFactory)
}
// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig.
type tracingConfig struct {
config soongconfig.SoongConfig
boolSet map[string]bool
stringSet map[string]string
isSetSet map[string]bool
}
func (c *tracingConfig) Bool(name string) bool {
c.boolSet[name] = c.config.Bool(name)
return c.boolSet[name]
}
func (c *tracingConfig) String(name string) string {
c.stringSet[name] = c.config.String(name)
return c.stringSet[name]
}
func (c *tracingConfig) IsSet(name string) bool {
c.isSetSet[name] = c.config.IsSet(name)
return c.isSetSet[name]
}
func (c *tracingConfig) getTrace() soongConfigTrace {
ret := soongConfigTrace{}
for k, v := range c.boolSet {
ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v))
}
for k, v := range c.stringSet {
ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v))
}
for k, v := range c.isSetSet {
ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v))
}
return ret
}
func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig {
c := tracingConfig{
config: config,
boolSet: make(map[string]bool),
stringSet: make(map[string]string),
isSetSet: make(map[string]bool),
}
return &c
}
var _ soongconfig.SoongConfig = (*tracingConfig)(nil)
// configModuleFactory takes an existing soongConfigModuleFactory and a
// ModuleType to create a new ModuleFactory that uses a custom loadhook.
func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType) blueprint.ModuleFactory {
@ -561,8 +510,8 @@ func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfi
// conditional on Soong config variables by reading the product
// config variables from Make.
AddLoadHook(module, func(ctx LoadHookContext) {
tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace))
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig)
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
if err != nil {
ctx.ModuleErrorf("%s", err)
return
@ -570,8 +519,6 @@ func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfi
for _, ps := range newProps {
ctx.AppendProperties(ps)
}
module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace()
})
return module, props
}

View file

@ -16,7 +16,6 @@ package android
import (
"fmt"
"path/filepath"
"testing"
)
@ -506,197 +505,3 @@ func TestSoongConfigModuleSingletonModule(t *testing.T) {
})
}
}
func TestSoongConfigModuleTrace(t *testing.T) {
bp := `
soong_config_module_type {
name: "acme_test",
module_type: "test",
config_namespace: "acme",
variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
bool_variables: ["feature2", "unused_feature", "always_true"],
value_variables: ["size", "unused_size"],
properties: ["cflags", "srcs", "defaults"],
}
soong_config_module_type {
name: "acme_test_defaults",
module_type: "test_defaults",
config_namespace: "acme",
variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
bool_variables: ["feature2", "unused_feature", "always_true"],
value_variables: ["size", "unused_size"],
properties: ["cflags", "srcs", "defaults"],
}
soong_config_string_variable {
name: "board",
values: ["soc_a", "soc_b", "soc_c"],
}
soong_config_string_variable {
name: "unused_string_var",
values: ["a", "b"],
}
soong_config_bool_variable {
name: "feature1",
}
soong_config_bool_variable {
name: "FEATURE3",
}
test_defaults {
name: "test_defaults",
cflags: ["DEFAULT"],
}
test {
name: "normal",
defaults: ["test_defaults"],
}
acme_test {
name: "board_1",
defaults: ["test_defaults"],
soong_config_variables: {
board: {
soc_a: {
cflags: ["-DSOC_A"],
},
},
},
}
acme_test {
name: "board_2",
defaults: ["test_defaults"],
soong_config_variables: {
board: {
soc_a: {
cflags: ["-DSOC_A"],
},
},
},
}
acme_test {
name: "size",
defaults: ["test_defaults"],
soong_config_variables: {
size: {
cflags: ["-DSIZE=%s"],
},
},
}
acme_test {
name: "board_and_size",
defaults: ["test_defaults"],
soong_config_variables: {
board: {
soc_a: {
cflags: ["-DSOC_A"],
},
},
size: {
cflags: ["-DSIZE=%s"],
},
},
}
acme_test_defaults {
name: "board_defaults",
soong_config_variables: {
board: {
soc_a: {
cflags: ["-DSOC_A"],
},
},
},
}
acme_test_defaults {
name: "size_defaults",
soong_config_variables: {
size: {
cflags: ["-DSIZE=%s"],
},
},
}
test {
name: "board_and_size_with_defaults",
defaults: ["board_defaults", "size_defaults"],
}
`
fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.VendorVars = vars
})
}
preparer := fixtureForVendorVars(map[string]map[string]string{
"acme": {
"board": "soc_a",
"size": "42",
"feature1": "true",
"feature2": "false",
// FEATURE3 unset
"unused_feature": "true", // unused
"unused_size": "1", // unused
"unused_string_var": "a", // unused
"always_true": "true",
},
})
t.Run("soong config trace hash", func(t *testing.T) {
result := GroupFixturePreparers(
preparer,
PrepareForTestWithDefaults,
PrepareForTestWithSoongConfigModuleBuildComponents,
prepareForSoongConfigTestModule,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.FinalDepsMutators(registerSoongConfigTraceMutator)
}),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
// Hashes of modules not using soong config should be empty
normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule)
AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "")
AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test")
board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule)
board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule)
size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule)
// Trace mutator sets soong config trace hash correctly
board1Hash := board1.base().commonProperties.SoongConfigTrace.hash()
board1Output := board1.outputPath.RelativeToTop().String()
AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash)
AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test"))
sizeHash := size.base().commonProperties.SoongConfigTrace.hash()
sizeOutput := size.outputPath.RelativeToTop().String()
AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash)
AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test"))
// Trace should be identical for modules using the same set of variables
AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace)
AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash)
// Trace hash should be different for different sets of soong variables
AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false)
boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule)
boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module()
// Trace should propagate
AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash)
AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace)
AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash)
})
}

View file

@ -9,7 +9,6 @@ bootstrap_go_package {
"blueprint",
"blueprint-parser",
"blueprint-proptools",
"soong-bazel",
"soong-starlark-format",
],
srcs: [

View file

@ -40,4 +40,8 @@ python_library_host {
proto: {
canonical_path_from_root: false,
},
visibility: [
"//build/soong:__subpackages__",
"//tools/asuite/team_build_scripts",
],
}

View file

@ -47,7 +47,8 @@ func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) {
files[testSuite] = make(map[string]InstallPaths)
}
name := ctx.ModuleName(m)
files[testSuite][name] = append(files[testSuite][name], tsm.FilesToInstall()...)
files[testSuite][name] = append(files[testSuite][name],
OtherModuleProviderOrDefault(ctx, tsm, InstallFilesProvider).InstallFiles...)
}
}
})

View file

@ -126,6 +126,10 @@ var PrepareForTestWithMakevars = FixtureRegisterWithContext(func(ctx Registratio
ctx.RegisterSingletonType("makevars", makeVarsSingletonFunc)
})
var PrepareForTestVintfFragmentModules = FixtureRegisterWithContext(func(ctx RegistrationContext) {
registerVintfFragmentComponents(ctx)
})
// Test fixture preparer that will register most java build components.
//
// Singletons and mutators should only be added here if they are needed for a majority of java
@ -149,6 +153,7 @@ var PrepareForTestWithAndroidBuildComponents = GroupFixturePreparers(
PrepareForTestWithPackageModule,
PrepareForTestWithPrebuilts,
PrepareForTestWithVisibility,
PrepareForTestVintfFragmentModules,
)
// Prepares an integration test with all build components from the android package.
@ -174,6 +179,16 @@ var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Con
config.TestAllowNonExistentPaths = false
})
// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value.
func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer {
return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
if variables.BuildFlags == nil {
variables.BuildFlags = make(map[string]string)
}
variables.BuildFlags[flag] = value
})
}
func NewTestArchContext(config Config) *TestContext {
ctx := NewTestContext(config)
ctx.preDeps = append(ctx.preDeps, registerArchMutator)
@ -202,7 +217,7 @@ func (ctx *TestContext) HardCodedPreArchMutators(f RegisterMutatorFunc) {
ctx.PreArchMutators(f)
}
func (ctx *TestContext) moduleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
func (ctx *TestContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
return ctx.Context.ModuleProvider(m, p)
}
@ -220,7 +235,7 @@ func (ctx *TestContext) FinalDepsMutators(f RegisterMutatorFunc) {
func (ctx *TestContext) OtherModuleProviderAdaptor() OtherModuleProviderContext {
return NewOtherModuleProviderAdaptor(func(module blueprint.Module, provider blueprint.AnyProviderKey) (any, bool) {
return ctx.moduleProvider(module, provider)
return ctx.otherModuleProvider(module, provider)
})
}
@ -822,15 +837,15 @@ func newBaseTestingComponent(config Config, provider testBuildProvider) baseTest
// containing at most one instance of the temporary build directory at the start of the path while
// this assumes that there can be any number at any position.
func normalizeStringRelativeToTop(config Config, s string) string {
// The soongOutDir usually looks something like: /tmp/testFoo2345/001
// The outDir usually looks something like: /tmp/testFoo2345/001
//
// Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
// Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
// "out/soong".
outSoongDir := filepath.Clean(config.soongOutDir)
re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
s = re.ReplaceAllString(s, "out/soong")
// Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with
// Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with
// "out". This must come after the previous replacement otherwise this would replace
// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
outDir := filepath.Dir(outSoongDir)
@ -1018,28 +1033,21 @@ func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string {
return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
}
// OutputFiles first checks if module base outputFiles property has any output
// OutputFiles checks if module base outputFiles property has any output
// files can be used to return.
// If not, it calls OutputFileProducer.OutputFiles on the
// encapsulated module, exits the test immediately if there is an error and
// Exits the test immediately if there is an error and
// otherwise returns the result of calling Paths.RelativeToTop
// on the returned Paths.
func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
// TODO: add non-empty-string tag case and remove OutputFileProducer part
if tag == "" && m.module.base().outputFiles.DefaultOutputFiles != nil {
return m.module.base().outputFiles.DefaultOutputFiles.RelativeToTop()
func (m TestingModule) OutputFiles(ctx *TestContext, t *testing.T, tag string) Paths {
outputFiles := OtherModuleProviderOrDefault(ctx.OtherModuleProviderAdaptor(), m.Module(), OutputFilesProvider)
if tag == "" && outputFiles.DefaultOutputFiles != nil {
return outputFiles.DefaultOutputFiles.RelativeToTop()
} else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag {
return taggedOutputFiles.RelativeToTop()
}
producer, ok := m.module.(OutputFileProducer)
if !ok {
t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
}
paths, err := producer.OutputFiles(tag)
if err != nil {
t.Fatal(err)
}
return paths.RelativeToTop()
t.Fatal(fmt.Errorf("No test output file has been set for tag %q", tag))
return nil
}
// TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
@ -1241,8 +1249,14 @@ func StringPathRelativeToTop(soongOutDir string, path string) string {
}
if isRel {
// The path is in the soong out dir so indicate that in the relative path.
return filepath.Join("out/soong", rel)
if strings.HasSuffix(soongOutDir, testOutSoongSubDir) {
// The path is in the soong out dir so indicate that in the relative path.
return filepath.Join(TestOutSoongDir, rel)
} else {
// Handle the PathForArbitraryOutput case
return filepath.Join(testOutDir, rel)
}
}
// Check to see if the path is relative to the top level out dir.
@ -1312,7 +1326,15 @@ func (ctx *panickingConfigAndErrorContext) Config() Config {
return ctx.ctx.Config()
}
func PanickingConfigAndErrorContext(ctx *TestContext) ConfigAndErrorContext {
func (ctx *panickingConfigAndErrorContext) HasMutatorFinished(mutatorName string) bool {
return ctx.ctx.HasMutatorFinished(mutatorName)
}
func (ctx *panickingConfigAndErrorContext) otherModuleProvider(m blueprint.Module, p blueprint.AnyProviderKey) (any, bool) {
return ctx.ctx.otherModuleProvider(m, p)
}
func PanickingConfigAndErrorContext(ctx *TestContext) ConfigurableEvaluatorContext {
return &panickingConfigAndErrorContext{
ctx: ctx,
}

View file

@ -177,6 +177,41 @@ func setFromList[T comparable](l []T) map[T]bool {
return m
}
// PrettyConcat returns the formatted concatenated string suitable for displaying user-facing
// messages.
func PrettyConcat(list []string, quote bool, lastSep string) string {
if len(list) == 0 {
return ""
}
quoteStr := func(v string) string {
if !quote {
return v
}
return fmt.Sprintf("%q", v)
}
if len(list) == 1 {
return quoteStr(list[0])
}
var sb strings.Builder
for i, val := range list {
if i > 0 {
sb.WriteString(", ")
}
if i == len(list)-1 {
sb.WriteString(lastSep)
if lastSep != "" {
sb.WriteString(" ")
}
}
sb.WriteString(quoteStr(val))
}
return sb.String()
}
// ListSetDifference checks if the two lists contain the same elements. It returns
// a boolean which is true if there is a difference, and then returns lists of elements
// that are in l1 but not l2, and l2 but not l1.
@ -201,6 +236,12 @@ func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
return listsDiffer, diff1, diff2
}
// Returns true if the two lists have common elements.
func HasIntersection[T comparable](l1, l2 []T) bool {
_, a, b := ListSetDifference(l1, l2)
return len(a)+len(b) < len(setFromList(l1))+len(setFromList(l2))
}
// Returns true if the given string s is prefixed with any string in the given prefix list.
func HasAnyPrefix(s string, prefixList []string) bool {
for _, prefix := range prefixList {

View file

@ -818,3 +818,100 @@ func TestReverseSlice(t *testing.T) {
})
}
}
var hasIntersectionTestCases = []struct {
name string
l1 []string
l2 []string
expected bool
}{
{
name: "empty",
l1: []string{"a", "b", "c"},
l2: []string{},
expected: false,
},
{
name: "both empty",
l1: []string{},
l2: []string{},
expected: false,
},
{
name: "identical",
l1: []string{"a", "b", "c"},
l2: []string{"a", "b", "c"},
expected: true,
},
{
name: "duplicates",
l1: []string{"a", "a", "a"},
l2: []string{"a", "b", "c"},
expected: true,
},
{
name: "duplicates with no intersection",
l1: []string{"d", "d", "d", "d"},
l2: []string{"a", "b", "c"},
expected: false,
},
}
func TestHasIntersection(t *testing.T) {
for _, testCase := range hasIntersectionTestCases {
t.Run(testCase.name, func(t *testing.T) {
hasIntersection := HasIntersection(testCase.l1, testCase.l2)
if !reflect.DeepEqual(hasIntersection, testCase.expected) {
t.Errorf("expected %#v, got %#v", testCase.expected, hasIntersection)
}
})
}
}
var prettyConcatTestCases = []struct {
name string
list []string
quote bool
lastSeparator string
expected string
}{
{
name: "empty",
list: []string{},
quote: false,
lastSeparator: "and",
expected: ``,
},
{
name: "single",
list: []string{"a"},
quote: true,
lastSeparator: "and",
expected: `"a"`,
},
{
name: "with separator",
list: []string{"a", "b", "c"},
quote: true,
lastSeparator: "or",
expected: `"a", "b", or "c"`,
},
{
name: "without separator",
list: []string{"a", "b", "c"},
quote: false,
lastSeparator: "",
expected: `a, b, c`,
},
}
func TestPrettyConcat(t *testing.T) {
for _, testCase := range prettyConcatTestCases {
t.Run(testCase.name, func(t *testing.T) {
concatString := PrettyConcat(testCase.list, testCase.quote, testCase.lastSeparator)
if !reflect.DeepEqual(concatString, testCase.expected) {
t.Errorf("expected %#v, got %#v", testCase.expected, concatString)
}
})
}
}

View file

@ -131,6 +131,7 @@ type variableProperties struct {
// are used for dogfooding and performance testing, and should be as similar to user builds
// as possible.
Debuggable struct {
Apk *string
Cflags []string
Cppflags []string
Init_rc []string
@ -142,9 +143,11 @@ type variableProperties struct {
Keep_symbols *bool
Keep_symbols_and_debug_frame *bool
}
Static_libs []string
Whole_static_libs []string
Shared_libs []string
Static_libs []string
Exclude_static_libs []string
Whole_static_libs []string
Shared_libs []string
Jni_libs []string
Cmdline []string
@ -196,10 +199,10 @@ type variableProperties struct {
// release_aidl_use_unfrozen is "true" when a device can
// use the unfrozen versions of AIDL interfaces.
Release_aidl_use_unfrozen struct {
Cflags []string
Cmd *string
Required []string
Vintf_fragments []string
Cflags []string
Cmd *string
Required []string
Vintf_fragment_modules []string
}
} `android:"arch_variant"`
}
@ -248,8 +251,6 @@ type ProductVariables struct {
VendorApiLevel *string `json:",omitempty"`
RecoverySnapshotVersion *string `json:",omitempty"`
DeviceSecondaryArch *string `json:",omitempty"`
DeviceSecondaryArchVariant *string `json:",omitempty"`
DeviceSecondaryCpuVariant *string `json:",omitempty"`
@ -285,8 +286,10 @@ type ProductVariables struct {
AAPTPreferredConfig *string `json:",omitempty"`
AAPTPrebuiltDPI []string `json:",omitempty"`
DefaultAppCertificate *string `json:",omitempty"`
MainlineSepolicyDevCertificates *string `json:",omitempty"`
DefaultAppCertificate *string `json:",omitempty"`
ExtraOtaKeys []string `json:",omitempty"`
ExtraOtaRecoveryKeys []string `json:",omitempty"`
MainlineSepolicyDevCertificates *string `json:",omitempty"`
AppsDefaultVersionName *string `json:",omitempty"`
@ -304,6 +307,7 @@ type ProductVariables struct {
HostStaticBinaries *bool `json:",omitempty"`
Binder32bit *bool `json:",omitempty"`
UseGoma *bool `json:",omitempty"`
UseABFS *bool `json:",omitempty"`
UseRBE *bool `json:",omitempty"`
UseRBEJAVAC *bool `json:",omitempty"`
UseRBER8 *bool `json:",omitempty"`
@ -383,20 +387,6 @@ type ProductVariables struct {
PgoAdditionalProfileDirs []string `json:",omitempty"`
VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
DirectedVendorSnapshot bool `json:",omitempty"`
VendorSnapshotModules map[string]bool `json:",omitempty"`
DirectedRecoverySnapshot bool `json:",omitempty"`
RecoverySnapshotModules map[string]bool `json:",omitempty"`
VendorSnapshotDirsIncluded []string `json:",omitempty"`
VendorSnapshotDirsExcluded []string `json:",omitempty"`
RecoverySnapshotDirsExcluded []string `json:",omitempty"`
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
HostFakeSnapshotEnabled bool `json:",omitempty"`
MultitreeUpdateMeta bool `json:",omitempty"`
BoardVendorSepolicyDirs []string `json:",omitempty"`
@ -405,6 +395,7 @@ type ProductVariables struct {
SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
BoardSepolicyM4Defs []string `json:",omitempty"`
BoardPlatform *string `json:",omitempty"`
BoardSepolicyVers *string `json:",omitempty"`
PlatformSepolicyVersion *string `json:",omitempty"`
@ -446,6 +437,9 @@ type ProductVariables struct {
TargetFSConfigGen []string `json:",omitempty"`
UseSoongSystemImage *bool `json:",omitempty"`
ProductSoongDefinedSystemImage *string `json:",omitempty"`
EnforceProductPartitionInterface *bool `json:",omitempty"`
EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"`
@ -469,7 +463,6 @@ type ProductVariables struct {
GenruleSandboxing *bool `json:",omitempty"`
BuildBrokenEnforceSyspropOwner bool `json:",omitempty"`
BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
BuildBrokenUsesSoongPython2Modules bool `json:",omitempty"`
BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
BuildBrokenIncorrectPartitionImages bool `json:",omitempty"`
BuildBrokenInputDirModules []string `json:",omitempty"`
@ -539,6 +532,11 @@ type ProductVariables struct {
OdmPropFiles []string `json:",omitempty"`
EnableUffdGc *string `json:",omitempty"`
BoardAvbEnable *bool `json:",omitempty"`
BoardAvbSystemAddHashtreeFooterArgs []string `json:",omitempty"`
DeviceFrameworkCompatibilityMatrixFile []string `json:",omitempty"`
DeviceProductCompatibilityMatrixFile []string `json:",omitempty"`
}
type PartitionQualifiedVariablesType struct {

View file

@ -199,9 +199,7 @@ func TestProductVariables(t *testing.T) {
ctx.RegisterModuleType("module3", testProductVariableModuleFactoryFactory(&struct {
Foo []string
}{}))
ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
ctx.BottomUp("variable", VariableMutator).Parallel()
})
registerVariableBuildComponents(ctx)
}),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
@ -210,14 +208,14 @@ func TestProductVariables(t *testing.T) {
var testProductVariableDefaultsProperties = struct {
Product_variables struct {
Eng struct {
Foo []string
Foo []string `android:"arch_variant"`
Bar []string
}
}
} `android:"arch_variant"`
} `android:"arch_variant"`
}{}
type productVariablesDefaultsTestProperties struct {
Foo []string
Foo []string `android:"arch_variant"`
}
type productVariablesDefaultsTestProperties2 struct {
@ -242,7 +240,7 @@ func productVariablesDefaultsTestModuleFactory() Module {
module := &productVariablesDefaultsTestModule{}
module.AddProperties(&module.properties)
module.variableProperties = testProductVariableDefaultsProperties
InitAndroidModule(module)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
InitDefaultableModule(module)
return module
}
@ -324,3 +322,46 @@ func BenchmarkSliceToTypeArray(b *testing.B) {
})
}
}
// Test a defaults module that supports more product variable properties than the target module.
func TestProductVariablesArch(t *testing.T) {
bp := `
test {
name: "foo",
arch: {
arm: {
product_variables: {
eng: {
foo: ["arm"],
},
},
},
arm64: {
product_variables: {
eng: {
foo: ["arm64"],
},
},
},
},
foo: ["module"],
}
`
result := GroupFixturePreparers(
FixtureModifyProductVariables(func(variables FixtureProductVariables) {
variables.Eng = boolPtr(true)
}),
PrepareForTestWithArchMutator,
PrepareForTestWithVariables,
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("test", productVariablesDefaultsTestModuleFactory)
}),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
foo := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*productVariablesDefaultsTestModule)
want := []string{"module", "arm64"}
AssertDeepEquals(t, "foo", want, foo.properties.Foo)
}

84
android/vintf_fragment.go Normal file
View file

@ -0,0 +1,84 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
type vintfFragmentProperties struct {
// Vintf fragment XML file.
Src string `android:"path"`
}
type vintfFragmentModule struct {
ModuleBase
properties vintfFragmentProperties
installDirPath InstallPath
outputFilePath OutputPath
}
func init() {
registerVintfFragmentComponents(InitRegistrationContext)
}
func registerVintfFragmentComponents(ctx RegistrationContext) {
ctx.RegisterModuleType("vintf_fragment", vintfLibraryFactory)
}
// vintf_fragment module processes vintf fragment file and installs under etc/vintf/manifest.
// Vintf fragment files formerly listed in vintf_fragment property would be transformed into
// this module type.
func vintfLibraryFactory() Module {
m := &vintfFragmentModule{}
m.AddProperties(
&m.properties,
)
InitAndroidArchModule(m, DeviceSupported, MultilibFirst)
return m
}
func (m *vintfFragmentModule) GenerateAndroidBuildActions(ctx ModuleContext) {
builder := NewRuleBuilder(pctx, ctx)
srcVintfFragment := PathForModuleSrc(ctx, m.properties.Src)
processedVintfFragment := PathForModuleOut(ctx, srcVintfFragment.Base())
// Process vintf fragment source file with assemble_vintf tool
builder.Command().
Flag("VINTF_IGNORE_TARGET_FCM_VERSION=true").
BuiltTool("assemble_vintf").
FlagWithInput("-i ", srcVintfFragment).
FlagWithOutput("-o ", processedVintfFragment)
builder.Build("assemble_vintf", "Process vintf fragment "+processedVintfFragment.String())
m.installDirPath = PathForModuleInstall(ctx, "etc", "vintf", "manifest")
m.outputFilePath = processedVintfFragment.OutputPath
ctx.InstallFile(m.installDirPath, processedVintfFragment.Base(), processedVintfFragment)
}
// Make this module visible to AndroidMK so it can be referenced from modules defined from Android.mk files
func (m *vintfFragmentModule) AndroidMkEntries() []AndroidMkEntries {
return []AndroidMkEntries{{
Class: "ETC",
OutputFile: OptionalPathForPath(m.outputFilePath),
ExtraEntries: []AndroidMkExtraEntriesFunc{
func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", m.installDirPath.String())
entries.SetString("LOCAL_INSTALLED_MODULE_STEM", m.outputFilePath.Base())
},
},
}}
}

View file

@ -0,0 +1,36 @@
// Copyright 2024 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package android
import (
"strings"
"testing"
)
func TestVintfManifestBuildAction(t *testing.T) {
bp := `
vintf_fragment {
name: "test_vintf_fragment",
src: "test_vintf_file",
}
`
testResult := PrepareForTestWithAndroidBuildComponents.RunTestWithBp(t, bp)
vintfFragmentBuild := testResult.TestContext.ModuleForTests("test_vintf_fragment", "android_arm64_armv8-a").Rule("assemble_vintf")
if !strings.Contains(vintfFragmentBuild.RuleParams.Command, "assemble_vintf") {
t.Errorf("Vintf_manifest build command does not process with assemble_vintf : " + vintfFragmentBuild.RuleParams.Command)
}
}

View file

@ -283,7 +283,7 @@ func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
// This must be registered after the deps have been resolved.
func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
ctx.BottomUp("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
}
// Checks the per-module visibility rule lists before defaults expansion.
@ -507,7 +507,7 @@ func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg, propert
return true, pkg, name
}
func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
func visibilityRuleEnforcer(ctx BottomUpMutatorContext) {
qualified := createVisibilityModuleReference(ctx.ModuleName(), ctx.ModuleDir(), ctx.Module())
// Visit all the dependencies making sure that this module has access to them all.

View file

@ -46,6 +46,9 @@ type sdkRepoHost struct {
outputBaseName string
outputFile android.OptionalPath
// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
installFile android.InstallPath
}
type remapProperties struct {
@ -234,14 +237,18 @@ func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
s.outputBaseName = name
s.outputFile = android.OptionalPathForPath(outputZipFile)
ctx.InstallFile(android.PathForModuleInstall(ctx, "sdk-repo"), name+".zip", outputZipFile)
installPath := android.PathForModuleInstall(ctx, "sdk-repo")
name = name + ".zip"
ctx.InstallFile(installPath, name, outputZipFile)
// TODO(b/357908583): Temp field, remove this once we support Android Mk providers
s.installFile = installPath.Join(ctx, name)
}
func (s *sdkRepoHost) AndroidMk() android.AndroidMkData {
return android.AndroidMkData{
Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", s.installFile.String())
fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
},

View file

@ -84,6 +84,7 @@ type Rule struct {
Prerequisites *MakeString
RecipePos Pos
Recipe string
RecipeEndPos Pos
}
func (x *Rule) Dump() string {
@ -95,7 +96,7 @@ func (x *Rule) Dump() string {
}
func (x *Rule) Pos() Pos { return x.Target.Pos() }
func (x *Rule) End() Pos { return Pos(int(x.RecipePos) + len(x.Recipe)) }
func (x *Rule) End() Pos { return x.RecipeEndPos }
type Variable struct {
Name *MakeString

View file

@ -448,6 +448,7 @@ loop:
Prerequisites: prerequisites,
Recipe: recipe,
RecipePos: recipePos,
RecipeEndPos: p.pos(),
})
}
}

View file

@ -86,20 +86,19 @@ endif`,
},
{
name: "Blank line in rule's command",
in: `all:
in: `all:
echo first line
echo second line`,
out: []Node{
&Rule{
Target: SimpleMakeString("all", NoPos),
RecipePos: NoPos,
Recipe: "echo first line\necho second line",
Target: SimpleMakeString("all", NoPos),
RecipePos: NoPos,
Recipe: "echo first line\necho second line",
Prerequisites: SimpleMakeString("", NoPos),
},
},
},
}
func TestParse(t *testing.T) {
@ -125,3 +124,25 @@ func TestParse(t *testing.T) {
})
}
}
func TestRuleEnd(t *testing.T) {
name := "ruleEndTest"
in := `all:
ifeq (A, A)
echo foo
echo foo
echo foo
echo foo
endif
echo bar
`
p := NewParser(name, bytes.NewBufferString(in))
got, errs := p.Parse()
if len(errs) != 0 {
t.Fatalf("Unexpected errors while parsing: %v", errs)
}
if got[0].End() < got[len(got) -1].Pos() {
t.Errorf("Rule's end (%d) is smaller than directive that inside of rule's start (%v)\n", got[0].End(), got[len(got) -1].Pos())
}
}

View file

@ -15,7 +15,6 @@ bootstrap_go_package {
"soong-cc",
"soong-filesystem",
"soong-java",
"soong-multitree",
"soong-provenance",
"soong-python",
"soong-rust",
@ -37,9 +36,12 @@ bootstrap_go_package {
"apex_test.go",
"bootclasspath_fragment_test.go",
"classpath_element_test.go",
"container_test.go",
"dexpreopt_bootjars_test.go",
"platform_bootclasspath_test.go",
"systemserver_classpath_fragment_test.go",
],
pluginFor: ["soong_build"],
// Used by plugins
visibility: ["//visibility:public"],
}

View file

@ -74,6 +74,8 @@ func TestValidationAcrossContainersExportedPass(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
},
{
@ -122,6 +124,8 @@ func TestValidationAcrossContainersExportedPass(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
},
{
@ -345,6 +349,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
},
@ -392,6 +398,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
},
@ -693,6 +701,8 @@ func TestValidationAcrossContainersNotExportedFail(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
},
@ -769,6 +779,8 @@ func TestValidationNotPropagateAcrossShared(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "none",
system_modules: "none",
}`,
},
}

View file

@ -136,6 +136,11 @@ func (a *apexBundle) androidMkForFiles(w io.Writer, apexBundleName, moduleDir st
fmt.Fprintln(w, "LOCAL_SOONG_INSTALLED_MODULE :=", filepath.Join(modulePath, fi.stem()))
fmt.Fprintln(w, "LOCAL_SOONG_INSTALL_PAIRS :=", fi.builtFile.String()+":"+filepath.Join(modulePath, fi.stem()))
fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
if fi.checkbuildTarget != nil {
fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.checkbuildTarget.String())
} else {
fmt.Fprintln(w, "LOCAL_CHECKED_MODULE :=", fi.builtFile.String())
}
fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.nameInMake())
if fi.module != nil {
// This apexFile's module comes from Soong

View file

@ -32,7 +32,6 @@ import (
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
"android/soong/multitree"
"android/soong/rust"
"android/soong/sh"
)
@ -50,17 +49,11 @@ func registerApexBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("override_apex", OverrideApexFactory)
ctx.RegisterModuleType("apex_set", apexSetFactory)
ctx.PreArchMutators(registerPreArchMutators)
ctx.PreDepsMutators(RegisterPreDepsMutators)
ctx.PostDepsMutators(RegisterPostDepsMutators)
}
func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
ctx.TopDown("prebuilt_apex_module_creator", prebuiltApexModuleCreatorMutator).Parallel()
}
func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_vndk", apexVndkMutator).Parallel()
ctx.BottomUp("apex_vndk_deps", apexVndkDepsMutator).Parallel()
}
@ -158,10 +151,6 @@ type apexBundleProperties struct {
// Default: true.
Installable *bool
// If set true, VNDK libs are considered as stable libs and are not included in this APEX.
// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
Use_vndk_as_stable *bool
// The type of filesystem to use. Either 'ext4', 'f2fs' or 'erofs'. Default 'ext4'.
Payload_fs_type *string
@ -169,10 +158,6 @@ type apexBundleProperties struct {
// Default is false.
Ignore_system_library_special_case *bool
// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
// Default value is true.
Generate_hashtree *bool
// Whenever apex_payload.img of the APEX should not be dm-verity signed. Should be only
// used in tests.
Test_only_unsigned_payload *bool
@ -213,6 +198,50 @@ type apexBundleProperties struct {
}
type ApexNativeDependencies struct {
// List of native libraries that are embedded inside this APEX.
Native_shared_libs proptools.Configurable[[]string]
// List of JNI libraries that are embedded inside this APEX.
Jni_libs []string
// List of rust dyn libraries that are embedded inside this APEX.
Rust_dyn_libs []string
// List of native executables that are embedded inside this APEX.
Binaries proptools.Configurable[[]string]
// List of native tests that are embedded inside this APEX.
Tests []string
// List of filesystem images that are embedded inside this APEX bundle.
Filesystems []string
// List of prebuilt_etcs that are embedded inside this APEX bundle.
Prebuilts proptools.Configurable[[]string]
// List of native libraries to exclude from this APEX.
Exclude_native_shared_libs []string
// List of JNI libraries to exclude from this APEX.
Exclude_jni_libs []string
// List of rust dyn libraries to exclude from this APEX.
Exclude_rust_dyn_libs []string
// List of native executables to exclude from this APEX.
Exclude_binaries []string
// List of native tests to exclude from this APEX.
Exclude_tests []string
// List of filesystem images to exclude from this APEX bundle.
Exclude_filesystems []string
// List of prebuilt_etcs to exclude from this APEX bundle.
Exclude_prebuilts []string
}
type ResolvedApexNativeDependencies struct {
// List of native libraries that are embedded inside this APEX.
Native_shared_libs []string
@ -223,8 +252,7 @@ type ApexNativeDependencies struct {
Rust_dyn_libs []string
// List of native executables that are embedded inside this APEX.
Binaries proptools.Configurable[[]string]
ResolvedBinaries []string `blueprint:"mutated"`
Binaries []string
// List of native tests that are embedded inside this APEX.
Tests []string
@ -233,8 +261,7 @@ type ApexNativeDependencies struct {
Filesystems []string
// List of prebuilt_etcs that are embedded inside this APEX bundle.
Prebuilts proptools.Configurable[[]string]
ResolvedPrebuilts []string `blueprint:"mutated"`
Prebuilts []string
// List of native libraries to exclude from this APEX.
Exclude_native_shared_libs []string
@ -259,14 +286,14 @@ type ApexNativeDependencies struct {
}
// Merge combines another ApexNativeDependencies into this one
func (a *ApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...)
func (a *ResolvedApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) {
a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs.GetOrDefault(ctx, nil)...)
a.Jni_libs = append(a.Jni_libs, b.Jni_libs...)
a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...)
a.ResolvedBinaries = append(a.ResolvedBinaries, b.Binaries.GetOrDefault(ctx, nil)...)
a.Binaries = append(a.Binaries, b.Binaries.GetOrDefault(ctx, nil)...)
a.Tests = append(a.Tests, b.Tests...)
a.Filesystems = append(a.Filesystems, b.Filesystems...)
a.ResolvedPrebuilts = append(a.ResolvedPrebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...)
a.Prebuilts = append(a.Prebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...)
a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...)
a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...)
@ -397,7 +424,6 @@ type apexBundle struct {
android.ModuleBase
android.DefaultableModuleBase
android.OverridableModuleBase
multitree.ExportableModuleBase
// Properties
properties apexBundleProperties
@ -536,6 +562,8 @@ type apexFile struct {
customStem string
symlinks []string // additional symlinks
checkbuildTarget android.Path
// Info for Android.mk Module name of `module` in AndroidMk. Note the generated AndroidMk
// module for apexFile is named something like <AndroidMk module name>.<apex name>[<apex
// suffix>]
@ -571,6 +599,9 @@ func newApexFile(ctx android.BaseModuleContext, builtFile android.Path, androidM
module: module,
}
if module != nil {
if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
ret.checkbuildTarget = installFilesInfo.CheckbuildTarget
}
ret.moduleDir = ctx.OtherModuleDir(module)
ret.partition = module.PartitionTag(ctx.DeviceConfig())
ret.requiredModuleNames = module.RequiredModuleNames(ctx)
@ -658,6 +689,8 @@ type dependencyTag struct {
// If not-nil and an APEX is a member of an SDK then dependencies of that APEX with this tag will
// also be added as exported members of that SDK.
memberType android.SdkMemberType
installable bool
}
func (d *dependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType {
@ -676,18 +709,23 @@ func (d *dependencyTag) ReplaceSourceWithPrebuilt() bool {
return !d.sourceOnly
}
func (d *dependencyTag) InstallDepNeeded() bool {
return d.installable
}
var _ android.ReplaceSourceWithPrebuilt = &dependencyTag{}
var _ android.SdkMemberDependencyTag = &dependencyTag{}
var (
androidAppTag = &dependencyTag{name: "androidApp", payload: true}
bpfTag = &dependencyTag{name: "bpf", payload: true}
certificateTag = &dependencyTag{name: "certificate"}
dclaTag = &dependencyTag{name: "dcla"}
executableTag = &dependencyTag{name: "executable", payload: true}
fsTag = &dependencyTag{name: "filesystem", payload: true}
bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
sscpfTag = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType}
androidAppTag = &dependencyTag{name: "androidApp", payload: true}
bpfTag = &dependencyTag{name: "bpf", payload: true}
certificateTag = &dependencyTag{name: "certificate"}
dclaTag = &dependencyTag{name: "dcla"}
executableTag = &dependencyTag{name: "executable", payload: true}
fsTag = &dependencyTag{name: "filesystem", payload: true}
bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
// The dexpreopt artifacts of apex system server jars are installed onto system image.
sscpfTag = &dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true, memberType: java.SystemServerClasspathFragmentSdkMemberType, installable: true}
compatConfigTag = &dependencyTag{name: "compatConfig", payload: true, sourceOnly: true, memberType: java.CompatConfigSdkMemberType}
javaLibTag = &dependencyTag{name: "javaLib", payload: true}
jniLibTag = &dependencyTag{name: "jniLib", payload: true}
@ -701,13 +739,12 @@ var (
)
// TODO(jiyong): shorten this function signature
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ResolvedApexNativeDependencies, target android.Target, imageVariation string) {
binVariations := target.Variations()
libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
rustLibVariations := append(
target.Variations(), []blueprint.Variation{
{Mutator: "rust_libraries", Variation: "dylib"},
{Mutator: "link", Variation: ""},
}...,
)
@ -720,7 +757,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM
// this module. This is required since arch variant of an APEX bundle is 'common' but it is
// 'arm' or 'arm64' for native shared libs.
ctx.AddFarVariationDependencies(binVariations, executableTag,
android.RemoveListFromList(nativeModules.ResolvedBinaries, nativeModules.Exclude_binaries)...)
android.RemoveListFromList(nativeModules.Binaries, nativeModules.Exclude_binaries)...)
ctx.AddFarVariationDependencies(binVariations, testTag,
android.RemoveListFromList(nativeModules.Tests, nativeModules.Exclude_tests)...)
ctx.AddFarVariationDependencies(libVariations, jniLibTag,
@ -732,7 +769,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM
ctx.AddFarVariationDependencies(target.Variations(), fsTag,
android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...)
ctx.AddFarVariationDependencies(target.Variations(), prebuiltTag,
android.RemoveListFromList(nativeModules.ResolvedPrebuilts, nativeModules.Exclude_prebuilts)...)
android.RemoveListFromList(nativeModules.Prebuilts, nativeModules.Exclude_prebuilts)...)
}
func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@ -751,9 +788,9 @@ func (a *apexBundle) getImageVariationPair() (string, string) {
prefix := android.CoreVariation
if a.SocSpecific() || a.DeviceSpecific() {
prefix = cc.VendorVariation
prefix = android.VendorVariation
} else if a.ProductSpecific() {
prefix = cc.ProductVariation
prefix = android.ProductVariation
}
return prefix, ""
@ -783,7 +820,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
}
}
for i, target := range targets {
var deps ApexNativeDependencies
var deps ResolvedApexNativeDependencies
// Add native modules targeting both ABIs. When multilib.* is omitted for
// native_shared_libs/jni_libs/tests, it implies multilib.both
@ -800,7 +837,7 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
if isPrimaryAbi {
deps.Merge(ctx, a.properties.Multilib.First)
deps.Merge(ctx, ApexNativeDependencies{
Native_shared_libs: nil,
Native_shared_libs: proptools.NewConfigurable[[]string](nil, nil),
Tests: nil,
Jni_libs: nil,
Binaries: a.properties.Binaries,
@ -954,24 +991,6 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
return
}
// Special casing for APEXes on non-system (e.g., vendor, odm, etc.) partitions. They are
// provided with a property named use_vndk_as_stable, which when set to true doesn't collect
// VNDK libraries as transitive dependencies. This option is useful for reducing the size of
// the non-system APEXes because the VNDK libraries won't be included (and duped) in the
// APEX, but shared across APEXes via the VNDK APEX.
useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
if proptools.Bool(a.properties.Use_vndk_as_stable) {
if !useVndk {
mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
}
if a.minSdkVersionValue(mctx) != "" {
mctx.PropertyErrorf("use_vndk_as_stable", "not supported when min_sdk_version is set")
}
if mctx.Failed() {
return
}
}
continueApexDepsWalk := func(child, parent android.Module) bool {
am, ok := child.(android.ApexModule)
if !ok || !am.CanHaveApexVariants() {
@ -989,10 +1008,6 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
return false
}
if useVndk && child.Name() == "libbinder" {
mctx.ModuleErrorf("Module %s in the vendor APEX %s should not use libbinder. Use libbinder_ndk instead.", parent.Name(), a.Name())
}
// By default, all the transitive dependencies are collected, unless filtered out
// above.
return true
@ -1047,6 +1062,8 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo
ApexContents: []*android.ApexContents{apexContents},
TestApexes: testApexes,
BaseApexName: mctx.ModuleName(),
ApexAvailableName: proptools.String(a.properties.Apex_available_name),
}
mctx.WalkDeps(func(child, parent android.Module) bool {
if !continueApexDepsWalk(child, parent) {
@ -1058,7 +1075,7 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) {
if a.dynamic_common_lib_apex() {
android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
ProvidedLibs: a.properties.Native_shared_libs,
ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil),
})
}
}
@ -1175,6 +1192,7 @@ var (
"test_com.android.os.statsd",
"test_com.android.permission",
"test_com.android.wifi",
"test_imgdiag_com.android.art",
"test_jitzygote_com.android.art",
// go/keep-sorted end
}
@ -1373,27 +1391,6 @@ func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Modu
return true
}
var _ android.OutputFileProducer = (*apexBundle)(nil)
// Implements android.OutputFileProducer
func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "", android.DefaultDistTag:
// This is the default dist path.
return android.Paths{a.outputFile}, nil
case imageApexSuffix:
// uncompressed one
if a.outputApexFile != nil {
return android.Paths{a.outputApexFile}, nil
}
fallthrough
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
var _ multitree.Exportable = (*apexBundle)(nil)
func (a *apexBundle) Exportable() bool {
return true
}
@ -1407,7 +1404,7 @@ func (a *apexBundle) TaggedOutputs() map[string]android.Paths {
var _ cc.Coverage = (*apexBundle)(nil)
// Implements cc.Coverage
func (a *apexBundle) IsNativeCoverageNeeded(ctx android.IncomingTransitionContext) bool {
func (a *apexBundle) IsNativeCoverageNeeded(ctx cc.IsNativeCoverageNeededContext) bool {
return ctx.DeviceConfig().NativeCoverageEnabled()
}
@ -1469,11 +1466,6 @@ func (a *apexBundle) installable() bool {
return !a.properties.PreventInstall && (a.properties.Installable == nil || proptools.Bool(a.properties.Installable))
}
// See the generate_hashtree property
func (a *apexBundle) shouldGenerateHashtree() bool {
return proptools.BoolDefault(a.properties.Generate_hashtree, true)
}
// See the test_only_unsigned_payload property
func (a *apexBundle) testOnlyShouldSkipPayloadSign() bool {
return proptools.Bool(a.properties.Test_only_unsigned_payload)
@ -1533,7 +1525,7 @@ func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext
imageVariation := a.getImageVariation()
for _, target := range ctx.MultiTargets() {
if target.Arch.ArchType.Multilib == "lib64" {
addDependenciesForNativeModules(ctx, ApexNativeDependencies{
addDependenciesForNativeModules(ctx, ResolvedApexNativeDependencies{
Native_shared_libs: []string{"libclang_rt.hwasan"},
Tests: nil,
Jni_libs: nil,
@ -1685,12 +1677,10 @@ func apexFileForJavaModuleWithFile(ctx android.ModuleContext, module javaModule,
if sdkLib, ok := module.(*java.SdkLibrary); ok {
for _, install := range sdkLib.BuiltInstalledForApex() {
af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
install.PackageFile(ctx)
}
} else if dexpreopter, ok := module.(java.DexpreopterInterface); ok {
for _, install := range dexpreopter.DexpreoptBuiltInstalledForApex() {
af.requiredModuleNames = append(af.requiredModuleNames, install.FullModuleName())
install.PackageFile(ctx)
}
}
return af
@ -1931,8 +1921,6 @@ type visitorContext struct {
// visitor skips these from this list of module names
unwantedTransitiveDeps []string
aconfigFiles []android.Path
}
func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@ -1999,7 +1987,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
fi.isJniLib = isJniLib
vctx.filesInfo = append(vctx.filesInfo, fi)
addAconfigFiles(vctx, ctx, child)
// Collect the list of stub-providing libs except:
// - VNDK libs are only for vendors
// - bootstrap bionic libs are treated as provided by system
@ -2011,7 +1998,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
fi := apexFileForRustLibrary(ctx, ch)
fi.isJniLib = isJniLib
vctx.filesInfo = append(vctx.filesInfo, fi)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
default:
ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
@ -2020,11 +2006,9 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
switch ch := child.(type) {
case *cc.Module:
vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
case *rust.Module:
vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
default:
ctx.PropertyErrorf("binaries",
@ -2064,7 +2048,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
return false
}
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
default:
ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
@ -2073,14 +2056,11 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
switch ap := child.(type) {
case *java.AndroidApp:
vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
case *java.AndroidAppImport:
vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
addAconfigFiles(vctx, ctx, child)
case *java.AndroidTestHelperApp:
vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
addAconfigFiles(vctx, ctx, child)
case *java.AndroidAppSet:
appDir := "app"
if ap.Privileged() {
@ -2094,7 +2074,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
af.certificate = java.PresignedCertificate
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
default:
ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
}
@ -2126,7 +2105,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
for _, etcFile := range filesToCopy {
vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, etcFile))
}
addAconfigFiles(vctx, ctx, child)
} else {
ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
}
@ -2138,20 +2116,9 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
}
case testTag:
if ccTest, ok := child.(*cc.Module); ok {
if ccTest.IsTestPerSrcAllTestsVariation() {
// Multiple-output test module (where `test_per_src: true`).
//
// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
// We do not add this variation to `filesInfo`, as it has no output;
// however, we do add the other variations of this module as indirect
// dependencies (see below).
} else {
// Single-output test module (where `test_per_src: false`).
af := apexFileForExecutable(ctx, ccTest)
af.class = nativeTest
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
}
af := apexFileForExecutable(ctx, ccTest)
af.class = nativeTest
vctx.filesInfo = append(vctx.filesInfo, af)
return true // track transitive dependencies
} else {
ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
@ -2231,26 +2198,15 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
}
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
} else if rm, ok := child.(*rust.Module); ok {
if !android.IsDepInSameApex(ctx, am, am) {
return false
}
af := apexFileForRustLibrary(ctx, rm)
af.transitiveDep = true
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
}
} else if cc.IsTestPerSrcDepTag(depTag) {
if ch, ok := child.(*cc.Module); ok {
af := apexFileForExecutable(ctx, ch)
// Handle modules created as `test_per_src` variations of a single test module:
// use the name of the generated test binary (`fileToCopy`) instead of the name
// of the original test module (`depName`, shared by all `test_per_src`
// variations of that module).
af.androidMkModuleName = filepath.Base(af.builtFile.String())
// these are not considered transitive dep
af.transitiveDep = false
vctx.filesInfo = append(vctx.filesInfo, af)
return true // track transitive dependencies
}
} else if cc.IsHeaderDepTag(depTag) {
@ -2266,10 +2222,13 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
}
} else if rust.IsDylibDepTag(depTag) {
if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
if !android.IsDepInSameApex(ctx, am, am) {
return false
}
af := apexFileForRustLibrary(ctx, rustm)
af.transitiveDep = true
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
}
} else if rust.IsRlibDepTag(depTag) {
@ -2288,7 +2247,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
return false
}
vctx.filesInfo = append(vctx.filesInfo, af)
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
default:
ctx.PropertyErrorf("bootclasspath_fragments",
@ -2303,7 +2261,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
if profileAf := apexFileForJavaModuleProfile(ctx, child.(javaModule)); profileAf != nil {
vctx.filesInfo = append(vctx.filesInfo, *profileAf)
}
addAconfigFiles(vctx, ctx, child)
return true // track transitive dependencies
default:
ctx.PropertyErrorf("systemserverclasspath_fragments",
@ -2321,19 +2278,6 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
return false
}
func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigPropagatingProviderKey); ok {
if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
}
}
validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
if validationFlag == "error" || validationFlag == "warning" {
android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), module, validationFlag == "error")
}
}
func (a *apexBundle) shouldCheckDuplicate(ctx android.ModuleContext) bool {
// TODO(b/263308293) remove this
if a.properties.IsCoverageVariant {
@ -2415,12 +2359,15 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// 3) some fields in apexBundle struct are configured
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = vctx.filesInfo
a.aconfigFiles = android.FirstUniquePaths(vctx.aconfigFiles)
a.setPayloadFsType(ctx)
a.setSystemLibLink(ctx)
a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
////////////////////////////////////////////////////////////////////////////////////////////
// 3.a) some artifacts are generated from the collected files
a.filesInfo = append(a.filesInfo, a.buildAconfigFiles(ctx)...)
////////////////////////////////////////////////////////////////////////////////////////////
// 4) generate the build rules to create the APEX. This is done in builder.go.
a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
@ -2434,6 +2381,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
a.providePrebuiltInfo(ctx)
a.required = a.RequiredModuleNames(ctx)
a.required = append(a.required, a.VintfFragmentModuleNames(ctx)...)
a.setOutputFiles(ctx)
}
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@ -2462,6 +2412,18 @@ func (a *apexBundle) provideApexExportsInfo(ctx android.ModuleContext) {
})
}
// Set output files to outputFiles property, which is later used to set the
// OutputFilesProvider
func (a *apexBundle) setOutputFiles(ctx android.ModuleContext) {
// default dist path
ctx.SetOutputFiles(android.Paths{a.outputFile}, "")
ctx.SetOutputFiles(android.Paths{a.outputFile}, android.DefaultDistTag)
// uncompressed one
if a.outputApexFile != nil {
ctx.SetOutputFiles(android.Paths{a.outputApexFile}, imageApexSuffix)
}
}
// apexBootclasspathFragmentFiles returns the list of apexFile structures defining the files that
// the bootclasspath_fragment contributes to the apex.
func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint.Module) []apexFile {
@ -2556,7 +2518,6 @@ func newApexBundle() *apexBundle {
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
multitree.InitExportableModule(module)
return module
}
@ -2721,12 +2682,12 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
if a.minSdkVersionValue(ctx) == "" {
ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
}
if a.minSdkVersion(ctx).IsCurrent() {
ctx.PropertyErrorf("updatable", "updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename")
}
if a.UsePlatformApis() {
ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
}
if proptools.Bool(a.properties.Use_vndk_as_stable) {
ctx.PropertyErrorf("use_vndk_as_stable", "updatable APEXes can't use external VNDK libs")
}
if a.FutureUpdatable() {
ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
}
@ -2779,6 +2740,12 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
return
}
// Temporarily bypass /product APEXes with a specific prefix.
// TODO: b/352818241 - Remove this after APEX availability is enforced for /product APEXes.
if a.ProductSpecific() && strings.HasPrefix(a.ApexVariationName(), "com.sdv.") {
return
}
// Coverage build adds additional dependencies for the coverage-only runtime libraries.
// Requiring them and their transitive depencies with apex_available is not right
// because they just add noise.
@ -2813,13 +2780,24 @@ func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
return false
}
if to.AvailableFor(apexName) || baselineApexAvailable(apexName, toName) {
if to.AvailableFor(apexName) {
return true
}
// Let's give some hint for apex_available
hint := fmt.Sprintf("%q", apexName)
if strings.HasPrefix(apexName, "com.") && !strings.HasPrefix(apexName, "com.android.") && strings.Count(apexName, ".") >= 2 {
// In case of a partner APEX, prefix format might be an option.
components := strings.Split(apexName, ".")
components[len(components)-1] = "*"
hint += fmt.Sprintf(" or %q", strings.Join(components, "."))
}
ctx.ModuleErrorf("%q requires %q that doesn't list the APEX under 'apex_available'."+
"\n\nDependency path:%s\n\n"+
"Consider adding %q to 'apex_available' property of %q",
fromName, toName, ctx.GetPathString(true), apexName, toName)
"Consider adding %s to 'apex_available' property of %q",
fromName, toName, ctx.GetPathString(true), hint, toName)
// Visit this module's dependencies to check and report any issues with their availability.
return true
})
@ -2856,80 +2834,12 @@ func isStaticExecutableAllowed(apex string, exec string) bool {
}
// Collect information for opening IDE project files in java/jdeps.go.
func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
func (a *apexBundle) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...)
}
var (
apexAvailBaseline = makeApexAvailableBaseline()
inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
)
func baselineApexAvailable(apex, moduleName string) bool {
key := apex
moduleName = normalizeModuleName(moduleName)
if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
return true
}
key = android.AvailableToAnyApex
if val, ok := apexAvailBaseline[key]; ok && android.InList(moduleName, val) {
return true
}
return false
}
func normalizeModuleName(moduleName string) string {
// Prebuilt modules (e.g. java_import, etc.) have "prebuilt_" prefix added by the build
// system. Trim the prefix for the check since they are confusing
moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
if strings.HasPrefix(moduleName, "libclang_rt.") {
// This module has many arch variants that depend on the product being built.
// We don't want to list them all
moduleName = "libclang_rt"
}
if strings.HasPrefix(moduleName, "androidx.") {
// TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
moduleName = "androidx"
}
return moduleName
}
// Transform the map of apex -> modules to module -> apexes.
func invertApexBaseline(m map[string][]string) map[string][]string {
r := make(map[string][]string)
for apex, modules := range m {
for _, module := range modules {
r[module] = append(r[module], apex)
}
}
return r
}
// Retrieve the baseline of apexes to which the supplied module belongs.
func BaselineApexAvailable(moduleName string) []string {
return inverseApexAvailBaseline[normalizeModuleName(moduleName)]
}
// This is a map from apex to modules, which overrides the apex_available setting for that
// particular module to make it available for the apex regardless of its setting.
// TODO(b/147364041): remove this
func makeApexAvailableBaseline() map[string][]string {
// The "Module separator"s below are employed to minimize merge conflicts.
m := make(map[string][]string)
//
// Module separator
//
m["com.android.runtime"] = []string{
"libz",
}
return m
}
func init() {
android.AddNeverAllowRules(createBcpPermittedPackagesRules(qBcpPackages())...)
android.AddNeverAllowRules(createBcpPermittedPackagesRules(rBcpPackages())...)

View file

@ -46,6 +46,9 @@ var (
Command: "cat $out.rsp | xargs cat" +
// Only track non-external dependencies, i.e. those that end up in the binary
" | grep -v '(external)'" +
// Allowlist androidx deps
" | grep -v '^androidx\\.'" +
" | grep -v '^prebuilt_androidx\\.'" +
// Ignore comments in any of the files
" | grep -v '^#'" +
" | sort -u -f >$out",
@ -85,7 +88,7 @@ func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContex
updatableFlatLists := android.Paths{}
ctx.VisitAllModules(func(module android.Module) {
if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider)
apexInfo, _ := android.OtherModuleProvider(ctx, module, android.ApexInfoProvider)
if path := binaryInfo.FlatListPath(); path != nil {
if binaryInfo.Updatable() || apexInfo.Updatable {
updatableFlatLists = append(updatableFlatLists, path)
@ -152,7 +155,7 @@ func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) {
prebuiltInfos := []android.PrebuiltInfo{}
ctx.VisitAllModules(func(m android.Module) {
prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, android.PrebuiltInfoProvider)
prebuiltInfo, exists := android.OtherModuleProvider(ctx, m, android.PrebuiltInfoProvider)
// Use prebuiltInfoProvider to filter out non apex soong modules.
// Use HideFromMake to filter out the unselected variants of a specific apex.
if exists && !m.IsHideFromMake() {

File diff suppressed because it is too large Load diff

View file

@ -53,11 +53,7 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
prepareForTestWithArtApex,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo", "baz"),
).RunTestWithBp(t, `
@ -108,6 +104,7 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
test: {
enabled: true,
},
sdk_version: "core_current",
}
java_library {
@ -156,7 +153,7 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
// Check stub dex paths exported by art.
artFragment := result.Module("art-bootclasspath-fragment", "android_common")
artInfo, _ := android.SingletonModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
artInfo, _ := android.OtherModuleProvider(result, artFragment, java.HiddenAPIInfoProvider)
bazPublicStubs := "out/soong/.intermediates/baz.stubs.exportable/android_common/dex/baz.stubs.exportable.jar"
bazSystemStubs := "out/soong/.intermediates/baz.stubs.exportable.system/android_common/dex/baz.stubs.exportable.system.jar"
@ -169,7 +166,7 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
// Check stub dex paths exported by other.
otherFragment := result.Module("other-bootclasspath-fragment", "android_common")
otherInfo, _ := android.SingletonModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
otherInfo, _ := android.OtherModuleProvider(result, otherFragment, java.HiddenAPIInfoProvider)
fooPublicStubs := "out/soong/.intermediates/foo.stubs.exportable/android_common/dex/foo.stubs.exportable.jar"
fooSystemStubs := "out/soong/.intermediates/foo.stubs.exportable.system/android_common/dex/foo.stubs.exportable.system.jar"
@ -401,11 +398,20 @@ func TestBootclasspathFragmentInArtApex(t *testing.T) {
// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
addPrebuilt(true, "foo", "bar"),
android.FixtureMergeMockFs(android.MockFS{
"apex_contributions/Android.bp": []byte(`
apex_contributions {
name: "prebuilt_art_contributions",
contents: ["prebuilt_com.android.art"],
api_domain: "com.android.art",
}
`)}),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
).RunTest(t)
ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common", []string{
ensureExactDeapexedContents(t, result.TestContext, "prebuilt_com.android.art", "android_common_com.android.art", []string{
"etc/boot-image.prof",
"javalib/bar.jar",
"javalib/foo.jar",
@ -498,6 +504,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
dexpreopt.FixtureSetTestOnlyArtBootImageJars("com.android.art:foo", "com.android.art:bar"),
java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
)
bp := `
@ -555,6 +562,12 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
src: "com.mycompany.android.art.apex",
exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
}
apex_contributions {
name: "prebuilt_art_contributions",
contents: ["prebuilt_com.android.art"],
api_domain: "com.android.art",
}
`
t.Run("disabled alternative APEX", func(t *testing.T) {
@ -564,27 +577,18 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
`all_apex_contributions`,
`dex2oatd`,
`prebuilt_art-bootclasspath-fragment`,
`prebuilt_com.android.art.apex.selector`,
`prebuilt_com.android.art.deapexer`,
})
java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
`all_apex_contributions`,
`dex2oatd`,
`prebuilt_bar`,
`prebuilt_com.android.art.deapexer`,
`prebuilt_foo`,
})
module := result.ModuleForTests("dex_bootjars", "android_common")
checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
})
t.Run("enabled alternative APEX", func(t *testing.T) {
preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
"Multiple installable prebuilt APEXes provide ambiguous deapexers: prebuilt_com.android.art and prebuilt_com.mycompany.android.art")).
RunTestWithBp(t, fmt.Sprintf(bp, ""))
})
}
// checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
@ -692,7 +696,7 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) {
// Make sure that the fragment provides the hidden API encoded dex jars to the APEX.
fragment := result.Module("mybootclasspathfragment", "android_common_apex10000")
info, _ := android.SingletonModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
info, _ := android.OtherModuleProvider(result, fragment, java.BootclasspathFragmentApexContentInfoProvider)
checkFragmentExportedDexJar := func(name string, expectedDexJar string) {
module := result.Module(name, "android_common_apex10000")
@ -731,11 +735,7 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo", "quuz"),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
).RunTestWithBp(t, `
apex {
name: "com.android.art",
@ -757,6 +757,7 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
],
srcs: ["b.java"],
compile_dex: true,
sdk_version: "core_current",
}
java_sdk_library {
@ -841,6 +842,7 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) {
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"all_apex_contributions",
"art-bootclasspath-fragment",
"bar",
"dex2oatd",
@ -930,6 +932,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_FromSource(t *testing.T) {
],
srcs: ["b.java"],
compile_dex: true,
sdk_version: "core_current",
}
java_library {
@ -1011,6 +1014,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_FromSource(t *testing.T) {
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"all_apex_contributions",
"android-non-updatable.stubs",
"android-non-updatable.stubs.module_lib",
"android-non-updatable.stubs.system",
@ -1101,6 +1105,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_FromText(t *testing.T) {
],
srcs: ["b.java"],
compile_dex: true,
sdk_version: "core_current",
}
java_library {
@ -1182,6 +1187,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_FromText(t *testing.T) {
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"all_apex_contributions",
"android-non-updatable.stubs",
"android-non-updatable.stubs.system",
"android-non-updatable.stubs.test",
@ -1253,6 +1259,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *test
],
srcs: ["b.java"],
compile_dex: true,
sdk_version: "core_current",
}
java_library {
@ -1334,6 +1341,7 @@ func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *test
`)
java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_apex10000", []string{
"all_apex_contributions",
"art-bootclasspath-fragment",
"bar",
"dex2oatd",

View file

@ -83,6 +83,7 @@ func init() {
pctx.HostBinToolVariable("assemble_vintf", "assemble_vintf")
pctx.HostBinToolVariable("apex_elf_checker", "apex_elf_checker")
pctx.HostBinToolVariable("aconfig", "aconfig")
pctx.HostBinToolVariable("host_apex_verifier", "host_apex_verifier")
}
type createStorageStruct struct {
@ -249,6 +250,13 @@ var (
Description: "run apex_linkerconfig_validation",
}, "image_dir")
apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
Command: `${host_apex_verifier} --deapexer=${deapexer} --debugfs=${debugfs_static} ` +
`--fsckerofs=${fsck_erofs} --apex=${in} && touch ${out}`,
CommandDeps: []string{"${host_apex_verifier}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}"},
Description: "run host_apex_verifier",
})
assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{
Command: `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`,
CommandDeps: []string{"${assemble_vintf}"},
@ -262,6 +270,58 @@ var (
}, "tool_path", "unwanted")
)
func (a *apexBundle) buildAconfigFiles(ctx android.ModuleContext) []apexFile {
var aconfigFiles android.Paths
for _, file := range a.filesInfo {
if file.module == nil {
continue
}
if dep, ok := android.OtherModuleProvider(ctx, file.module, android.AconfigPropagatingProviderKey); ok {
if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
aconfigFiles = append(aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
}
}
validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
if validationFlag == "error" || validationFlag == "warning" {
android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), file.module, validationFlag == "error")
}
}
aconfigFiles = android.FirstUniquePaths(aconfigFiles)
var files []apexFile
if len(aconfigFiles) > 0 {
apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
ctx.Build(pctx, android.BuildParams{
Rule: aconfig.AllDeclarationsRule,
Inputs: aconfigFiles,
Output: apexAconfigFile,
Description: "combine_aconfig_declarations",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
},
})
files = append(files, newApexFile(ctx, apexAconfigFile, "aconfig_flags", "etc", etc, nil))
for _, info := range createStorageInfo {
outputFile := android.PathForModuleOut(ctx, info.Output_file)
ctx.Build(pctx, android.BuildParams{
Rule: aconfig.CreateStorageRule,
Inputs: aconfigFiles,
Output: outputFile,
Description: info.Desc,
Args: map[string]string{
"container": ctx.ModuleName(),
"file_type": info.File_type,
"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
},
})
files = append(files, newApexFile(ctx, outputFile, info.File_type, "etc", etc, nil))
}
}
return files
}
// buildManifest creates buile rules to modify the input apex_manifest.json to add information
// gathered by the build system such as provided/required native libraries. Two output files having
// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and
@ -595,7 +655,7 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) {
if len(installMapSet) > 0 {
var installs []string
installs = append(installs, android.SortedKeys(installMapSet)...)
a.SetLicenseInstallMap(installs)
ctx.SetLicenseInstallMap(installs)
}
////////////////////////////////////////////////////////////////////////////////////////////
@ -644,48 +704,10 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) {
outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
defaultReadOnlyFiles := []string{"apex_manifest.json", "apex_manifest.pb"}
aconfigDest := imageDir.Join(ctx, "etc").String()
if len(a.aconfigFiles) > 0 {
apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
ctx.Build(pctx, android.BuildParams{
Rule: aconfig.AllDeclarationsRule,
Inputs: a.aconfigFiles,
Output: apexAconfigFile,
Description: "combine_aconfig_declarations",
Args: map[string]string{
"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
},
})
copyCommands = append(copyCommands, "cp -f "+apexAconfigFile.String()+" "+aconfigDest)
implicitInputs = append(implicitInputs, apexAconfigFile)
defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+apexAconfigFile.Base())
for _, info := range createStorageInfo {
outputFile := android.PathForModuleOut(ctx, info.Output_file)
ctx.Build(pctx, android.BuildParams{
Rule: aconfig.CreateStorageRule,
Inputs: a.aconfigFiles,
Output: outputFile,
Description: info.Desc,
Args: map[string]string{
"container": ctx.ModuleName(),
"file_type": info.File_type,
"cache_files": android.JoinPathsWithPrefix(a.aconfigFiles, "--cache "),
},
})
copyCommands = append(copyCommands, "cp -f "+outputFile.String()+" "+aconfigDest)
implicitInputs = append(implicitInputs, outputFile)
defaultReadOnlyFiles = append(defaultReadOnlyFiles, "etc/"+outputFile.Base())
}
}
////////////////////////////////////////////////////////////////////////////////////
// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
// in this APEX. The file will be used by apexer in later steps.
cannedFsConfig := a.buildCannedFsConfig(ctx, defaultReadOnlyFiles)
cannedFsConfig := a.buildCannedFsConfig(ctx)
implicitInputs = append(implicitInputs, cannedFsConfig)
////////////////////////////////////////////////////////////////////////////////////
@ -767,18 +789,6 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) {
implicitInputs = append(implicitInputs, noticeAssetPath)
optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
// Apexes which are supposed to be installed in builtin dirs(/system, etc)
// don't need hashtree for activation. Therefore, by removing hashtree from
// apex bundle (filesystem image in it, to be specific), we can save storage.
needHashTree := moduleMinSdkVersion.LessThanOrEqualTo(android.SdkVersion_Android10) ||
a.shouldGenerateHashtree()
if ctx.Config().ApexCompressionEnabled() && a.isCompressable() {
needHashTree = true
}
if !needHashTree {
optFlags = append(optFlags, "--no_hashtree")
}
if a.testOnlyShouldSkipPayloadSign() {
optFlags = append(optFlags, "--unsigned_payload")
}
@ -952,6 +962,9 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) {
validations = append(validations,
runApexElfCheckerUnwanted(ctx, unsignedOutputFile.OutputPath, a.properties.Unwanted_transitive_deps))
}
if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
validations = append(validations, runApexHostVerifier(ctx, unsignedOutputFile.OutputPath))
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
Description: "signapk",
@ -1139,8 +1152,8 @@ func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
}
func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext, defaultReadOnlyFiles []string) android.OutputPath {
var readOnlyPaths = defaultReadOnlyFiles
func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
var executablePaths []string // this also includes dirs
var appSetDirs []string
appSetFiles := make(map[string]android.Path)
@ -1246,3 +1259,13 @@ func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.Outpu
})
return timestamp
}
func runApexHostVerifier(ctx android.ModuleContext, apexFile android.OutputPath) android.Path {
timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
ctx.Build(pctx, android.BuildParams{
Rule: apexHostVerifierRule,
Input: apexFile,
Output: timestamp,
})
return timestamp
}

View file

@ -92,6 +92,7 @@ func TestCreateClasspathElements(t *testing.T) {
],
srcs: ["b.java"],
installable: true,
sdk_version: "core_current",
}
java_library {

337
apex/container_test.go Normal file
View file

@ -0,0 +1,337 @@
// Copyright 2024 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 apex
import (
"android/soong/android"
"android/soong/java"
"fmt"
"testing"
)
var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) {
errorMessage := fmt.Sprintf("module %s container %s value differ", name, container)
android.AssertBoolEquals(t, errorMessage, expected, actual)
}
func TestApexDepsContainers(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForApexTest,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mybootclasspathlib", "bar"),
).RunTestWithBp(t, `
apex {
name: "myapex",
key: "myapex.key",
bootclasspath_fragments: [
"mybootclasspathfragment",
],
updatable: true,
min_sdk_version: "30",
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
bootclasspath_fragment {
name: "mybootclasspathfragment",
contents: [
"mybootclasspathlib",
],
apex_available: [
"myapex",
],
hidden_api: {
split_packages: ["*"],
},
}
java_sdk_library {
name: "mybootclasspathlib",
srcs: [
"mybootclasspathlib.java",
],
apex_available: [
"myapex",
],
compile_dex: true,
static_libs: [
"food",
"baz",
],
libs: [
"bar.stubs",
],
min_sdk_version: "30",
sdk_version: "current",
}
java_library {
name: "food",
srcs:[
"A.java",
],
apex_available: [
"myapex",
],
min_sdk_version: "30",
sdk_version: "core_current",
}
java_sdk_library {
name: "bar",
srcs:[
"A.java",
],
min_sdk_version: "30",
sdk_version: "core_current",
}
java_library {
name: "baz",
srcs:[
"A.java",
],
apex_available: [
"//apex_available:platform",
"myapex",
],
min_sdk_version: "30",
sdk_version: "core_current",
}
`)
testcases := []struct {
moduleName string
variant string
isSystemContainer bool
isApexContainer bool
}{
{
moduleName: "mybootclasspathlib",
variant: "android_common_myapex",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "mybootclasspathlib.impl",
variant: "android_common_apex30",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "mybootclasspathlib.stubs",
variant: "android_common",
isSystemContainer: true,
isApexContainer: false,
},
{
moduleName: "food",
variant: "android_common_apex30",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "bar",
variant: "android_common",
isSystemContainer: true,
isApexContainer: false,
},
{
moduleName: "baz",
variant: "android_common_apex30",
isSystemContainer: true,
isApexContainer: true,
},
}
for _, c := range testcases {
m := result.ModuleForTests(c.moduleName, c.variant)
containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
belongingContainers := containers.BelongingContainers()
checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers))
}
}
func TestNonUpdatableApexDepsContainers(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForApexTest,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("mybootclasspathlib", "bar"),
).RunTestWithBp(t, `
apex {
name: "myapex",
key: "myapex.key",
bootclasspath_fragments: [
"mybootclasspathfragment",
],
updatable: false,
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
bootclasspath_fragment {
name: "mybootclasspathfragment",
contents: [
"mybootclasspathlib",
],
apex_available: [
"myapex",
],
hidden_api: {
split_packages: ["*"],
},
}
java_sdk_library {
name: "mybootclasspathlib",
srcs: [
"mybootclasspathlib.java",
],
apex_available: [
"myapex",
],
compile_dex: true,
static_libs: [
"food",
],
libs: [
"bar.stubs",
],
sdk_version: "current",
}
java_library {
name: "food",
srcs:[
"A.java",
],
apex_available: [
"myapex",
],
sdk_version: "core_current",
}
java_sdk_library {
name: "bar",
srcs:[
"A.java",
],
sdk_version: "none",
system_modules: "none",
}
`)
testcases := []struct {
moduleName string
variant string
isSystemContainer bool
isApexContainer bool
}{
{
moduleName: "mybootclasspathlib",
variant: "android_common_myapex",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "mybootclasspathlib.impl",
variant: "android_common_apex10000",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "mybootclasspathlib.stubs",
variant: "android_common",
isSystemContainer: true,
isApexContainer: false,
},
{
moduleName: "food",
variant: "android_common_apex10000",
isSystemContainer: true,
isApexContainer: true,
},
{
moduleName: "bar",
variant: "android_common",
isSystemContainer: true,
isApexContainer: false,
},
}
for _, c := range testcases {
m := result.ModuleForTests(c.moduleName, c.variant)
containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider)
belongingContainers := containers.BelongingContainers()
checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers))
checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers))
}
}
func TestUpdatableAndNonUpdatableApexesIdenticalMinSdkVersion(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForApexTest,
java.PrepareForTestWithJavaSdkLibraryFiles,
android.FixtureMergeMockFs(android.MockFS{
"system/sepolicy/apex/myapex_non_updatable-file_contexts": nil,
"system/sepolicy/apex/myapex_updatable-file_contexts": nil,
}),
).RunTestWithBp(t, `
apex {
name: "myapex_non_updatable",
key: "myapex_non_updatable.key",
java_libs: [
"foo",
],
updatable: false,
min_sdk_version: "30",
}
apex_key {
name: "myapex_non_updatable.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
apex {
name: "myapex_updatable",
key: "myapex_updatable.key",
java_libs: [
"foo",
],
updatable: true,
min_sdk_version: "30",
}
apex_key {
name: "myapex_updatable.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
java_library {
name: "foo",
srcs:[
"A.java",
],
apex_available: [
"myapex_non_updatable",
"myapex_updatable",
],
min_sdk_version: "30",
sdk_version: "current",
}
`)
fooApexVariant := result.ModuleForTests("foo", "android_common_apex30")
containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), fooApexVariant.Module(), android.ContainersInfoProvider)
belongingContainers := containers.BelongingContainers()
checkContainerMatch(t, "foo", "system", true, android.InList(android.SystemContainer, belongingContainers))
checkContainerMatch(t, "foo", "apex", true, android.InList(android.ApexContainer, belongingContainers))
}

View file

@ -15,37 +15,9 @@
package apex
import (
"strings"
"android/soong/android"
)
// Contains 'deapexer' a private module type used by 'prebuilt_apex' to make dex files contained
// within a .apex file referenced by `prebuilt_apex` available for use by their associated
// `java_import` modules.
//
// An 'apex' module references `java_library` modules from which .dex files are obtained that are
// stored in the resulting `.apex` file. The resulting `.apex` file is then made available as a
// prebuilt by referencing it from a `prebuilt_apex`. For each such `java_library` that is used by
// modules outside the `.apex` file a `java_import` prebuilt is made available referencing a jar
// that contains the Java classes.
//
// When building a Java module type, e.g. `java_module` or `android_app` against such prebuilts the
// `java_import` provides the classes jar (jar containing `.class` files) against which the
// module's `.java` files are compiled. That classes jar usually contains only stub classes. The
// resulting classes jar is converted into a dex jar (jar containing `.dex` files). Then if
// necessary the dex jar is further processed by `dexpreopt` to produce an optimized form of the
// library specific to the current Android version. This process requires access to implementation
// dex jars for each `java_import`. The `java_import` will obtain the implementation dex jar from
// the `.apex` file in the associated `prebuilt_apex`.
//
// This is intentionally not registered by name as it is not intended to be used from within an
// `Android.bp` file.
// DeapexerProperties specifies the properties supported by the deapexer module.
//
// As these are never intended to be supplied in a .bp file they use a different naming convention
// to make it clear that they are different.
type DeapexerProperties struct {
// List of common modules that may need access to files exported by this module.
//
@ -72,46 +44,9 @@ type SelectedApexProperties struct {
Selected_apex *string `android:"path" blueprint:"mutated"`
}
type Deapexer struct {
android.ModuleBase
properties DeapexerProperties
selectedApexProperties SelectedApexProperties
inputApex android.Path
}
// Returns the name of the deapexer module corresponding to an APEX module with the given name.
func deapexerModuleName(apexModuleName string) string {
return apexModuleName + ".deapexer"
}
// Returns the name of the APEX module corresponding to an deapexer module with
// the given name. This reverses deapexerModuleName.
func apexModuleName(deapexerModuleName string) string {
return strings.TrimSuffix(deapexerModuleName, ".deapexer")
}
func privateDeapexerFactory() android.Module {
module := &Deapexer{}
module.AddProperties(&module.properties, &module.selectedApexProperties)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func (p *Deapexer) DepsMutator(ctx android.BottomUpMutatorContext) {
// Add dependencies from the java modules to which this exports files from the `.apex` file onto
// this module so that they can access the `DeapexerInfo` object that this provides.
// TODO: b/308174306 - Once all the mainline modules have been flagged, drop this dependency edge
for _, lib := range p.properties.CommonModules {
dep := prebuiltApexExportedModuleName(ctx, lib)
ctx.AddReverseDependency(ctx.Module(), android.DeapexerTag, dep)
}
}
func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.inputApex = android.OptionalPathForModuleSrc(ctx, p.selectedApexProperties.Selected_apex).Path()
// deapex creates the build rules to deapex a prebuilt .apex file
// it returns a pointer to a DeapexerInfo object
func deapex(ctx android.ModuleContext, apexFile android.Path, deapexerProps DeapexerProperties) *android.DeapexerInfo {
// Create and remember the directory into which the .apex file's contents will be unpacked.
deapexerOutput := android.PathForModuleOut(ctx, "deapexer")
@ -119,7 +54,7 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// Create mappings from apex relative path to the extracted file's path.
exportedPaths := make(android.Paths, 0, len(exports))
for _, path := range p.properties.ExportedFiles {
for _, path := range deapexerProps.ExportedFiles {
// Populate the exports that this makes available.
extractedPath := deapexerOutput.Join(ctx, path)
exports[path] = extractedPath
@ -131,9 +66,8 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// apex relative path to extracted file path available for other modules.
if len(exports) > 0 {
// Make the information available for other modules.
di := android.NewDeapexerInfo(apexModuleName(ctx.ModuleName()), exports, p.properties.CommonModules)
di.AddDexpreoptProfileGuidedExportedModuleNames(p.properties.DexpreoptProfileGuidedModules...)
android.SetProvider(ctx, android.DeapexerProvider, di)
di := android.NewDeapexerInfo(ctx.ModuleName(), exports, deapexerProps.CommonModules)
di.AddDexpreoptProfileGuidedExportedModuleNames(deapexerProps.DexpreoptProfileGuidedModules...)
// Create a sorted list of the files that this exports.
exportedPaths = android.SortedUniquePaths(exportedPaths)
@ -147,11 +81,13 @@ func (p *Deapexer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
BuiltTool("deapexer").
BuiltTool("debugfs").
BuiltTool("fsck.erofs").
Input(p.inputApex).
Input(apexFile).
Text(deapexerOutput.String())
for _, p := range exportedPaths {
command.Output(p.(android.WritablePath))
}
builder.Build("deapexer", "deapex "+apexModuleName(ctx.ModuleName()))
builder.Build("deapexer", "deapex "+ctx.ModuleName())
return &di
}
return nil
}

View file

@ -127,16 +127,29 @@ func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOu
src: "com.android.art-arm.apex",
exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
}
apex_contributions {
name: "prebuilt_art_contributions",
contents: ["prebuilt_com.android.art"],
api_domain: "com.android.art",
}
`
result := android.GroupFixturePreparers(
fixture := android.GroupFixturePreparers(
java.PrepareForTestWithDexpreopt,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo"),
java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"),
PrepareForTestWithApexBuildComponents,
prepareForTestWithArtApex,
).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
)
if preferPrebuilt {
fixture = android.GroupFixturePreparers(
fixture,
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
)
}
result := fixture.RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")
rule := dexBootJars.Output(ruleFile)
@ -200,7 +213,7 @@ func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
"out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
"out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
"out/soong/.intermediates/default/java/dex_bootjars/android_common/boot/boot.prof",
"out/soong/dexpreopt/uffd_gc_flag.txt",
}
@ -384,12 +397,12 @@ func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) {
{
desc: "Prebuilt apex prebuilt_com.android.art is selected, profile should come from .prof deapexed from the prebuilt",
selectedArtApexContributions: "art.prebuilt.contributions",
expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art/android_common_com.android.art/deapexer/etc/boot-image.prof",
},
{
desc: "Prebuilt apex prebuilt_com.android.art.v2 is selected, profile should come from .prof deapexed from the prebuilt",
selectedArtApexContributions: "art.prebuilt.v2.contributions",
expectedProfile: "out/soong/.intermediates/prebuilt_com.android.art.v2.deapexer/android_common/deapexer/etc/boot-image.prof",
expectedProfile: "out/soong/.intermediates/com.android.art.v2/android_common_com.android.art/deapexer/etc/boot-image.prof",
},
}
for _, tc := range testCases {
@ -399,11 +412,7 @@ func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) {
java.FixtureConfigureBootJars("com.android.art:core-oj"),
PrepareForTestWithApexBuildComponents,
prepareForTestWithArtApex,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions,
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", tc.selectedArtApexContributions),
).RunTestWithBp(t, bp)
dexBootJars := result.ModuleForTests("dex_bootjars", "android_common")

View file

@ -154,7 +154,7 @@ func TestPlatformBootclasspath_Fragments(t *testing.T) {
).RunTest(t)
pbcp := result.Module("platform-bootclasspath", "android_common")
info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
for _, category := range java.HiddenAPIFlagFileCategories {
name := category.PropertyName()
@ -236,7 +236,7 @@ func TestPlatformBootclasspath_LegacyPrebuiltFragment(t *testing.T) {
)
pbcp := result.Module("myplatform-bootclasspath", "android_common")
info, _ := android.SingletonModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
info, _ := android.OtherModuleProvider(result, pbcp, java.MonolithicHiddenAPIInfoProvider)
android.AssertArrayString(t, "stub flags", []string{"prebuilt-stub-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
android.AssertArrayString(t, "all flags", []string{"prebuilt-all-flags.csv:out/soong/.intermediates/mybootclasspath-fragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
@ -254,11 +254,7 @@ func TestPlatformBootclasspathDependencies(t *testing.T) {
java.FixtureWithLastReleaseApis("foo"),
java.PrepareForTestWithDexpreopt,
dexpreopt.FixtureDisableDexpreoptBootImages(false),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
).RunTestWithBp(t, `
apex {
name: "com.android.art",
@ -297,6 +293,7 @@ func TestPlatformBootclasspathDependencies(t *testing.T) {
],
srcs: ["b.java"],
installable: true,
sdk_version: "core_current",
}
// Add a java_import that is not preferred and so won't have an appropriate apex variant created
@ -429,10 +426,9 @@ func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) {
java.PrepareForTestWithJavaSdkLibraryFiles,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
variables.BuildFlags = map[string]string{
"RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true",
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
java.FixtureWithPrebuiltApis(map[string][]string{
"current": {},
"30": {"foo"},
@ -796,6 +792,128 @@ func TestNonBootJarInFragment(t *testing.T) {
`)
}
// Skip bcp_fragment content validation of source apexes if prebuilts are active.
func TestNonBootJarInPrebuilts(t *testing.T) {
testCases := []struct {
description string
selectedApexContributions string
expectedError string
}{
{
description: "source is active",
selectedApexContributions: "",
expectedError: "in contents must also be declared in PRODUCT_APEX_BOOT_JARS",
},
{
description: "prebuilts are active",
selectedApexContributions: "myapex.prebuilt.contributions",
expectedError: "", // skip content validation of source bcp fragment
},
}
bp := `
// Source
apex {
name: "myapex",
key: "myapex.key",
bootclasspath_fragments: ["apex-fragment"],
updatable: false,
min_sdk_version: "29",
}
override_apex {
name: "myapex.override", // overrides the min_sdk_version, thereby creating different variants of its transitive deps
base: "myapex",
min_sdk_version: "34",
}
apex_key {
name: "myapex.key",
public_key: "testkey.avbpubkey",
private_key: "testkey.pem",
}
java_library {
name: "foo",
srcs: ["b.java"],
installable: true,
apex_available: ["myapex"],
permitted_packages: ["foo"],
min_sdk_version: "29",
}
java_library {
name: "bar",
srcs: ["b.java"],
installable: true,
apex_available: ["myapex"],
permitted_packages: ["bar"],
min_sdk_version: "29",
}
bootclasspath_fragment {
name: "apex-fragment",
contents: ["foo", "bar"],
apex_available:[ "myapex" ],
hidden_api: {
split_packages: ["*"],
},
}
platform_bootclasspath {
name: "myplatform-bootclasspath",
fragments: [{
apex: "myapex",
module:"apex-fragment",
}],
}
// prebuilts
prebuilt_apex {
name: "myapex",
apex_name: "myapex",
src: "myapex.apex",
exported_bootclasspath_fragments: ["apex-fragment"],
}
prebuilt_bootclasspath_fragment {
name: "apex-fragment",
contents: ["foo"],
hidden_api: {
annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
metadata: "my-bootclasspath-fragment/metadata.csv",
index: "my-bootclasspath-fragment/index.csv",
stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
all_flags: "my-bootclasspath-fragment/all-flags.csv",
},
}
java_import {
name: "foo",
jars: ["foo.jar"],
}
apex_contributions {
name: "myapex.prebuilt.contributions",
api_domain: "myapex",
contents: ["prebuilt_myapex"],
}
`
for _, tc := range testCases {
fixture := android.GroupFixturePreparers(
prepareForTestWithPlatformBootclasspath,
PrepareForTestWithApexBuildComponents,
prepareForTestWithMyapex,
java.FixtureConfigureApexBootJars("myapex:foo"),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions),
)
if tc.expectedError != "" {
fixture = fixture.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedError))
}
fixture.RunTestWithBp(t, bp)
}
}
// Source and prebuilt apex provide different set of boot jars
func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
bp := `
@ -935,11 +1053,7 @@ func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
PrepareForTestWithApexBuildComponents,
prepareForTestWithMyapex,
java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.BuildFlags = map[string]string{
"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
}
}),
android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "my_apex_contributions"),
)
if tc.errorExpected {
fixture = fixture.ExtendWithErrorHandler(

View file

@ -15,9 +15,6 @@
package apex
import (
"fmt"
"io"
"path/filepath"
"strconv"
"strings"
@ -42,6 +39,11 @@ var (
CommandDeps: []string{"${extract_apks}"},
},
"abis", "allow-prereleased", "sdk-version", "skip-sdk-check")
decompressApex = pctx.StaticRule("decompressApex", blueprint.RuleParams{
Command: `rm -rf $out && ${deapexer} decompress --copy-if-uncompressed --input ${in} --output ${out}`,
CommandDeps: []string{"${deapexer}"},
Description: "decompress $out",
})
)
type prebuilt interface {
@ -65,10 +67,6 @@ type prebuiltCommon struct {
// fragment for this apex for apexkeys.txt
apexKeysPath android.WritablePath
// A list of apexFile objects created in prebuiltCommon.initApexFilesForAndroidMk which are used
// to create make modules in prebuiltCommon.AndroidMkEntries.
apexFilesForAndroidMk []apexFile
// Installed locations of symlinks for backward compatibility.
compatSymlinks android.InstallPaths
@ -109,11 +107,6 @@ type PrebuiltCommonProperties struct {
// from PRODUCT_PACKAGES.
Overrides []string
// List of java libraries that are embedded inside this prebuilt APEX bundle and for which this
// APEX bundle will create an APEX variant and provide dex implementation jars for use by
// dexpreopt and boot jars package check.
Exported_java_libs []string
// List of bootclasspath fragments inside this prebuilt APEX bundle and for which this APEX
// bundle will create an APEX variant.
Exported_bootclasspath_fragments []string
@ -197,14 +190,12 @@ func (p *prebuiltCommon) initApexFilesForAndroidMk(ctx android.ModuleContext) {
// If this apex contains a system server jar, then the dexpreopt artifacts should be added as required
for _, install := range p.Dexpreopter.DexpreoptBuiltInstalledForApex() {
p.requiredModuleNames = append(p.requiredModuleNames, install.FullModuleName())
install.PackageFile(ctx)
}
}
// If this prebuilt has system server jar, create the rules to dexpreopt it and install it alongside the prebuilt apex
func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
// If this apex does not export anything, return
if !p.hasExportedDeps() {
func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext, di *android.DeapexerInfo) {
if di == nil {
return
}
// If this prebuilt apex has not been selected, return
@ -213,10 +204,7 @@ func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
}
// Use apex_name to determine the api domain of this prebuilt apex
apexName := p.ApexVariationName()
di, err := android.FindDeapexerProviderForModule(ctx)
if err != nil {
ctx.ModuleErrorf(err.Error())
}
// TODO: do not compute twice
dc := dexpreopt.GetGlobalConfig(ctx)
systemServerJarList := dc.AllApexSystemServerJars(ctx)
@ -231,11 +219,6 @@ func (p *prebuiltCommon) dexpreoptSystemServerJars(ctx android.ModuleContext) {
}
func (p *prebuiltCommon) addRequiredModules(entries *android.AndroidMkEntries) {
for _, fi := range p.apexFilesForAndroidMk {
entries.AddStrings("LOCAL_REQUIRED_MODULES", fi.requiredModuleNames...)
entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
}
entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
}
@ -267,77 +250,11 @@ func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
entriesList = append(entriesList, install.ToMakeEntries())
}
// Iterate over the apexFilesForAndroidMk list and create an AndroidMkEntries struct for each
// file. This provides similar behavior to that provided in apexBundle.AndroidMk() as it makes the
// apex specific variants of the exported java modules available for use from within make.
apexName := p.BaseModuleName()
for _, fi := range p.apexFilesForAndroidMk {
entries := p.createEntriesForApexFile(fi, apexName)
entriesList = append(entriesList, entries)
}
return entriesList
}
// createEntriesForApexFile creates an AndroidMkEntries for the supplied apexFile
func (p *prebuiltCommon) createEntriesForApexFile(fi apexFile, apexName string) android.AndroidMkEntries {
moduleName := fi.androidMkModuleName + "." + apexName
entries := android.AndroidMkEntries{
Class: fi.class.nameInMake(),
OverrideName: moduleName,
OutputFile: android.OptionalPathForPath(fi.builtFile),
Include: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar Therefore
// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
// we will have foo.jar.jar
entries.SetString("LOCAL_MODULE_STEM", strings.TrimSuffix(fi.stem(), ".jar"))
entries.SetString("LOCAL_SOONG_DEX_JAR", fi.builtFile.String())
entries.SetString("LOCAL_DEX_PREOPT", "false")
},
},
ExtraFooters: []android.AndroidMkExtraFootersFunc{
func(w io.Writer, name, prefix, moduleDir string) {
// m <module_name> will build <module_name>.<apex_name> as well.
if fi.androidMkModuleName != moduleName {
fmt.Fprintf(w, ".PHONY: %s\n", fi.androidMkModuleName)
fmt.Fprintf(w, "%s: %s\n", fi.androidMkModuleName, moduleName)
}
},
},
}
return entries
}
// prebuiltApexModuleCreator defines the methods that need to be implemented by prebuilt_apex and
// apex_set in order to create the modules needed to provide access to the prebuilt .apex file.
type prebuiltApexModuleCreator interface {
createPrebuiltApexModules(ctx android.TopDownMutatorContext)
}
// prebuiltApexModuleCreatorMutator is the mutator responsible for invoking the
// prebuiltApexModuleCreator's createPrebuiltApexModules method.
//
// It is registered as a pre-arch mutator as it must run after the ComponentDepsMutator because it
// will need to access dependencies added by that (exported modules) but must run before the
// DepsMutator so that the deapexer module it creates can add dependencies onto itself from the
// exported modules.
func prebuiltApexModuleCreatorMutator(ctx android.TopDownMutatorContext) {
module := ctx.Module()
if creator, ok := module.(prebuiltApexModuleCreator); ok {
creator.createPrebuiltApexModules(ctx)
}
}
func (p *prebuiltCommon) hasExportedDeps() bool {
return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 ||
len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
return len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
}
@ -345,11 +262,6 @@ func (p *prebuiltCommon) hasExportedDeps() bool {
func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
module := ctx.Module()
for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
prebuiltDep := android.PrebuiltNameFromSource(dep)
ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep)
}
for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
prebuiltDep := android.PrebuiltNameFromSource(dep)
ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
@ -467,34 +379,6 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
}
}
// prebuiltApexSelectorModule is a private module type that is only created by the prebuilt_apex
// module. It selects the apex to use and makes it available for use by prebuilt_apex and the
// deapexer.
type prebuiltApexSelectorModule struct {
android.ModuleBase
apexFileProperties ApexFileProperties
inputApex android.Path
}
func privateApexSelectorModuleFactory() android.Module {
module := &prebuiltApexSelectorModule{}
module.AddProperties(
&module.apexFileProperties,
)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func (p *prebuiltApexSelectorModule) Srcs() android.Paths {
return android.Paths{p.inputApex}
}
func (p *prebuiltApexSelectorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.inputApex = android.SingleSourcePathFromSupplier(ctx, p.apexFileProperties.prebuiltApexSelector, "src")
}
type Prebuilt struct {
prebuiltCommon
@ -511,7 +395,7 @@ type ApexFileProperties struct {
// This cannot be marked as `android:"arch_variant"` because the `prebuilt_apex` is only mutated
// for android_common. That is so that it will have the same arch variant as, and so be compatible
// with, the source `apex` module type that it replaces.
Src *string `android:"path"`
Src proptools.Configurable[string] `android:"path,replace_instead_of_append"`
Arch struct {
Arm struct {
Src *string `android:"path"`
@ -537,11 +421,11 @@ type ApexFileProperties struct {
// to use methods on it that are specific to the current module.
//
// See the ApexFileProperties.Src property.
func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) []string {
func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext, prebuilt android.Module) string {
multiTargets := prebuilt.MultiTargets()
if len(multiTargets) != 1 {
ctx.OtherModuleErrorf(prebuilt, "compile_multilib shouldn't be \"both\" for prebuilt_apex")
return nil
return ""
}
var src string
switch multiTargets[0].Arch.ArchType {
@ -561,7 +445,7 @@ func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext,
src = String(p.Arch.X86_64.Src)
}
if src == "" {
src = String(p.Src)
src = p.Src.GetOrDefault(ctx, "")
}
if src == "" {
@ -574,7 +458,7 @@ func (p *ApexFileProperties) prebuiltApexSelector(ctx android.BaseModuleContext,
// logic from reporting a more general, less useful message.
}
return []string{src}
return src
}
type PrebuiltProperties struct {
@ -587,48 +471,23 @@ func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
return false
}
func (p *Prebuilt) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
return android.Paths{p.outputApex}, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func PrebuiltFactory() android.Module {
module := &Prebuilt{}
module.AddProperties(&module.properties)
module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
// init the module as a prebuilt
// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
// InitPrebuiltModule* are not friendly with Sources of Configurable type.
// The actual src will be evaluated in GenerateAndroidBuildActions.
android.InitPrebuiltModuleWithoutSrcs(module)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func createApexSelectorModule(ctx android.TopDownMutatorContext, name string, apexFileProperties *ApexFileProperties) {
props := struct {
Name *string
}{
Name: proptools.StringPtr(name),
}
ctx.CreateModule(privateApexSelectorModuleFactory,
&props,
apexFileProperties,
)
}
// createDeapexerModuleIfNeeded will create a deapexer module if it is needed.
//
// A deapexer module is only needed when the prebuilt apex specifies one or more modules in either
// the `exported_java_libs` or `exported_bootclasspath_fragments` properties as that indicates that
// the listed modules need access to files from within the prebuilt .apex file.
func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
// Only create the deapexer module if it is needed.
if !p.hasExportedDeps() {
return
}
func (p *prebuiltCommon) getDeapexerPropertiesIfNeeded(ctx android.ModuleContext) DeapexerProperties {
// Compute the deapexer properties from the transitive dependencies of this module.
commonModules := []string{}
dexpreoptProfileGuidedModules := []string{}
@ -662,7 +521,7 @@ func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutator
})
// Create properties for deapexer module.
deapexerProperties := &DeapexerProperties{
deapexerProperties := DeapexerProperties{
// Remove any duplicates from the common modules lists as a module may be included via a direct
// dependency as well as transitive ones.
CommonModules: android.SortedUniqueStrings(commonModules),
@ -671,22 +530,7 @@ func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutator
// Populate the exported files property in a fixed order.
deapexerProperties.ExportedFiles = android.SortedUniqueStrings(exportedFiles)
props := struct {
Name *string
Selected_apex *string
}{
Name: proptools.StringPtr(deapexerName),
Selected_apex: proptools.StringPtr(apexFileSource),
}
ctx.CreateModule(privateDeapexerFactory,
&props,
deapexerProperties,
)
}
func apexSelectorModuleName(baseModuleName string) string {
return baseModuleName + ".apex.selector"
return deapexerProperties
}
func prebuiltApexExportedModuleName(ctx android.BottomUpMutatorContext, name string) string {
@ -728,97 +572,50 @@ func (t exportedDependencyTag) RequiresFilesFromPrebuiltApex() {}
var _ android.RequiresFilesFromPrebuiltApexTag = exportedDependencyTag{}
var (
exportedJavaLibTag = exportedDependencyTag{name: "exported_java_libs"}
exportedBootclasspathFragmentTag = exportedDependencyTag{name: "exported_bootclasspath_fragments"}
exportedSystemserverclasspathFragmentTag = exportedDependencyTag{name: "exported_systemserverclasspath_fragments"}
)
var _ prebuiltApexModuleCreator = (*Prebuilt)(nil)
// createPrebuiltApexModules creates modules necessary to export files from the prebuilt apex to the
// build.
//
// If this needs to make files from within a `.apex` file available for use by other Soong modules,
// e.g. make dex implementation jars available for java_import modules listed in exported_java_libs,
// it does so as follows:
//
// 1. It creates a `deapexer` module that actually extracts the files from the `.apex` file and
// makes them available for use by other modules, at both Soong and ninja levels.
//
// 2. It adds a dependency onto those modules and creates an apex specific variant similar to what
// an `apex` module does. That ensures that code which looks for specific apex variant, e.g.
// dexpreopt, will work the same way from source and prebuilt.
//
// 3. The `deapexer` module adds a dependency from the modules that require the exported files onto
// itself so that they can retrieve the file paths to those files.
//
// It also creates a child module `selector` that is responsible for selecting the appropriate
// input apex for both the prebuilt_apex and the deapexer. That is needed for a couple of reasons:
//
// 1. To dedup the selection logic so it only runs in one module.
//
// 2. To allow the deapexer to be wired up to a different source for the input apex, e.g. an
// `apex_set`.
//
// prebuilt_apex
// / | \
// / | \
// V V V
// selector <--- deapexer <--- exported java lib
func (p *Prebuilt) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
apexSelectorModuleName := apexSelectorModuleName(p.Name())
createApexSelectorModule(ctx, apexSelectorModuleName, &p.properties.ApexFileProperties)
apexFileSource := ":" + apexSelectorModuleName
p.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(p.Name()), apexFileSource)
// Add a source reference to retrieve the selected apex from the selector module.
p.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
}
func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
p.prebuiltApexContentsDeps(ctx)
}
func (p *prebuiltCommon) DepsMutator(ctx android.BottomUpMutatorContext) {
if p.hasExportedDeps() {
// Create a dependency from the prebuilt apex (prebuilt_apex/apex_set) to the internal deapexer module
// The deapexer will return a provider that will be bubbled up to the rdeps of apexes (e.g. dex_bootjars)
ctx.AddDependency(ctx.Module(), android.DeapexerTag, deapexerModuleName(p.Name()))
}
}
var _ ApexInfoMutator = (*Prebuilt)(nil)
func (p *Prebuilt) ApexInfoMutator(mctx android.TopDownMutatorContext) {
p.apexInfoMutator(mctx)
}
// creates the build rules to deapex the prebuilt, and returns a deapexerInfo
func (p *prebuiltCommon) getDeapexerInfo(ctx android.ModuleContext, apexFile android.Path) *android.DeapexerInfo {
if !p.hasExportedDeps() {
// nothing to do
return nil
}
deapexerProps := p.getDeapexerPropertiesIfNeeded(ctx)
return deapex(ctx, apexFile, deapexerProps)
}
// Set a provider containing information about the jars and .prof provided by the apex
// Apexes built from prebuilts retrieve this information by visiting its internal deapexer module
// Used by dex_bootjars to generate the boot image
func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext) {
if !p.hasExportedDeps() {
// nothing to do
func (p *prebuiltCommon) provideApexExportsInfo(ctx android.ModuleContext, di *android.DeapexerInfo) {
if di == nil {
return
}
if di, err := android.FindDeapexerProviderForModule(ctx); err == nil {
javaModuleToDexPath := map[string]android.Path{}
for _, commonModule := range di.GetExportedModuleNames() {
if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
javaModuleToDexPath[commonModule] = dex
}
javaModuleToDexPath := map[string]android.Path{}
for _, commonModule := range di.GetExportedModuleNames() {
if dex := di.PrebuiltExportPath(java.ApexRootRelativePathToJavaLib(commonModule)); dex != nil {
javaModuleToDexPath[commonModule] = dex
}
exports := android.ApexExportsInfo{
ApexName: p.ApexVariationName(),
ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex),
LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
}
android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
} else {
ctx.ModuleErrorf(err.Error())
}
exports := android.ApexExportsInfo{
ApexName: p.ApexVariationName(),
ProfilePathOnHost: di.PrebuiltExportPath(java.ProfileInstallPathInApex),
LibraryNameToDexJarPathOnHost: javaModuleToDexPath,
}
android.SetProvider(ctx, android.ApexExportsInfoProvider, exports)
}
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@ -854,7 +651,7 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.apexKeysPath = writeApexKeys(ctx, p)
// TODO(jungjw): Check the key validity.
p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
p.inputApex = android.PathForModuleSrc(ctx, p.properties.prebuiltApexSelector(ctx, ctx.Module()))
p.installDir = android.PathForModuleInstall(ctx, "apex")
p.installFilename = p.InstallFilename()
if !strings.HasSuffix(p.installFilename, imageApexSuffix) {
@ -872,11 +669,13 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
return
}
deapexerInfo := p.getDeapexerInfo(ctx, p.inputApex)
// dexpreopt any system server jars if present
p.dexpreoptSystemServerJars(ctx)
p.dexpreoptSystemServerJars(ctx, deapexerInfo)
// provide info used for generating the boot image
p.provideApexExportsInfo(ctx)
p.provideApexExportsInfo(ctx, deapexerInfo)
p.providePrebuiltInfo(ctx)
@ -894,6 +693,8 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks...)
p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
}
ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
}
func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
@ -910,26 +711,11 @@ type prebuiltApexExtractorModule struct {
extractedApex android.WritablePath
}
func privateApexExtractorModuleFactory() android.Module {
module := &prebuiltApexExtractorModule{}
module.AddProperties(
&module.properties,
)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func (p *prebuiltApexExtractorModule) Srcs() android.Paths {
return android.Paths{p.extractedApex}
}
func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
srcsSupplier := func(ctx android.BaseModuleContext, prebuilt android.Module) []string {
return p.properties.prebuiltSrcs(ctx)
}
// extract registers the build actions to extract an apex from .apks file
// returns the path of the extracted apex
func extract(ctx android.ModuleContext, apexSet android.Path, prerelease *bool) android.Path {
defaultAllowPrerelease := ctx.Config().IsEnvTrue("SOONG_ALLOW_PRERELEASE_APEXES")
apexSet := android.SingleSourcePathFromSupplier(ctx, srcsSupplier, "set")
p.extractedApex = android.PathForModuleOut(ctx, "extracted", apexSet.Base())
extractedApex := android.PathForModuleOut(ctx, "extracted", apexSet.Base())
// Filter out NativeBridge archs (b/260115309)
abis := java.SupportedAbis(ctx, true)
ctx.Build(pctx,
@ -937,14 +723,16 @@ func (p *prebuiltApexExtractorModule) GenerateAndroidBuildActions(ctx android.Mo
Rule: extractMatchingApex,
Description: "Extract an apex from an apex set",
Inputs: android.Paths{apexSet},
Output: p.extractedApex,
Output: extractedApex,
Args: map[string]string{
"abis": strings.Join(abis, ","),
"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(p.properties.Prerelease, defaultAllowPrerelease)),
"allow-prereleased": strconv.FormatBool(proptools.BoolDefault(prerelease, defaultAllowPrerelease)),
"sdk-version": ctx.Config().PlatformSdkVersion().String(),
"skip-sdk-check": strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")),
},
})
},
)
return extractedApex
}
type ApexSet struct {
@ -1009,61 +797,22 @@ func (a *ApexSet) hasSanitizedSource(sanitizer string) bool {
return false
}
func (a *ApexSet) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
return android.Paths{a.outputApex}, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
func apexSetFactory() android.Module {
module := &ApexSet{}
module.AddProperties(&module.properties)
module.initPrebuiltCommon(module, &module.properties.PrebuiltCommonProperties)
module.prebuiltCommon.prebuiltCommonProperties = &module.properties.PrebuiltCommonProperties
// init the module as a prebuilt
// even though this module type has srcs, use `InitPrebuiltModuleWithoutSrcs`, since the existing
// InitPrebuiltModule* are not friendly with Sources of Configurable type.
// The actual src will be evaluated in GenerateAndroidBuildActions.
android.InitPrebuiltModuleWithoutSrcs(module)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
func createApexExtractorModule(ctx android.TopDownMutatorContext, name string, apexExtractorProperties *ApexExtractorProperties) {
props := struct {
Name *string
}{
Name: proptools.StringPtr(name),
}
ctx.CreateModule(privateApexExtractorModuleFactory,
&props,
apexExtractorProperties,
)
}
func apexExtractorModuleName(baseModuleName string) string {
return baseModuleName + ".apex.extractor"
}
var _ prebuiltApexModuleCreator = (*ApexSet)(nil)
// createPrebuiltApexModules creates modules necessary to export files from the apex set to other
// modules.
//
// This effectively does for apex_set what Prebuilt.createPrebuiltApexModules does for a
// prebuilt_apex except that instead of creating a selector module which selects one .apex file
// from those provided this creates an extractor module which extracts the appropriate .apex file
// from the zip file containing them.
func (a *ApexSet) createPrebuiltApexModules(ctx android.TopDownMutatorContext) {
apexExtractorModuleName := apexExtractorModuleName(a.Name())
createApexExtractorModule(ctx, apexExtractorModuleName, &a.properties.ApexExtractorProperties)
apexFileSource := ":" + apexExtractorModuleName
a.createDeapexerModuleIfNeeded(ctx, deapexerModuleName(a.Name()), apexFileSource)
// After passing the arch specific src properties to the creating the apex selector module
a.prebuiltCommonProperties.Selected_apex = proptools.StringPtr(apexFileSource)
}
func (a *ApexSet) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
a.prebuiltApexContentsDeps(ctx)
}
@ -1086,11 +835,25 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
ctx.ModuleErrorf("filename should end in %s or %s for apex_set", imageApexSuffix, imageCapexSuffix)
}
inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path()
var apexSet android.Path
if srcs := a.properties.prebuiltSrcs(ctx); len(srcs) == 1 {
apexSet = android.PathForModuleSrc(ctx, srcs[0])
} else {
ctx.ModuleErrorf("Expected exactly one source apex_set file, found %v\n", srcs)
}
extractedApex := extract(ctx, apexSet, a.properties.Prerelease)
a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
// Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed
buildRule := android.Cp
if !ctx.Config().ApexCompressionEnabled() {
buildRule = decompressApex
}
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: inputApex,
Rule: buildRule,
Input: extractedApex,
Output: a.outputApex,
})
@ -1099,11 +862,13 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
return
}
deapexerInfo := a.getDeapexerInfo(ctx, extractedApex)
// dexpreopt any system server jars if present
a.dexpreoptSystemServerJars(ctx)
a.dexpreoptSystemServerJars(ctx, deapexerInfo)
// provide info used for generating the boot image
a.provideApexExportsInfo(ctx)
a.provideApexExportsInfo(ctx, deapexerInfo)
a.providePrebuiltInfo(ctx)
@ -1121,6 +886,8 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
for _, overridden := range a.prebuiltCommonProperties.Overrides {
a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
}
ctx.SetOutputFiles(android.Paths{a.outputApex}, "")
}
type systemExtContext struct {

View file

@ -80,6 +80,7 @@ func TestSystemserverclasspathFragmentContents(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "core_current",
}
systemserverclasspath_fragment {
@ -276,8 +277,6 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) {
java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
`all_apex_contributions`,
`dex2oatd`,
`prebuilt_myapex.apex.selector`,
`prebuilt_myapex.deapexer`,
`prebuilt_mysystemserverclasspathfragment`,
})
@ -285,10 +284,9 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) {
`all_apex_contributions`,
`prebuilt_bar`,
`prebuilt_foo`,
`prebuilt_myapex.deapexer`,
})
ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
"javalib/foo.jar",
"javalib/bar.jar",
"javalib/bar.jar.prof",
@ -350,6 +348,7 @@ func TestSystemserverclasspathFragmentStandaloneContents(t *testing.T) {
apex_available: [
"myapex",
],
sdk_version: "core_current",
}
systemserverclasspath_fragment {
@ -437,10 +436,9 @@ func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) {
`all_apex_contributions`,
`prebuilt_bar`,
`prebuilt_foo`,
`prebuilt_myapex.deapexer`,
})
ensureExactDeapexedContents(t, ctx, "prebuilt_myapex", "android_common", []string{
ensureExactDeapexedContents(t, ctx, "myapex", "android_common_myapex", []string{
"javalib/foo.jar",
"javalib/bar.jar",
"javalib/bar.jar.prof",

Some files were not shown because too many files have changed in this diff Show more