Merge "Support mechanism to select a specific version of module sdk prebuilt" into main
diff --git a/aconfig/codegen/aconfig_declarations_group.go b/aconfig/codegen/aconfig_declarations_group.go
index 203b6be..1c91bee 100644
--- a/aconfig/codegen/aconfig_declarations_group.go
+++ b/aconfig/codegen/aconfig_declarations_group.go
@@ -15,9 +15,10 @@
package codegen
import (
- "android/soong/aconfig"
- "android/soong/android"
"fmt"
+ "maps"
+
+ "android/soong/android"
"github.com/google/blueprint"
)
@@ -43,6 +44,7 @@
aconfigDeclarationNames []string
intermediateCacheOutputPaths android.Paths
javaSrcjars android.Paths
+ modeInfos map[string]android.ModeInfo
}
type AconfigDeclarationsGroupProperties struct {
@@ -76,9 +78,10 @@
}
func (adg *AconfigDeclarationsGroup) VisitDeps(ctx android.ModuleContext) {
+ adg.modeInfos = make(map[string]android.ModeInfo)
ctx.VisitDirectDeps(func(dep android.Module) {
tag := ctx.OtherModuleDependencyTag(dep)
- if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok {
+ if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok {
// aconfig declaration names and cache files are collected for all aconfig library dependencies
adg.aconfigDeclarationNames = append(adg.aconfigDeclarationNames, provider.AconfigDeclarations...)
@@ -88,8 +91,14 @@
case aconfigDeclarationsGroupTag:
// Will retrieve outputs from another language codegen modules when support is added
adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
+ maps.Copy(adg.modeInfos, provider.ModeInfos)
case javaAconfigLibraryTag:
adg.javaSrcjars = append(adg.javaSrcjars, provider.Srcjars...)
+ maps.Copy(adg.modeInfos, provider.ModeInfos)
+ case ccAconfigLibraryTag:
+ maps.Copy(adg.modeInfos, provider.ModeInfos)
+ case rustAconfigLibraryTag:
+ maps.Copy(adg.modeInfos, provider.ModeInfos)
}
}
})
@@ -100,10 +109,11 @@
adg.aconfigDeclarationNames = android.FirstUniqueStrings(adg.aconfigDeclarationNames)
adg.intermediateCacheOutputPaths = android.FirstUniquePaths(adg.intermediateCacheOutputPaths)
- android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{
+ android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{
AconfigDeclarations: adg.aconfigDeclarationNames,
IntermediateCacheOutputPaths: adg.intermediateCacheOutputPaths,
Srcjars: adg.javaSrcjars,
+ ModeInfos: adg.modeInfos,
})
}
diff --git a/aconfig/codegen/cc_aconfig_library.go b/aconfig/codegen/cc_aconfig_library.go
index 50cd4de..80e4926 100644
--- a/aconfig/codegen/cc_aconfig_library.go
+++ b/aconfig/codegen/cc_aconfig_library.go
@@ -146,4 +146,12 @@
"mode": mode,
},
})
+
+ android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{
+ ModeInfos: map[string]android.ModeInfo{
+ ctx.ModuleName(): {
+ Container: declarations.Container,
+ Mode: mode,
+ }},
+ })
}
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go
index 3d15ac9..1378dfe 100644
--- a/aconfig/codegen/java_aconfig_library.go
+++ b/aconfig/codegen/java_aconfig_library.go
@@ -17,7 +17,6 @@
import (
"fmt"
- "android/soong/aconfig"
"android/soong/android"
"android/soong/java"
@@ -119,10 +118,15 @@
module.AddJarJarRenameRule(declarations.Package+".FakeFeatureFlagsImpl", "")
}
- android.SetProvider(ctx, aconfig.CodegenInfoProvider, aconfig.CodegenInfo{
+ android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{
AconfigDeclarations: []string{declarationsModules[0].Name()},
IntermediateCacheOutputPaths: android.Paths{declarations.IntermediateCacheOutputPath},
Srcjars: android.Paths{srcJarPath},
+ ModeInfos: map[string]android.ModeInfo{
+ ctx.ModuleName(): {
+ Container: declarations.Container,
+ Mode: mode,
+ }},
})
return srcJarPath
diff --git a/aconfig/codegen/rust_aconfig_library.go b/aconfig/codegen/rust_aconfig_library.go
index 2ab54b6..3f7495b 100644
--- a/aconfig/codegen/rust_aconfig_library.go
+++ b/aconfig/codegen/rust_aconfig_library.go
@@ -85,6 +85,15 @@
},
})
a.BaseSourceProvider.OutputFiles = android.Paths{generatedSource}
+
+ android.SetProvider(ctx, android.CodegenInfoProvider, android.CodegenInfo{
+ ModeInfos: map[string]android.ModeInfo{
+ ctx.ModuleName(): {
+ Container: declarations.Container,
+ Mode: mode,
+ }},
+ })
+
return generatedSource
}
diff --git a/aconfig/init.go b/aconfig/init.go
index e64429f..4625128 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -20,20 +20,6 @@
"github.com/google/blueprint"
)
-type CodegenInfo struct {
- // AconfigDeclarations is the name of the aconfig_declarations modules that
- // the codegen module is associated with
- AconfigDeclarations []string
-
- // Paths to the cache files of the associated aconfig_declaration modules
- IntermediateCacheOutputPaths android.Paths
-
- // Paths to the srcjar files generated from the java_aconfig_library modules
- Srcjars android.Paths
-}
-
-var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]()
-
var (
pctx = android.NewPackageContext("android/soong/aconfig")
diff --git a/android/aconfig_providers.go b/android/aconfig_providers.go
index 74c1a5e..fcc57e1 100644
--- a/android/aconfig_providers.go
+++ b/android/aconfig_providers.go
@@ -17,6 +17,7 @@
import (
"fmt"
"io"
+ "maps"
"reflect"
"github.com/google/blueprint"
@@ -50,6 +51,35 @@
var AconfigTransitiveDeclarationsInfoProvider = blueprint.NewProvider[AconfigTransitiveDeclarationsInfo]()
+type ModeInfo struct {
+ Container string
+ Mode string
+}
+type CodegenInfo struct {
+ // AconfigDeclarations is the name of the aconfig_declarations modules that
+ // the codegen module is associated with
+ AconfigDeclarations []string
+
+ // Paths to the cache files of the associated aconfig_declaration modules
+ IntermediateCacheOutputPaths Paths
+
+ // Paths to the srcjar files generated from the java_aconfig_library modules
+ Srcjars Paths
+
+ ModeInfos map[string]ModeInfo
+}
+
+var CodegenInfoProvider = blueprint.NewProvider[CodegenInfo]()
+
+func propagateModeInfos(ctx ModuleContext, module Module, to, from map[string]ModeInfo) {
+ if len(from) > 0 {
+ depTag := ctx.OtherModuleDependencyTag(module)
+ if tag, ok := depTag.(PropagateAconfigValidationDependencyTag); ok && tag.PropagateAconfigValidation() {
+ maps.Copy(to, from)
+ }
+ }
+}
+
// CollectDependencyAconfigFiles is used by some module types to provide finer dependency graphing than
// we can do in ModuleBase.
func CollectDependencyAconfigFiles(ctx ModuleContext, mergedAconfigFiles *map[string]Paths) {
@@ -90,13 +120,40 @@
type aconfigPropagatingDeclarationsInfo struct {
AconfigFiles map[string]Paths
+ ModeInfos map[string]ModeInfo
}
var aconfigPropagatingProviderKey = blueprint.NewProvider[aconfigPropagatingDeclarationsInfo]()
+func VerifyAconfigBuildMode(ctx ModuleContext, container string, module blueprint.Module, asError bool) {
+ if dep, ok := OtherModuleProvider(ctx, module, aconfigPropagatingProviderKey); ok {
+ for k, v := range dep.ModeInfos {
+ msg := fmt.Sprintf("%s/%s depends on %s/%s/%s across containers\n",
+ module.Name(), container, k, v.Container, v.Mode)
+ if v.Container != container && v.Mode != "exported" && v.Mode != "force-read-only" {
+ if asError {
+ ctx.ModuleErrorf(msg)
+ } else {
+ fmt.Printf("WARNING: " + msg)
+ }
+ } else {
+ if !asError {
+ fmt.Printf("PASSED: " + msg)
+ }
+ }
+ }
+ }
+}
+
func aconfigUpdateAndroidBuildActions(ctx ModuleContext) {
mergedAconfigFiles := make(map[string]Paths)
+ mergedModeInfos := make(map[string]ModeInfo)
+
ctx.VisitDirectDepsIgnoreBlueprint(func(module Module) {
+ if aconfig_dep, ok := OtherModuleProvider(ctx, module, CodegenInfoProvider); ok && len(aconfig_dep.ModeInfos) > 0 {
+ maps.Copy(mergedModeInfos, aconfig_dep.ModeInfos)
+ }
+
// If any of our dependencies have aconfig declarations (directly or propagated), then merge those and provide them.
if dep, ok := OtherModuleProvider(ctx, module, AconfigDeclarationsProviderKey); ok {
mergedAconfigFiles[dep.Container] = append(mergedAconfigFiles[dep.Container], dep.IntermediateCacheOutputPath)
@@ -105,6 +162,7 @@
for container, v := range dep.AconfigFiles {
mergedAconfigFiles[container] = append(mergedAconfigFiles[container], v...)
}
+ propagateModeInfos(ctx, module, mergedModeInfos, dep.ModeInfos)
}
if dep, ok := OtherModuleProvider(ctx, module, AconfigTransitiveDeclarationsInfoProvider); ok {
for container, v := range dep.AconfigFiles {
@@ -120,6 +178,7 @@
SetProvider(ctx, aconfigPropagatingProviderKey, aconfigPropagatingDeclarationsInfo{
AconfigFiles: mergedAconfigFiles,
+ ModeInfos: mergedModeInfos,
})
}
}
diff --git a/android/apex.go b/android/apex.go
index 4d36a93..8759905 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -976,3 +976,18 @@
// Map from the apex library name (without prebuilt_ prefix) to the dex file path on host
LibraryNameToDexJarPathOnHost map[string]Path
}
+
+var PrebuiltInfoProvider = blueprint.NewProvider[PrebuiltInfo]()
+
+// contents of prebuilt_info.json
+type PrebuiltInfo struct {
+ // Name of the apex, without the prebuilt_ prefix
+ Name string
+
+ Is_prebuilt bool
+
+ // This is relative to root of the workspace.
+ // In case of mainline modules, this file contains the build_id that was used
+ // to generate the mainline module prebuilt.
+ Prebuilt_info_file_path string `json:",omitempty"`
+}
diff --git a/android/base_module_context.go b/android/base_module_context.go
index b9c1153..dd38a4e 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -16,9 +16,11 @@
import (
"fmt"
- "github.com/google/blueprint"
"regexp"
"strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/parser"
)
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -214,6 +216,10 @@
// getMissingDependencies returns the list of missing dependencies.
// Calling this function prevents adding new dependencies.
getMissingDependencies() []string
+
+ // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
+ // can be used to evaluate the final value of Configurable properties.
+ EvaluateConfiguration(parser.SelectType, string) (string, bool)
}
type baseModuleContext struct {
@@ -564,3 +570,32 @@
}
return sb.String()
}
+
+func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
+ switch ty {
+ case parser.SelectTypeReleaseVariable:
+ if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
+ return v, true
+ }
+ return "", false
+ case parser.SelectTypeProductVariable:
+ // TODO(b/323382414): Might add these on a case-by-case basis
+ m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
+ return "", false
+ case parser.SelectTypeSoongConfigVariable:
+ parts := strings.Split(condition, ":")
+ namespace := parts[0]
+ variable := parts[1]
+ if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
+ if v, ok := n[variable]; ok {
+ return v, true
+ }
+ }
+ return "", false
+ case parser.SelectTypeVariant:
+ m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
+ return "", false
+ default:
+ panic("Should be unreachable")
+ }
+}
diff --git a/android/config.go b/android/config.go
index 1bb1a22..567ebd8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -18,7 +18,6 @@
// product variables necessary for soong_build's operation.
import (
- "android/soong/shared"
"encoding/json"
"fmt"
"os"
@@ -30,6 +29,8 @@
"sync"
"unicode"
+ "android/soong/shared"
+
"github.com/google/blueprint"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/pathtools"
@@ -1938,6 +1939,10 @@
return c.config.productVariables.GenerateAidlNdkPlatformBackend
}
+func (c *deviceConfig) AconfigContainerValidation() string {
+ return c.config.productVariables.AconfigContainerValidation
+}
+
func (c *config) IgnorePrefer32OnDevice() bool {
return c.productVariables.IgnorePrefer32OnDevice
}
diff --git a/android/deptag.go b/android/deptag.go
index a15443b..c7ba4d3 100644
--- a/android/deptag.go
+++ b/android/deptag.go
@@ -43,3 +43,15 @@
}
return false
}
+
+type PropagateAconfigValidationDependencyTag interface {
+ PropagateAconfigValidation() bool
+}
+
+type AlwaysPropagateAconfigValidationDependencyTag struct{}
+
+func (p AlwaysPropagateAconfigValidationDependencyTag) PropagateAconfigValidation() bool {
+ return true
+}
+
+var _ PropagateAconfigValidationDependencyTag = AlwaysPropagateAconfigValidationDependencyTag{}
diff --git a/android/module.go b/android/module.go
index b615ff5..000476c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,7 +15,6 @@
package android
import (
- "android/soong/bazel"
"crypto/md5"
"encoding/hex"
"encoding/json"
@@ -27,6 +26,8 @@
"sort"
"strings"
+ "android/soong/bazel"
+
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@@ -2087,6 +2088,7 @@
// caused by prebuilt_ prefix, or fully qualified module names.
type sourceOrOutputDependencyTag struct {
blueprint.BaseDependencyTag
+ AlwaysPropagateAconfigValidationDependencyTag
// The name of the module.
moduleName string
diff --git a/android/module_context.go b/android/module_context.go
index 3fc5d01..1cab630 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -21,7 +21,6 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
)
@@ -213,10 +212,6 @@
// GenerateAndroidBuildActions. If it is called then the struct will be written out and included in
// the module-info.json generated by Make, and Make will not generate its own data for this module.
ModuleInfoJSON() *ModuleInfoJSON
-
- // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
- // can be used to evaluate the final value of Configurable properties.
- EvaluateConfiguration(parser.SelectType, string) (string, bool)
}
type moduleContext struct {
@@ -719,32 +714,3 @@
func (m *moduleContext) TargetRequiredModuleNames() []string {
return m.module.TargetRequiredModuleNames()
}
-
-func (m *moduleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
- switch ty {
- case parser.SelectTypeReleaseVariable:
- if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
- return v, true
- }
- return "", false
- case parser.SelectTypeProductVariable:
- // TODO: Might add these on a case-by-case basis
- m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
- return "", false
- case parser.SelectTypeSoongConfigVariable:
- parts := strings.Split(condition, ":")
- namespace := parts[0]
- variable := parts[1]
- if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
- if v, ok := n[variable]; ok {
- return v, true
- }
- }
- return "", false
- case parser.SelectTypeVariant:
- m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
- return "", false
- default:
- panic("Should be unreachable")
- }
-}
diff --git a/android/path_properties.go b/android/path_properties.go
index bbfaa8c..ea92565 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -47,7 +47,7 @@
// tagged with `android:"path"`.
var pathProperties []string
for _, ps := range props {
- pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
}
// Remove duplicates to avoid multiple dependencies.
@@ -64,7 +64,7 @@
// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
// android:"path" to extract all their values from a property struct, returning them as a single
// slice of strings.
-func pathPropertiesForPropertyStruct(ps interface{}) []string {
+func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
v := reflect.ValueOf(ps)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
@@ -106,6 +106,16 @@
ret = append(ret, sv.String())
case reflect.Slice:
ret = append(ret, sv.Interface().([]string)...)
+ case reflect.Struct:
+ intf := sv.Interface()
+ if configurable, ok := intf.(proptools.Configurable[string]); ok {
+ ret = append(ret, proptools.String(configurable.Evaluate(ctx)))
+ } else if configurable, ok := intf.(proptools.Configurable[[]string]); ok {
+ ret = append(ret, proptools.Slice(configurable.Evaluate(ctx))...)
+ } else {
+ panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
+ v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+ }
default:
panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
diff --git a/android/selects_test.go b/android/selects_test.go
index dca3789..aa9c521 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -80,6 +80,36 @@
},
},
{
+ name: "basic paths",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
+ "a": ["foo.txt"],
+ "b": ["bar.txt"],
+ _: ["baz.txt"],
+ }),
+ }
+ `,
+ provider: selectsTestProvider{
+ my_paths: &[]string{"baz.txt"},
+ },
+ },
+ {
+ name: "paths with module references",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
+ "a": [":a"],
+ "b": [":b"],
+ _: [":c"],
+ }),
+ }
+ `,
+ expectedError: `"foo" depends on undefined module "c"`,
+ },
+ {
name: "Differing types",
bp: `
my_module_type {
@@ -233,6 +263,7 @@
my_bool *bool
my_string *string
my_string_list *[]string
+ my_paths *[]string
}
func (p *selectsTestProvider) String() string {
@@ -248,7 +279,8 @@
my_bool: %v,
my_string: %s,
my_string_list: %s,
-}`, myBoolStr, myStringStr, p.my_string_list)
+ my_paths: %s,
+}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths)
}
var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
@@ -257,6 +289,7 @@
My_bool proptools.Configurable[bool]
My_string proptools.Configurable[string]
My_string_list proptools.Configurable[[]string]
+ My_paths proptools.Configurable[[]string] `android:"path"`
}
type selectsMockModule struct {
@@ -266,10 +299,11 @@
}
func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- SetProvider[selectsTestProvider](ctx, selectsTestProviderKey, selectsTestProvider{
+ SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
my_bool: p.properties.My_bool.Evaluate(ctx),
my_string: p.properties.My_string.Evaluate(ctx),
my_string_list: p.properties.My_string_list.Evaluate(ctx),
+ my_paths: p.properties.My_paths.Evaluate(ctx),
})
}
diff --git a/android/variable.go b/android/variable.go
index be3c80d..73f5bfd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -500,6 +500,8 @@
HiddenapiExportableStubs *bool `json:",omitempty"`
ExportRuntimeApis *bool `json:",omitempty"`
+
+ AconfigContainerValidation string `json:",omitempty"`
}
type PartitionQualifiedVariablesType struct {
diff --git a/android/visibility.go b/android/visibility.go
index b387562..79a534f 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -303,7 +303,10 @@
if pkg == "visibility" {
switch name {
- case "private", "public", "any_partition":
+ case "private", "public":
+ case "any_partition":
+ // any_partition can be used with another visibility fields
+ continue
case "legacy_public":
ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
continue
diff --git a/android/visibility_test.go b/android/visibility_test.go
index d4add7d..bb43b1f 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1921,6 +1921,26 @@
},
},
{
+ name: "any_partition visibility works with the other visibility",
+ fs: MockFS{
+ "top/Android.bp": []byte(`
+ android_filesystem {
+ name: "foo",
+ deps: ["bar"],
+ }`),
+ "top2/Android.bp": []byte(``),
+ "top/nested/Android.bp": []byte(`
+ package(default_visibility=["//visibility:private"])
+ mock_library {
+ name: "bar",
+ visibility: [
+ "//top2",
+ "//visibility:any_partition"
+ ],
+ }`),
+ },
+ },
+ {
name: "any_partition visibility doesn't work for non-partitions",
fs: MockFS{
"top/Android.bp": []byte(`
diff --git a/apex/aconfig_test.go b/apex/aconfig_test.go
new file mode 100644
index 0000000..be98d45
--- /dev/null
+++ b/apex/aconfig_test.go
@@ -0,0 +1,550 @@
+// 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 (
+ "testing"
+
+ "android/soong/aconfig/codegen"
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/genrule"
+ "android/soong/java"
+ "android/soong/rust"
+ "github.com/google/blueprint/proptools"
+)
+
+var withAconfigValidationError = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.AconfigContainerValidation = "error"
+ variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
+})
+
+func TestValidationAcrossContainersExportedPass(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+ }{
+ {
+ name: "Java lib passes for exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ java_libs: [
+ "my_java_library_foo",
+ ],
+ updatable: false,
+ }
+ java_library {
+ name: "my_java_library_foo",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: ["my_java_aconfig_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["foo.aconfig"],
+ exportable: true,
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ mode: "exported",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ },
+ {
+ name: "Android app passes for exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ apps: [
+ "my_android_app_foo",
+ ],
+ updatable: false,
+ }
+ android_app {
+ name: "my_android_app_foo",
+ srcs: ["foo/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ stl: "none",
+ static_libs: ["my_java_library_bar"],
+ apex_available: [ "myapex" ],
+ }
+ java_library {
+ name: "my_java_library_bar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: ["my_java_aconfig_library_bar"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_bar",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["bar.aconfig"],
+ exportable: true,
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_bar",
+ aconfig_declarations: "my_aconfig_declarations_bar",
+ mode: "exported",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ },
+ {
+ name: "Cc lib passes for exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ native_shared_libs: [
+ "my_cc_library_bar",
+ ],
+ binaries: [
+ "my_cc_binary_baz",
+ ],
+ updatable: false,
+ }
+ cc_library {
+ name: "my_cc_library_bar",
+ srcs: ["foo/bar/MyClass.cc"],
+ static_libs: [
+ "my_cc_aconfig_library_bar",
+ "my_cc_aconfig_library_baz",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ cc_binary {
+ name: "my_cc_binary_baz",
+ srcs: ["foo/bar/MyClass.cc"],
+ static_libs: ["my_cc_aconfig_library_baz"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ cc_library {
+ name: "server_configurable_flags",
+ srcs: ["server_configurable_flags.cc"],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_bar",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["bar.aconfig"],
+ exportable: true,
+ }
+ cc_aconfig_library {
+ name: "my_cc_aconfig_library_bar",
+ aconfig_declarations: "my_aconfig_declarations_bar",
+ apex_available: [
+ "myapex",
+ ],
+ mode: "exported",
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_baz",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["baz.aconfig"],
+ exportable: true,
+ }
+ cc_aconfig_library {
+ name: "my_cc_aconfig_library_baz",
+ aconfig_declarations: "my_aconfig_declarations_baz",
+ apex_available: [
+ "myapex",
+ ],
+ mode: "exported",
+ }`,
+ },
+ }
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcBuildComponents,
+ rust.PrepareForTestWithRustDefaultModules,
+ codegen.PrepareForTestWithAconfigBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ withAconfigValidationError,
+ ).
+ RunTestWithBp(t, test.bp)
+ })
+ }
+}
+
+func TestValidationAcrossContainersNotExportedFail(t *testing.T) {
+ testCases := []struct {
+ name string
+ expectedError string
+ bp string
+ }{
+ {
+ name: "Java lib fails for non-exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ java_libs: [
+ "my_java_library_foo",
+ ],
+ updatable: false,
+ }
+ java_library {
+ name: "my_java_library_foo",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: ["my_java_aconfig_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["foo.aconfig"],
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ expectedError: `.*my_java_library_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
+ },
+ {
+ name: "Android app fails for non-exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ apps: [
+ "my_android_app_foo",
+ ],
+ updatable: false,
+ }
+ android_app {
+ name: "my_android_app_foo",
+ srcs: ["foo/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ stl: "none",
+ static_libs: ["my_java_library_foo"],
+ apex_available: [ "myapex" ],
+ }
+ java_library {
+ name: "my_java_library_foo",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: ["my_java_aconfig_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["bar.aconfig"],
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
+ },
+ {
+ name: "Cc lib fails for non-exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ native_shared_libs: [
+ "my_cc_library_foo",
+ ],
+ updatable: false,
+ }
+ cc_library {
+ name: "my_cc_library_foo",
+ srcs: ["foo/bar/MyClass.cc"],
+ shared_libs: [
+ "my_cc_aconfig_library_foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ cc_library {
+ name: "server_configurable_flags",
+ srcs: ["server_configurable_flags.cc"],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["foo.aconfig"],
+ }
+ cc_aconfig_library {
+ name: "my_cc_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ expectedError: `.*my_cc_library_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`,
+ },
+ {
+ name: "Cc binary fails for non-exported containers cross",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ binaries: [
+ "my_cc_binary_foo",
+ ],
+ updatable: false,
+ }
+ cc_library {
+ name: "my_cc_library_foo",
+ srcs: ["foo/bar/MyClass.cc"],
+ static_libs: [
+ "my_cc_aconfig_library_foo",
+ ],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ cc_binary {
+ name: "my_cc_binary_foo",
+ srcs: ["foo/bar/MyClass.cc"],
+ static_libs: ["my_cc_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ cc_library {
+ name: "server_configurable_flags",
+ srcs: ["server_configurable_flags.cc"],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["foo.aconfig"],
+ }
+ cc_aconfig_library {
+ name: "my_cc_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ expectedError: `.*my_cc_binary_foo/myapex depends on my_cc_aconfig_library_foo/otherapex/production across containers`,
+ },
+ {
+ name: "Aconfig validation propagate along sourceOrOutputDependencyTag",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ apps: [
+ "my_android_app_foo",
+ ],
+ updatable: false,
+ }
+ android_app {
+ name: "my_android_app_foo",
+ srcs: ["foo/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ stl: "none",
+ static_libs: ["my_java_library_foo"],
+ apex_available: [ "myapex" ],
+ }
+ java_library {
+ name: "my_java_library_foo",
+ srcs: [":my_genrule_foo"],
+ sdk_version: "none",
+ system_modules: "none",
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations_group {
+ name: "my_aconfig_declarations_group_foo",
+ java_aconfig_libraries: [
+ "my_java_aconfig_library_foo",
+ ],
+ }
+ filegroup {
+ name: "my_filegroup_foo_srcjars",
+ srcs: [
+ ":my_aconfig_declarations_group_foo{.srcjars}",
+ ],
+ }
+ genrule {
+ name: "my_genrule_foo",
+ srcs: [":my_filegroup_foo_srcjars"],
+ cmd: "cp $(in) $(out)",
+ out: ["my_genrule_foo.srcjar"],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["bar.aconfig"],
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ expectedError: `.*my_android_app_foo/myapex depends on my_java_aconfig_library_foo/otherapex/production across containers`,
+ },
+ }
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ errorHandler := android.FixtureExpectsNoErrors
+ if test.expectedError != "" {
+ errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
+ }
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcBuildComponents,
+ rust.PrepareForTestWithRustDefaultModules,
+ codegen.PrepareForTestWithAconfigBuildComponents,
+ genrule.PrepareForIntegrationTestWithGenrule,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ withAconfigValidationError,
+ ).
+ ExtendWithErrorHandler(errorHandler).
+ RunTestWithBp(t, test.bp)
+ })
+ }
+}
+
+func TestValidationNotPropagateAcrossShared(t *testing.T) {
+ testCases := []struct {
+ name string
+ bp string
+ }{
+ {
+ name: "Java shared lib not propagate aconfig validation",
+ bp: apex_default_bp + `
+ apex {
+ name: "myapex",
+ manifest: ":myapex.manifest",
+ androidManifest: ":myapex.androidmanifest",
+ key: "myapex.key",
+ java_libs: [
+ "my_java_library_bar",
+ ],
+ updatable: false,
+ }
+ java_library {
+ name: "my_java_library_bar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ libs: ["my_java_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ java_library {
+ name: "my_java_library_foo",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ static_libs: ["my_java_aconfig_library_foo"],
+ apex_available: [
+ "myapex",
+ ],
+ }
+ aconfig_declarations {
+ name: "my_aconfig_declarations_foo",
+ package: "com.example.package",
+ container: "otherapex",
+ srcs: ["foo.aconfig"],
+ }
+ java_aconfig_library {
+ name: "my_java_aconfig_library_foo",
+ aconfig_declarations: "my_aconfig_declarations_foo",
+ apex_available: [
+ "myapex",
+ ],
+ }`,
+ },
+ }
+ for _, test := range testCases {
+ t.Run(test.name, func(t *testing.T) {
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ cc.PrepareForTestWithCcBuildComponents,
+ rust.PrepareForTestWithRustDefaultModules,
+ codegen.PrepareForTestWithAconfigBuildComponents,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ withAconfigValidationError,
+ ).
+ RunTestWithBp(t, test.bp)
+ })
+ }
+}
diff --git a/apex/apex.go b/apex/apex.go
index c688438..32a3638 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2321,9 +2321,15 @@
}
func addAconfigFiles(vctx *visitorContext, ctx android.ModuleContext, module blueprint.Module) {
- dep, _ := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider)
- if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
- vctx.aconfigFiles = append(vctx.aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
+ if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigTransitiveDeclarationsInfoProvider); 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")
}
}
@@ -2428,29 +2434,14 @@
a.providePrebuiltInfo(ctx)
}
-var prebuiltInfoProvider = blueprint.NewProvider[prebuiltInfo]()
-
-// contents of prebuilt_info.json
-type prebuiltInfo struct {
- // Name of the apex, without the prebuilt_ prefix
- Name string
-
- Is_prebuilt bool
-
- // This is relative to root of the workspace.
- // In case of mainline modules, this file contains the build_id that was used
- // to generate the mainline module prebuilt.
- Prebuilt_info_file_path string `json:",omitempty"`
-}
-
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
// with information about whether source or prebuilt of an apex was used during the build.
func (a *apexBundle) providePrebuiltInfo(ctx android.ModuleContext) {
- info := prebuiltInfo{
+ info := android.PrebuiltInfo{
Name: a.Name(),
Is_prebuilt: false,
}
- android.SetProvider(ctx, prebuiltInfoProvider, info)
+ android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
}
// Set a provider containing information about the jars and .prof provided by the apex
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 8aaddbe..e6ebff2 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -149,10 +149,10 @@
}
func (a *apexPrebuiltInfo) GenerateBuildActions(ctx android.SingletonContext) {
- prebuiltInfos := []prebuiltInfo{}
+ prebuiltInfos := []android.PrebuiltInfo{}
ctx.VisitAllModules(func(m android.Module) {
- prebuiltInfo, exists := android.SingletonModuleProvider(ctx, m, prebuiltInfoProvider)
+ prebuiltInfo, exists := android.SingletonModuleProvider(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() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 54d2d08..add6083 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -2068,7 +2068,7 @@
// Ensure that mylib links with "current" LLNDK
libFlags := names(mylib.Rule("ld").Args["libFlags"])
- ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared_current/libbar.so")
+ ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared/libbar.so")
// Ensure that mylib is targeting 29
ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o")
@@ -11022,7 +11022,7 @@
}
`)
- inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+ inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits
android.AssertStringListDoesNotContain(t, "filesystem should not have libbar",
inputs.Strings(),
"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 34dfc46..ea847e1 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -827,15 +827,15 @@
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
// with information about whether source or prebuilt of an apex was used during the build.
func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) {
- info := prebuiltInfo{
- Name: p.BaseModuleName(), // BaseModuleName ensures that this will not contain the prebuilt_ prefix.
+ info := android.PrebuiltInfo{
+ Name: p.BaseModuleName(),
Is_prebuilt: true,
}
// If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json.
if p.prebuiltCommonProperties.Prebuilt_info != nil {
info.Prebuilt_info_file_path = android.PathForModuleSrc(ctx, *p.prebuiltCommonProperties.Prebuilt_info).String()
}
- android.SetProvider(ctx, prebuiltInfoProvider, info)
+ android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/cc/cc.go b/cc/cc.go
index 2770fb2..0fa3457 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -766,6 +766,12 @@
var _ android.InstallNeededDependencyTag = libraryDependencyTag{}
+func (d libraryDependencyTag) PropagateAconfigValidation() bool {
+ return d.static()
+}
+
+var _ android.PropagateAconfigValidationDependencyTag = libraryDependencyTag{}
+
// dependencyTag is used for tagging miscellaneous dependency types that don't fit into
// libraryDependencyTag. Each tag object is created globally and reused for multiple
// dependencies (although since the object contains no references, assigning a tag to a
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 6cc500b..d1b728e 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2680,15 +2680,13 @@
}
}
expected := []string{
- "android_vendor.29_arm64_armv8-a_shared_current",
"android_vendor.29_arm64_armv8-a_shared",
- "android_vendor.29_arm_armv7-a-neon_shared_current",
"android_vendor.29_arm_armv7-a-neon_shared",
}
android.AssertArrayString(t, "variants for llndk stubs", expected, actual)
params := result.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub")
- android.AssertSame(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
+ android.AssertSame(t, "use Vendor API level for default stubs", "202404", params.Args["apiLevel"])
checkExportedIncludeDirs := func(module, variant string, expectedDirs ...string) {
t.Helper()
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 82beb29..0de9e05 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -51,6 +51,7 @@
arm64Ldflags = []string{
"-Wl,--hash-style=gnu",
"-Wl,-z,separate-code",
+ "-Wl,-z,separate-loadable-segments",
}
arm64Lldflags = arm64Ldflags
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index b97d511..12119a7 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -31,6 +31,7 @@
x86_64Ldflags = []string{
"-Wl,--hash-style=gnu",
+ "-Wl,-z,separate-loadable-segments",
}
X86_64Lldflags = x86_64Ldflags
diff --git a/cc/library.go b/cc/library.go
index bbff49a..5607632 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -677,18 +677,16 @@
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
if ctx.IsLlndk() {
+ vendorApiLevel := ctx.Config().VendorApiLevel()
+ if vendorApiLevel == "" {
+ // TODO(b/321892570): Some tests relying on old fixtures which
+ // doesn't set vendorApiLevel. Needs to fix them.
+ vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
+ }
// This is the vendor variant of an LLNDK library, build the LLNDK stubs.
- vndkVer := ctx.Module().(*Module).VndkVersion()
- if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
- // For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
- vndkVer = "current"
- }
- if library.stubsVersion() != "" {
- vndkVer = library.stubsVersion()
- }
nativeAbiResult := parseNativeAbiDefinition(ctx,
String(library.Properties.Llndk.Symbol_file),
- android.ApiLevelOrPanic(ctx, vndkVer), "--llndk")
+ android.ApiLevelOrPanic(ctx, vendorApiLevel), "--llndk")
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
if !Bool(library.Properties.Llndk.Unversioned) {
library.versionScriptPath = android.OptionalPathForPath(
@@ -1917,12 +1915,15 @@
}
if library.hasLLNDKStubs() && ctx.Module().(*Module).InVendorOrProduct() {
- // LLNDK libraries only need a single stubs variant.
- return []string{android.FutureApiLevel.String()}
+ // LLNDK libraries only need a single stubs variant (""), which is
+ // added automatically in createVersionVariations().
+ return nil
}
// Future API level is implicitly added if there isn't
- return addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+ versions := addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+ normalizeVersions(ctx, versions)
+ return versions
}
func addCurrentVersionIfNotPresent(vers []string) []string {
@@ -2294,10 +2295,6 @@
return
}
versions := library.stubsVersions(mctx)
- if len(versions) <= 0 {
- return
- }
- normalizeVersions(mctx, versions)
if mctx.Failed() {
return
}
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 1e0bdf3..22f31d9 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -463,6 +463,98 @@
""")
self.assertEqual(expected_version, version_file.getvalue())
+ def test_integration_with_llndk(self) -> None:
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_34 { # introduced=34
+ global:
+ foo;
+ bar; # llndk
+ };
+ VERSION_35 { # introduced=35
+ global:
+ wiggle;
+ waggle;
+ waggle; # llndk=202404
+ bubble; # llndk=202404
+ duddle;
+ duddle; # llndk=202504
+ } VERSION_34;
+ """))
+ f = copy(self.filter)
+ f.llndk = True
+ f.api = 202404
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ symbol_list_file = io.StringIO()
+
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file, f)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ void foo() {}
+ void bar() {}
+ void waggle() {}
+ void bubble() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ VERSION_34 {
+ global:
+ foo;
+ bar;
+ };
+ VERSION_35 {
+ global:
+ waggle;
+ bubble;
+ } VERSION_34;
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
+ def test_integration_with_llndk_with_single_version_block(self) -> None:
+ input_file = io.StringIO(textwrap.dedent("""\
+ LIBANDROID {
+ global:
+ foo; # introduced=34
+ bar; # introduced=35
+ bar; # llndk=202404
+ baz; # introduced=35
+ };
+ """))
+ f = copy(self.filter)
+ f.llndk = True
+ f.api = 202404
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
+ versions = parser.parse()
+
+ src_file = io.StringIO()
+ version_file = io.StringIO()
+ symbol_list_file = io.StringIO()
+
+ generator = ndkstubgen.Generator(src_file,
+ version_file, symbol_list_file, f)
+ generator.write(versions)
+
+ expected_src = textwrap.dedent("""\
+ void foo() {}
+ void bar() {}
+ """)
+ self.assertEqual(expected_src, src_file.getvalue())
+
+ expected_version = textwrap.dedent("""\
+ LIBANDROID {
+ global:
+ foo;
+ bar;
+ };
+ """)
+ self.assertEqual(expected_version, version_file.getvalue())
+
def test_empty_stub(self) -> None:
"""Tests that empty stubs can be generated.
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 345e9f9..4553616 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -103,13 +103,24 @@
@property
def has_llndk_tags(self) -> bool:
"""Returns True if any LL-NDK tags are set."""
- return 'llndk' in self.tags
+ for tag in self.tags:
+ if tag == 'llndk' or tag.startswith('llndk='):
+ return True
+ return False
@property
def has_platform_only_tags(self) -> bool:
"""Returns True if any platform-only tags are set."""
return 'platform-only' in self.tags
+ def copy_introduced_from(self, tags: Tags) -> None:
+ """Copies introduced= or introduced-*= tags."""
+ for tag in tags:
+ if tag.startswith('introduced=') or tag.startswith('introduced-'):
+ name, _ = split_tag(tag)
+ if not any(self_tag.startswith(name + '=') for self_tag in self.tags):
+ self.tags += (tag,)
+
@dataclass
class Symbol:
@@ -147,6 +158,8 @@
"""Returns true if this tag has an API level that may need decoding."""
if tag.startswith('llndk-deprecated='):
return True
+ if tag.startswith('llndk='):
+ return True
if tag.startswith('introduced='):
return True
if tag.startswith('introduced-'):
@@ -237,15 +250,22 @@
This defines the rules shared between version tagging and symbol tagging.
"""
- # The apex and llndk tags will only exclude APIs from other modes. If in
+ # LLNDK mode/tags follow the similar filtering except that API level checking
+ # is based llndk= instead of introduced=.
+ if self.llndk:
+ if tags.has_mode_tags and not tags.has_llndk_tags:
+ return True
+ if not symbol_in_arch(tags, self.arch):
+ return True
+ if not symbol_in_llndk_api(tags, self.arch, self.api):
+ return True
+ return False
# APEX or LLNDK mode and neither tag is provided, we fall back to the
# default behavior because all NDK symbols are implicitly available to
# APEX and LLNDK.
if tags.has_mode_tags:
if self.apex and tags.has_apex_tags:
return False
- if self.llndk and tags.has_llndk_tags:
- return False
if self.systemapi and tags.has_systemapi_tags:
return False
return True
@@ -266,6 +286,10 @@
return True
if version.tags.has_platform_only_tags:
return True
+ # Include all versions when targeting LLNDK because LLNDK symbols are self-versioned.
+ # Empty version block will be handled separately.
+ if self.llndk:
+ return False
return self._should_omit_tags(version.tags)
def should_omit_symbol(self, symbol: Symbol) -> bool:
@@ -292,6 +316,14 @@
# for the tagged architectures.
return not has_arch_tags
+def symbol_in_llndk_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
+ """Returns true if the symbol is present for the given LLNDK API level."""
+ # Check llndk= first.
+ for tag in tags:
+ if tag.startswith('llndk='):
+ return api >= int(get_tag_value(tag))
+ # If not, we keep old behavior: NDK symbols in <= 34 are LLNDK symbols.
+ return symbol_in_api(tags, arch, 34)
def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
"""Returns true if the symbol is present for the given API level."""
@@ -368,6 +400,7 @@
f'Unexpected contents at top level: {self.current_line}')
self.check_no_duplicate_symbols(versions)
+ self.check_llndk_introduced(versions)
return versions
def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None:
@@ -396,6 +429,31 @@
raise MultiplyDefinedSymbolError(
sorted(list(multiply_defined_symbols)))
+ def check_llndk_introduced(self, versions: Iterable[Version]) -> None:
+ """Raises errors when llndk= is missing for new llndk symbols."""
+ if not self.filter.llndk:
+ return
+
+ def assert_llndk_with_version(tags: Tags, name: str) -> None:
+ has_llndk_introduced = False
+ for tag in tags:
+ if tag.startswith('llndk='):
+ has_llndk_introduced = True
+ break
+ if not has_llndk_introduced:
+ raise ParseError(f'{name}: missing version. `llndk=yyyymm`')
+
+ arch = self.filter.arch
+ for version in versions:
+ # llndk symbols >= introduced=35 should be tagged
+ # explicitly with llndk=yyyymm.
+ for symbol in version.symbols:
+ if not symbol.tags.has_llndk_tags:
+ continue
+ if symbol_in_api(symbol.tags, arch, 34):
+ continue
+ assert_llndk_with_version(symbol.tags, symbol.name)
+
def parse_version(self) -> Version:
"""Parses a single version section and returns a Version object."""
assert self.current_line is not None
@@ -429,7 +487,9 @@
else:
raise ParseError('Unknown visiblity label: ' + visibility)
elif global_scope and not cpp_symbols:
- symbols.append(self.parse_symbol())
+ symbol = self.parse_symbol()
+ symbol.tags.copy_introduced_from(tags)
+ symbols.append(symbol)
else:
# We're in a hidden scope or in 'extern "C++"' block. Ignore
# everything.
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 83becc2..8b412b9 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -344,6 +344,45 @@
self.assertInclude(f_llndk, s_none)
self.assertInclude(f_llndk, s_llndk)
+ def test_omit_llndk_versioned(self) -> None:
+ f_ndk = self.filter
+ f_ndk.api = 35
+
+ f_llndk = copy(f_ndk)
+ f_llndk.llndk = True
+ f_llndk.api = 202404
+
+ s = Symbol('foo', Tags())
+ s_llndk = Symbol('foo', Tags.from_strs(['llndk']))
+ s_llndk_202404 = Symbol('foo', Tags.from_strs(['llndk=202404']))
+ s_34 = Symbol('foo', Tags.from_strs(['introduced=34']))
+ s_34_llndk = Symbol('foo', Tags.from_strs(['introduced=34', 'llndk']))
+ s_35 = Symbol('foo', Tags.from_strs(['introduced=35']))
+ s_35_llndk_202404 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202404']))
+ s_35_llndk_202504 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202504']))
+
+ # When targeting NDK, omit LLNDK tags
+ self.assertInclude(f_ndk, s)
+ self.assertOmit(f_ndk, s_llndk)
+ self.assertOmit(f_ndk, s_llndk_202404)
+ self.assertInclude(f_ndk, s_34)
+ self.assertOmit(f_ndk, s_34_llndk)
+ self.assertInclude(f_ndk, s_35)
+ self.assertOmit(f_ndk, s_35_llndk_202404)
+ self.assertOmit(f_ndk, s_35_llndk_202504)
+
+ # When targeting LLNDK, old symbols without any mode tags are included as LLNDK
+ self.assertInclude(f_llndk, s)
+ # When targeting LLNDK, old symbols with #llndk are included as LLNDK
+ self.assertInclude(f_llndk, s_llndk)
+ self.assertInclude(f_llndk, s_llndk_202404)
+ self.assertInclude(f_llndk, s_34)
+ self.assertInclude(f_llndk, s_34_llndk)
+ # When targeting LLNDK, new symbols(>=35) should be tagged with llndk-introduced=.
+ self.assertOmit(f_llndk, s_35)
+ self.assertInclude(f_llndk, s_35_llndk_202404)
+ self.assertOmit(f_llndk, s_35_llndk_202504)
+
def test_omit_apex(self) -> None:
f_none = self.filter
f_apex = copy(f_none)
@@ -451,9 +490,12 @@
self.assertIsNone(version.base)
self.assertEqual(Tags.from_strs(['weak', 'introduced=35']), version.tags)
+ # Inherit introduced= tags from version block so that
+ # should_omit_tags() can differently based on introduced API level when treating
+ # LLNDK-available symbols.
expected_symbols = [
- Symbol('baz', Tags()),
- Symbol('qux', Tags.from_strs(['apex', 'llndk'])),
+ Symbol('baz', Tags.from_strs(['introduced=35'])),
+ Symbol('qux', Tags.from_strs(['apex', 'llndk', 'introduced=35'])),
]
self.assertEqual(expected_symbols, version.symbols)
@@ -601,6 +643,19 @@
]
self.assertEqual(expected_symbols, version.symbols)
+ def test_parse_llndk_version_is_missing(self) -> None:
+ input_file = io.StringIO(textwrap.dedent("""\
+ VERSION_1 { # introduced=35
+ foo;
+ bar; # llndk
+ };
+ """))
+ f = copy(self.filter)
+ f.llndk = True
+ parser = symbolfile.SymbolFileParser(input_file, {}, f)
+ with self.assertRaises(symbolfile.ParseError):
+ parser.parse()
+
def main() -> None:
suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 795a0aa..64a2e23 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -19,6 +19,7 @@
"fmt"
"io"
"path/filepath"
+ "slices"
"strings"
"android/soong/android"
@@ -109,6 +110,12 @@
// Mount point for this image. Default is "/"
Mount_point *string
+
+ // If set to the name of a partition ("system", "vendor", etc), this filesystem module
+ // will also include the contents of the make-built staging directories. If any soong
+ // modules would be installed to the same location as a make module, they will overwrite
+ // the make version.
+ Include_make_built_files string
}
// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
@@ -183,13 +190,9 @@
ctx.InstallFile(f.installDir, f.installFileName(), f.output)
}
-// root zip will contain extra files/dirs that are not from the `deps` property.
-func (f *filesystem) buildRootZip(ctx android.ModuleContext) android.OutputPath {
- rootDir := android.PathForModuleGen(ctx, "root").OutputPath
- builder := android.NewRuleBuilder(pctx, ctx)
- builder.Command().Text("rm -rf").Text(rootDir.String())
- builder.Command().Text("mkdir -p").Text(rootDir.String())
-
+// Copy extra files/dirs that are not from the `deps` property to `rootDir`, checking for conflicts with files
+// already in `rootDir`.
+func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.OutputPath) {
// create dirs and symlinks
for _, dir := range f.properties.Dirs {
// OutputPath.Join verifies dir
@@ -212,65 +215,43 @@
// OutputPath.Join verifies name. don't need to verify target.
dst := rootDir.Join(ctx, name)
-
+ builder.Command().Textf("(! [ -e %s -o -L %s ] || (echo \"%s already exists from an earlier stage of the build\" && exit 1))", dst, dst, dst)
builder.Command().Text("mkdir -p").Text(filepath.Dir(dst.String()))
builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
}
// create extra files if there's any
- rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
- var extraFiles android.OutputPaths
if f.buildExtraFiles != nil {
- extraFiles = f.buildExtraFiles(ctx, rootForExtraFiles)
+ rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
+ extraFiles := f.buildExtraFiles(ctx, rootForExtraFiles)
for _, f := range extraFiles {
- rel, _ := filepath.Rel(rootForExtraFiles.String(), f.String())
- if strings.HasPrefix(rel, "..") {
- panic(fmt.Errorf("%q is not under %q\n", f, rootForExtraFiles))
+ rel, err := filepath.Rel(rootForExtraFiles.String(), f.String())
+ if err != nil || strings.HasPrefix(rel, "..") {
+ ctx.ModuleErrorf("can't make %q relative to %q", f, rootForExtraFiles)
}
}
- }
-
- // Zip them all
- zipOut := android.PathForModuleGen(ctx, "root.zip").OutputPath
- zipCommand := builder.Command().BuiltTool("soong_zip")
- zipCommand.FlagWithOutput("-o ", zipOut).
- FlagWithArg("-C ", rootDir.String()).
- Flag("-L 0"). // no compression because this will be unzipped soon
- FlagWithArg("-D ", rootDir.String()).
- Flag("-d") // include empty directories
- if len(extraFiles) > 0 {
- zipCommand.FlagWithArg("-C ", rootForExtraFiles.String())
- for _, f := range extraFiles {
- zipCommand.FlagWithInput("-f ", f)
+ if len(extraFiles) > 0 {
+ builder.Command().BuiltTool("merge_directories").
+ Implicits(extraFiles.Paths()).
+ Text(rootDir.String()).
+ Text(rootForExtraFiles.String())
}
}
-
- builder.Command().Text("rm -rf").Text(rootDir.String())
-
- builder.Build("zip_root", fmt.Sprintf("zipping root contents for %s", ctx.ModuleName()))
- return zipOut
}
func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
- depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
-
- builder := android.NewRuleBuilder(pctx, ctx)
- depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
- rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
- builder.Command().
- BuiltTool("zip2zip").
- FlagWithInput("-i ", depsZipFile).
- FlagWithOutput("-o ", rebasedDepsZip).
- Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
-
rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- rootZip := f.buildRootZip(ctx)
- builder.Command().
- BuiltTool("zipsync").
- FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
- Input(rootZip).
- Input(rebasedDepsZip)
+ rebasedDir := rootDir
+ if f.properties.Base_dir != nil {
+ rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
+ }
+ builder := android.NewRuleBuilder(pctx, ctx)
+ // Wipe the root dir to get rid of leftover files from prior builds
+ builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
+ f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
+
+ f.buildNonDepsFiles(ctx, builder, rootDir)
+ f.addMakeBuiltFiles(ctx, builder, rootDir)
// run host_init_verifier
// Ideally we should have a concept of pluggable linters that verify the generated image.
@@ -388,25 +369,21 @@
ctx.PropertyErrorf("file_contexts", "file_contexts is not supported for compressed cpio image.")
}
- depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
-
- builder := android.NewRuleBuilder(pctx, ctx)
- depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
- rebasedDepsZip := android.PathForModuleOut(ctx, "rebased_deps.zip").OutputPath
- builder.Command().
- BuiltTool("zip2zip").
- FlagWithInput("-i ", depsZipFile).
- FlagWithOutput("-o ", rebasedDepsZip).
- Text("**/*:" + proptools.ShellEscape(depsBase)) // zip2zip verifies depsBase
+ if f.properties.Include_make_built_files != "" {
+ ctx.PropertyErrorf("include_make_built_files", "include_make_built_files is not supported for compressed cpio image.")
+ }
rootDir := android.PathForModuleOut(ctx, "root").OutputPath
- rootZip := f.buildRootZip(ctx)
- builder.Command().
- BuiltTool("zipsync").
- FlagWithArg("-d ", rootDir.String()). // zipsync wipes this. No need to clear.
- Input(rootZip).
- Input(rebasedDepsZip)
+ rebasedDir := rootDir
+ if f.properties.Base_dir != nil {
+ rebasedDir = rootDir.Join(ctx, *f.properties.Base_dir)
+ }
+ builder := android.NewRuleBuilder(pctx, ctx)
+ // Wipe the root dir to get rid of leftover files from prior builds
+ builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir)
+ f.entries = f.CopySpecsToDir(ctx, builder, f.gatherFilteredPackagingSpecs(ctx), rebasedDir)
+
+ f.buildNonDepsFiles(ctx, builder, rootDir)
output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
cmd := builder.Command().
@@ -429,6 +406,41 @@
return output
}
+var validPartitions = []string{
+ "system",
+ "userdata",
+ "cache",
+ "system_other",
+ "vendor",
+ "product",
+ "system_ext",
+ "odm",
+ "vendor_dlkm",
+ "odm_dlkm",
+ "system_dlkm",
+}
+
+func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
+ partition := f.properties.Include_make_built_files
+ if partition == "" {
+ return
+ }
+ if !slices.Contains(validPartitions, partition) {
+ ctx.PropertyErrorf("include_make_built_files", "Expected one of %#v, found %q", validPartitions, partition)
+ return
+ }
+ stampFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/staging_dir.stamp", ctx.Config().DeviceName(), partition)
+ fileListFile := fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partition)
+ stagingDir := fmt.Sprintf("target/product/%s/%s", ctx.Config().DeviceName(), partition)
+
+ builder.Command().BuiltTool("merge_directories").
+ Implicit(android.PathForArbitraryOutput(ctx, stampFile)).
+ Text("--ignore-duplicates").
+ FlagWithInput("--file-list", android.PathForArbitraryOutput(ctx, fileListFile)).
+ Text(rootDir.String()).
+ Text(android.PathForArbitraryOutput(ctx, stagingDir).String())
+}
+
var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
// Implements android.AndroidMkEntriesProvider
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index c448105..6d62746 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -16,6 +16,7 @@
import (
"os"
+ "path/filepath"
"testing"
"android/soong/android"
@@ -93,6 +94,22 @@
}
}
+func TestIncludeMakeBuiltFiles(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "myfilesystem",
+ include_make_built_files: "system",
+ }
+ `)
+
+ output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img")
+
+ stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp")
+ fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt")
+ android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile)
+ android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile)
+}
+
func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
result := fixture.RunTestWithBp(t, `
android_system_image {
@@ -270,7 +287,7 @@
}
`)
- inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+ inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits
android.AssertStringListContains(t, "filesystem should have libbar even for unbundled build",
inputs.Strings(),
"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
@@ -314,7 +331,7 @@
`)
filesystem := result.ModuleForTests("myfilesystem", "android_common_cov")
- inputs := filesystem.Output("deps.zip").Implicits
+ inputs := filesystem.Output("myfilesystem.img").Implicits
android.AssertStringListContains(t, "filesystem should have libfoo(cov)",
inputs.Strings(),
"out/soong/.intermediates/libfoo/android_arm64_armv8-a_shared_cov/libfoo.so")
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 60b1366..7c71b77 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -21,9 +21,4 @@
"com.google.pixel.camera.hal.manifest",
// go/keep-sorted end
}
-
- SandboxingDenyPathList = []string{
- // go/keep-sorted start
- // go/keep-sorted end
- }
)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 6f66088..a2a3f75 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -842,19 +842,15 @@
type sandboxingAllowlistSets struct {
sandboxingDenyModuleSet map[string]bool
- sandboxingDenyPathSet map[string]bool
}
func getSandboxingAllowlistSets(ctx android.PathContext) *sandboxingAllowlistSets {
return ctx.Config().Once(sandboxingAllowlistKey, func() interface{} {
sandboxingDenyModuleSet := map[string]bool{}
- sandboxingDenyPathSet := map[string]bool{}
android.AddToStringSet(sandboxingDenyModuleSet, SandboxingDenyModuleList)
- android.AddToStringSet(sandboxingDenyPathSet, SandboxingDenyPathList)
return &sandboxingAllowlistSets{
sandboxingDenyModuleSet: sandboxingDenyModuleSet,
- sandboxingDenyPathSet: sandboxingDenyPathSet,
}
}).(*sandboxingAllowlistSets)
}
@@ -864,8 +860,7 @@
return r.SandboxTools()
}
sandboxingAllowlistSets := getSandboxingAllowlistSets(ctx)
- if sandboxingAllowlistSets.sandboxingDenyPathSet[ctx.ModuleDir()] ||
- sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] {
+ if sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] {
return r.SandboxTools()
}
return r.SandboxInputs()
diff --git a/java/app.go b/java/app.go
index 8209d4c..4f6f1f3 100755
--- a/java/app.go
+++ b/java/app.go
@@ -912,6 +912,13 @@
}
a.buildAppDependencyInfo(ctx)
+
+ providePrebuiltInfo(ctx,
+ prebuiltInfoProps{
+ baseModuleName: a.BaseModuleName(),
+ isPrebuilt: false,
+ },
+ )
}
type appDepsInterface interface {
diff --git a/java/app_import.go b/java/app_import.go
index dc84fc2..7387e16 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -150,6 +150,11 @@
// If unspecified, follows the naming convention that the source module of
// the prebuilt is Name() without "prebuilt_" prefix
Source_module_name *string
+
+ // Path to the .prebuilt_info file of the prebuilt app.
+ // In case of mainline modules, the .prebuilt_info file contains the build_id that was used
+ // to generate the prebuilt.
+ Prebuilt_info *string `android:"path"`
}
func (a *AndroidAppImport) IsInstallable() bool {
@@ -413,6 +418,14 @@
}
android.CollectDependencyAconfigFiles(ctx, &a.mergedAconfigFiles)
+ providePrebuiltInfo(ctx,
+ prebuiltInfoProps{
+ baseModuleName: a.BaseModuleName(),
+ isPrebuilt: true,
+ prebuiltInfo: a.properties.Prebuilt_info,
+ },
+ )
+
// TODO: androidmk converter jni libs
}
diff --git a/java/app_set.go b/java/app_set.go
index d2d3b06..33d3ade 100644
--- a/java/app_set.go
+++ b/java/app_set.go
@@ -48,6 +48,11 @@
// Names of modules to be overridden. Listed modules can only be other apps
// (in Make or Soong).
Overrides []string
+
+ // Path to the .prebuilt_info file of the prebuilt app.
+ // In case of mainline modules, the .prebuilt_info file contains the build_id that was used
+ // to generate the prebuilt.
+ Prebuilt_info *string `android:"path"`
}
type AndroidAppSet struct {
@@ -117,6 +122,27 @@
return result
}
+type prebuiltInfoProps struct {
+ baseModuleName string
+ isPrebuilt bool
+ prebuiltInfo *string
+}
+
+// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
+// with information about whether source or prebuilt of an apex was used during the build.
+func providePrebuiltInfo(ctx android.ModuleContext, p prebuiltInfoProps) {
+ info := android.PrebuiltInfo{
+ Name: p.baseModuleName,
+ Is_prebuilt: p.isPrebuilt,
+ }
+ // If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json.
+ if p.prebuiltInfo != nil {
+ prebuiltInfoFile := android.PathForModuleSrc(ctx, *p.prebuiltInfo)
+ info.Prebuilt_info_file_path = prebuiltInfoFile.String()
+ }
+ android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
+}
+
func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk")
@@ -157,6 +183,15 @@
installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
}
ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput)
+
+ providePrebuiltInfo(ctx,
+ prebuiltInfoProps{
+ baseModuleName: as.BaseModuleName(),
+ isPrebuilt: true,
+ prebuiltInfo: as.properties.Prebuilt_info,
+ },
+ )
+
}
func (as *AndroidAppSet) InstallBypassMake() bool { return true }
diff --git a/java/base.go b/java/base.go
index d8ccec6..69f88be 100644
--- a/java/base.go
+++ b/java/base.go
@@ -26,7 +26,6 @@
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
- "android/soong/aconfig"
"android/soong/android"
"android/soong/dexpreopt"
"android/soong/java/config"
@@ -2551,7 +2550,7 @@
default:
return RenameUseExclude, "srcfile"
}
- } else if _, ok := android.OtherModuleProvider(ctx, m, aconfig.CodegenInfoProvider); ok {
+ } else if _, ok := android.OtherModuleProvider(ctx, m, android.CodegenInfoProvider); ok {
return RenameUseInclude, "aconfig_declarations_group"
} else {
switch tag {
diff --git a/java/builder.go b/java/builder.go
index b07a622..5d84d0b 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -120,6 +120,8 @@
`--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED ` +
`--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED ` +
`--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED ` +
+ `--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED ` +
+ `--add-exports=jdk.internal.opt/jdk.internal.opt=ALL-UNNAMED ` +
`-jar ${config.JavaKytheExtractorJar} ` +
`${config.JavacHeapFlags} ${config.CommonJdkFlags} ` +
`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
diff --git a/java/dex.go b/java/dex.go
index 4474c63..b721f58 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -223,6 +223,16 @@
if err != nil {
ctx.PropertyErrorf("min_sdk_version", "%s", err)
}
+ if effectiveVersion.FinalOrFutureInt() >= 35 {
+ // V is 35, but we have not bumped the SDK version yet, so check for both.
+ if ctx.Config().PlatformSdkVersion().FinalInt() >= 35 ||
+ ctx.Config().PlatformSdkCodename() == "VanillaIceCream" {
+ // TODO(b/329465418): Skip this module since it causes issue with app DRM
+ if ctx.ModuleName() != "framework-minus-apex" {
+ flags = append([]string{"-JDcom.android.tools.r8.dexContainerExperiment"}, flags...)
+ }
+ }
+ }
// If the specified SDK level is 10000, then configure the compiler to use the
// current platform SDK level and to compile the build as a platform build.
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 6a66f45..aec40b3 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -21,7 +21,6 @@
"github.com/google/blueprint/proptools"
- "android/soong/aconfig"
"android/soong/android"
"android/soong/java/config"
)
@@ -414,7 +413,7 @@
case aconfigDeclarationTag:
if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok {
deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath)
- } else if dep, ok := android.OtherModuleProvider(ctx, module, aconfig.CodegenInfoProvider); ok {
+ } else if dep, ok := android.OtherModuleProvider(ctx, module, android.CodegenInfoProvider); ok {
deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPaths...)
} else {
ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 76c8d88..9556e95 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -368,7 +368,7 @@
ret, err = nil, fmt.Errorf("api file path not supported for the stub type %s", stubsType.String())
}
if ret == nil && err == nil {
- err = fmt.Errorf("stubs srcjar is null for the stub type %s", stubsType.String())
+ err = fmt.Errorf("api file is null for the stub type %s", stubsType.String())
}
return ret, err
}
@@ -478,34 +478,41 @@
}
func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath, stubsType StubsType, checkApi bool) {
- if checkApi || String(d.properties.Api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
- uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), filename)
- cmd.FlagWithOutput("--api ", uncheckedApiFile)
+ apiFileName := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
+ uncheckedApiFile := android.PathForModuleOut(ctx, stubsType.String(), apiFileName)
+ cmd.FlagWithOutput("--api ", uncheckedApiFile)
+ if checkApi || String(d.properties.Api_filename) != "" {
if stubsType == Everything {
d.apiFile = uncheckedApiFile
} else if stubsType == Exportable {
d.exportableApiFile = uncheckedApiFile
}
} else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
- // If check api is disabled then make the source file available for export.
- d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
+ if stubsType == Everything {
+ // If check api is disabled then make the source file available for export.
+ d.apiFile = android.PathForModuleSrc(ctx, sourceApiFile)
+ } else if stubsType == Exportable {
+ d.exportableApiFile = uncheckedApiFile
+ }
}
+ removedApiFileName := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
+ uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), removedApiFileName)
+ cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
if checkApi || String(d.properties.Removed_api_filename) != "" {
- filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
- uncheckedRemovedFile := android.PathForModuleOut(ctx, stubsType.String(), filename)
- cmd.FlagWithOutput("--removed-api ", uncheckedRemovedFile)
-
if stubsType == Everything {
d.removedApiFile = uncheckedRemovedFile
} else if stubsType == Exportable {
d.exportableRemovedApiFile = uncheckedRemovedFile
}
} else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
- // If check api is disabled then make the source removed api file available for export.
- d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+ if stubsType == Everything {
+ // If check api is disabled then make the source removed api file available for export.
+ d.removedApiFile = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
+ } else if stubsType == Exportable {
+ d.exportableRemovedApiFile = uncheckedRemovedFile
+ }
}
if stubsDir.Valid() {
diff --git a/java/java.go b/java/java.go
index 6423eeb..e606993 100644
--- a/java/java.go
+++ b/java/java.go
@@ -24,7 +24,6 @@
"sort"
"strings"
- "android/soong/aconfig"
"android/soong/remoteexec"
"android/soong/testing"
@@ -346,6 +345,12 @@
return j.kytheFiles
}
+func (d dependencyTag) PropagateAconfigValidation() bool {
+ return d.static
+}
+
+var _ android.PropagateAconfigValidationDependencyTag = dependencyTag{}
+
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
@@ -355,6 +360,8 @@
// True if the dependency is a toolchain, for example an annotation processor.
toolchain bool
+
+ static bool
}
// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
@@ -400,7 +407,7 @@
var (
dataNativeBinsTag = dependencyTag{name: "dataNativeBins"}
dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
- staticLibTag = dependencyTag{name: "staticlib"}
+ staticLibTag = dependencyTag{name: "staticlib", static: true}
libTag = dependencyTag{name: "javalib", runtimeLinked: true}
sdkLibTag = dependencyTag{name: "sdklib", runtimeLinked: true}
java9LibTag = dependencyTag{name: "java9lib", runtimeLinked: true}
@@ -2173,7 +2180,7 @@
case aconfigDeclarationTag:
if provider, ok := android.OtherModuleProvider(ctx, dep, android.AconfigDeclarationsProviderKey); ok {
al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPath)
- } else if provider, ok := android.OtherModuleProvider(ctx, dep, aconfig.CodegenInfoProvider); ok {
+ } else if provider, ok := android.OtherModuleProvider(ctx, dep, android.CodegenInfoProvider); ok {
al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.IntermediateCacheOutputPaths...)
} else {
ctx.ModuleErrorf("Only aconfig_declarations and aconfig_declarations_group "+
diff --git a/java/system_modules.go b/java/system_modules.go
index 92e31cd..8e2d5d8 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -64,6 +64,7 @@
// useful on Android, and (b) it causes errors with later versions of jlink
// when the jdk.internal.module is absent from java.base (as it is here).
` --disable-plugin system-modules && ` +
+ `rm -rf ${workDir} && ` +
`cp ${config.JrtFsJar} ${outDir}/lib/`,
CommandDeps: []string{
"${moduleInfoJavaPath}",
diff --git a/rust/config/global.go b/rust/config/global.go
index 418eca4..e28dbaa 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -25,7 +25,7 @@
pctx = android.NewPackageContext("android/soong/rust/config")
ExportedVars = android.NewExportedVariables(pctx)
- RustDefaultVersion = "1.75.0"
+ RustDefaultVersion = "1.76.0"
RustDefaultBase = "prebuilts/rust/"
DefaultEdition = "2021"
Stdlibs = []string{
diff --git a/scripts/Android.bp b/scripts/Android.bp
index e2fd59f..f36329b 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -292,3 +292,16 @@
name: "keep-flagged-apis",
src: "keep-flagged-apis.sh",
}
+
+python_binary_host {
+ name: "merge_directories",
+ main: "merge_directories.py",
+ srcs: [
+ "merge_directories.py",
+ ],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+}
diff --git a/scripts/merge_directories.py b/scripts/merge_directories.py
new file mode 100755
index 0000000..3f8631b
--- /dev/null
+++ b/scripts/merge_directories.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+import argparse
+import os
+import shutil
+import sys
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Given a list of directories, this script will copy the contents of all of "
+ "them into the first directory, erroring out if any duplicate files are found."
+ )
+ parser.add_argument(
+ "--ignore-duplicates",
+ action="store_true",
+ help="Don't error out on duplicate files, just skip them. The file from the earliest "
+ "directory listed on the command line will be the winner."
+ )
+ parser.add_argument(
+ "--file-list",
+ help="Path to a text file containing paths relative to in_dir. Only these paths will be "
+ "copied out of in_dir."
+ )
+ parser.add_argument("out_dir")
+ parser.add_argument("in_dir")
+ args = parser.parse_args()
+
+ if not os.path.isdir(args.out_dir):
+ sys.exit(f"error: {args.out_dir} must be a directory")
+ if not os.path.isdir(args.in_dir):
+ sys.exit(f"error: {args.in_dir} must be a directory")
+
+ file_list = None
+ if args.file_list:
+ with open(file_list_file, "r") as f:
+ file_list = f.read().strip().splitlines()
+
+ in_dir = args.in_dir
+ for root, dirs, files in os.walk(in_dir):
+ rel_root = os.path.relpath(root, in_dir)
+ dst_root = os.path.join(args.out_dir, rel_root)
+ made_parent_dirs = False
+ for f in files:
+ src = os.path.join(root, f)
+ dst = os.path.join(dst_root, f)
+ p = os.path.normpath(os.path.join(rel_root, f))
+ if file_list is not None and p not in file_list:
+ continue
+ if os.path.lexists(dst):
+ if args.ignore_duplicates:
+ continue
+ sys.exit(f"error: {p} exists in both {args.out_dir} and {in_dir}")
+
+ if not made_parent_dirs:
+ os.makedirs(dst_root, exist_ok=True)
+ made_parent_dirs = True
+
+ shutil.copy2(src, dst, follow_symlinks=False)
+
+if __name__ == "__main__":
+ main()
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 82abba4..698aaf2 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -157,12 +157,11 @@
syspropDir := android.GenPathWithExt(ctx, "sysprop", syspropFile, "srcrust")
outputDir := syspropDir.Join(ctx, "src")
libPath := syspropDir.Join(ctx, "src", "lib.rs")
- parsersPath := syspropDir.Join(ctx, "src", "gen_parsers_and_formatters.rs")
ctx.Build(pctx, android.BuildParams{
Rule: syspropRust,
Description: "sysprop_rust " + syspropFile.Rel(),
- Outputs: android.WritablePaths{libPath, parsersPath},
+ Outputs: android.WritablePaths{libPath},
Input: syspropFile,
Implicit: checkApiFileTimeStamp,
Args: map[string]string{
@@ -171,7 +170,7 @@
},
})
- g.genSrcs = append(g.genSrcs, libPath, parsersPath)
+ g.genSrcs = append(g.genSrcs, libPath)
}
}